diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 2096f0f201..5800948e8b 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -8299,3 +8299,6 @@ Sorry for the inconvenience."; "Notification.ForumTopicUnhiddenAuthor" = "%1$@ unhidden topic"; "Notification.OverviewTopicHidden" = "%1$@ hidden %2$@ %3$@"; "Notification.OverviewTopicUnhidden" = "%1$@ unhidden %2$@ %3$@"; + +"CreateTopic.ShowGeneral" = "Show in Topics"; +"CreateTopic.ShowGeneralInfo" = "If the 'General' topic is hidden, group members can pull down in the topic list to view it."; diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index 13461a68ef..9215a25832 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -705,6 +705,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { }, insertText: { _ in }, backwardsDeleteText: { }, restartTopic: { + }, requestLayout: { _ in }, chatController: { return nil }, statuses: nil) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index daa9e4bdd4..2bc5165286 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1617,7 +1617,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create) controller.navigationPresentation = .modal - controller.completion = { [weak controller] title, fileId in + controller.completion = { [weak controller] title, fileId, _ in controller?.isInProgress = true let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: ForumCreateTopicScreen.iconColors.randomElement()!, iconFileId: fileId) @@ -2723,7 +2723,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create) controller.navigationPresentation = .modal - controller.completion = { [weak controller] title, fileId in + controller.completion = { [weak controller] title, fileId, _ in controller?.isInProgress = true let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: ForumCreateTopicScreen.iconColors.randomElement()!, iconFileId: fileId) diff --git a/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift b/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift index 6a6f673cad..9fa4c200f7 100644 --- a/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift +++ b/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift @@ -148,6 +148,7 @@ public final class ChatPanelInterfaceInteraction { public let insertText: (NSAttributedString) -> Void public let backwardsDeleteText: () -> Void public let restartTopic: () -> Void + public let requestLayout: (ContainedViewLayoutTransition) -> Void public let chatController: () -> ViewController? public let statuses: ChatPanelInterfaceInteractionStatuses? @@ -245,6 +246,7 @@ public final class ChatPanelInterfaceInteraction { insertText: @escaping (NSAttributedString) -> Void, backwardsDeleteText: @escaping () -> Void, restartTopic: @escaping () -> Void, + requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, chatController: @escaping () -> ViewController?, statuses: ChatPanelInterfaceInteractionStatuses? ) { @@ -341,6 +343,7 @@ public final class ChatPanelInterfaceInteraction { self.insertText = insertText self.backwardsDeleteText = backwardsDeleteText self.restartTopic = restartTopic + self.requestLayout = requestLayout self.chatController = chatController self.statuses = statuses @@ -445,6 +448,7 @@ public final class ChatPanelInterfaceInteraction { }, insertText: { _ in }, backwardsDeleteText: { }, restartTopic: { + }, requestLayout: { _ in }, chatController: { return nil }, statuses: nil) diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index 3ab528c5e6..482556ec01 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -1262,6 +1262,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { }, openFeatured: { }, + openSearch: { + }, addGroupAction: { [weak self] groupId, isPremiumLocked in guard let strongSelf = self, let collectionId = groupId.base as? ItemCollectionId else { return diff --git a/submodules/SettingsUI/Sources/UsernameSetupController.swift b/submodules/SettingsUI/Sources/UsernameSetupController.swift index 9e2d695bc0..44c7e961d4 100644 --- a/submodules/SettingsUI/Sources/UsernameSetupController.swift +++ b/submodules/SettingsUI/Sources/UsernameSetupController.swift @@ -456,7 +456,8 @@ public func usernameSetupController(context: AccountContext) -> ViewController { dismissInputImpl?() let presentationData = context.sharedContext.currentPresentationData.with { $0 } presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.Username_ActivateAlertTitle, text: presentationData.strings.Username_ActivateAlertText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Username_ActivateAlertShow, action: { - let _ = context.engine.peers.toggleAddressNameActive(domain: .account, name: name, active: true).start(error: { error in + let _ = (context.engine.peers.toggleAddressNameActive(domain: .account, name: name, active: true) + |> deliverOnMainQueue).start(error: { error in let errorText: String switch error { case .activeLimitReached: diff --git a/submodules/TelegramCore/Sources/ForumChannels.swift b/submodules/TelegramCore/Sources/ForumChannels.swift index 1e9044dae7..ac3d1ed374 100644 --- a/submodules/TelegramCore/Sources/ForumChannels.swift +++ b/submodules/TelegramCore/Sources/ForumChannels.swift @@ -341,6 +341,12 @@ func _internal_setForumChannelTopicClosed(account: Account, id: EnginePeer.Id, t var flags: Int32 = 0 flags |= (1 << 2) + var hidden: Api.Bool? = nil + if threadId == 1, !isClosed { + flags |= (1 << 3) + hidden = .boolFalse + } + return account.network.request(Api.functions.channels.editForumTopic( flags: flags, channel: inputChannel, @@ -348,7 +354,7 @@ func _internal_setForumChannelTopicClosed(account: Account, id: EnginePeer.Id, t title: nil, iconEmojiId: nil, closed: isClosed ? .boolTrue : .boolFalse, - hidden: nil + hidden: hidden )) |> mapError { _ -> EditForumChannelTopicError in return .generic @@ -361,6 +367,9 @@ func _internal_setForumChannelTopicClosed(account: Account, id: EnginePeer.Id, t var data = initialData data.isClosed = isClosed + if let _ = hidden { + data.isHidden = false + } if data != initialData { if let entry = StoredMessageHistoryThreadInfo(data) { diff --git a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift index 678f914fae..7b28eed953 100644 --- a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift +++ b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift @@ -350,7 +350,7 @@ public final class EmojiStatusSelectionController: ViewController { return } strongSelf.controller?._ready.set(.single(true)) - + var emojiContent = emojiContent if let emojiSearchResult = emojiSearchResult { var emptySearchResults: EmojiPagerContentComponent.EmptySearchResults? @@ -387,6 +387,8 @@ public final class EmojiStatusSelectionController: ViewController { }, openFeatured: { }, + openSearch: { + }, addGroupAction: { groupId, isPremiumLocked in guard let strongSelf = self, let collectionId = groupId.base as? ItemCollectionId else { return diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index 07b6ba95ab..6973a2bdab 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -1517,6 +1517,7 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate { var useOpaqueTheme: Bool var isActive: Bool var size: CGSize + var canFocus: Bool static func ==(lhs: Params, rhs: Params) -> Bool { if lhs.theme !== rhs.theme { @@ -1537,6 +1538,9 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate { if lhs.size != rhs.size { return false } + if lhs.canFocus != rhs.canFocus { + return false + } return true } } @@ -1673,7 +1677,7 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate { @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { - if self.textField == nil, let textComponentView = self.textView.view { + if self.textField == nil, let textComponentView = self.textView.view, self.params?.canFocus == true { let backgroundFrame = self.backgroundLayer.frame let textFieldFrame = CGRect(origin: CGPoint(x: textComponentView.frame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textComponentView.frame.minX, height: backgroundFrame.height)) @@ -1697,13 +1701,15 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate { self.clearIconTintView.isHidden = true self.clearIconButton.isHidden = true + + self.deactivated() + if let textField = self.textField { self.textField = nil textField.resignFirstResponder() textField.removeFromSuperview() } - self.deactivated() } @objc private func clearPressed() { @@ -1746,17 +1752,18 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate { return } self.params = nil - self.update(theme: params.theme, strings: params.strings, text: params.text, useOpaqueTheme: params.useOpaqueTheme, isActive: params.isActive, size: params.size, transition: transition) + self.update(theme: params.theme, strings: params.strings, text: params.text, useOpaqueTheme: params.useOpaqueTheme, isActive: params.isActive, size: params.size, canFocus: params.canFocus, transition: transition) } - public func update(theme: PresentationTheme, strings: PresentationStrings, text: String, useOpaqueTheme: Bool, isActive: Bool, size: CGSize, transition: Transition) { + public func update(theme: PresentationTheme, strings: PresentationStrings, text: String, useOpaqueTheme: Bool, isActive: Bool, size: CGSize, canFocus: Bool, transition: Transition) { let params = Params( theme: theme, strings: strings, text: text, useOpaqueTheme: useOpaqueTheme, isActive: isActive, - size: size + size: size, + canFocus: canFocus ) if self.params == params { @@ -1931,7 +1938,7 @@ private final class EmptySearchResultsView: UIView { fatalError("init(coder:) has not been implemented") } - func update(context: AccountContext, theme: PresentationTheme, useOpaqueTheme: Bool, text: String, file: TelegramMediaFile?, size: CGSize, transition: Transition) { + func update(context: AccountContext, theme: PresentationTheme, useOpaqueTheme: Bool, text: String, file: TelegramMediaFile?, size: CGSize, searchInitiallyHidden: Bool, transition: Transition) { let titleColor: UIColor if useOpaqueTheme { titleColor = theme.chat.inputMediaPanel.panelContentControlOpaqueOverlayColor @@ -1973,7 +1980,7 @@ private final class EmptySearchResultsView: UIView { let spacing: CGFloat = 4.0 let contentHeight = iconSize.height + spacing + titleSize.height - let contentOriginY = floor((size.height - contentHeight) / 2.0) + let contentOriginY = searchInitiallyHidden ? floor((size.height - contentHeight) / 2.0) : 10.0 let iconFrame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: contentOriginY), size: iconSize) let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: iconFrame.maxY + spacing), size: titleSize) @@ -2069,6 +2076,7 @@ public final class EmojiPagerContentComponent: Component { public let deleteBackwards: () -> Void public let openStickerSettings: () -> Void public let openFeatured: () -> Void + public let openSearch: () -> Void public let addGroupAction: (AnyHashable, Bool) -> Void public let clearGroup: (AnyHashable) -> Void public let pushController: (ViewController) -> Void @@ -2089,6 +2097,7 @@ public final class EmojiPagerContentComponent: Component { deleteBackwards: @escaping () -> Void, openStickerSettings: @escaping () -> Void, openFeatured: @escaping () -> Void, + openSearch: @escaping () -> Void, addGroupAction: @escaping (AnyHashable, Bool) -> Void, clearGroup: @escaping (AnyHashable) -> Void, pushController: @escaping (ViewController) -> Void, @@ -2108,6 +2117,7 @@ public final class EmojiPagerContentComponent: Component { self.deleteBackwards = deleteBackwards self.openStickerSettings = openStickerSettings self.openFeatured = openFeatured + self.openSearch = openSearch self.addGroupAction = addGroupAction self.clearGroup = clearGroup self.pushController = pushController @@ -2349,6 +2359,8 @@ public final class EmojiPagerContentComponent: Component { public let itemContentUniqueId: AnyHashable? public let warpContentsOnEdges: Bool public let displaySearchWithPlaceholder: String? + public let searchInitiallyHidden: Bool + public let searchIsPlaceholderOnly: Bool public let emptySearchResults: EmptySearchResults? public let enableLongPress: Bool public let selectedItems: Set @@ -2365,6 +2377,8 @@ public final class EmojiPagerContentComponent: Component { itemContentUniqueId: AnyHashable?, warpContentsOnEdges: Bool, displaySearchWithPlaceholder: String?, + searchInitiallyHidden: Bool, + searchIsPlaceholderOnly: Bool, emptySearchResults: EmptySearchResults?, enableLongPress: Bool, selectedItems: Set @@ -2380,6 +2394,8 @@ public final class EmojiPagerContentComponent: Component { self.itemContentUniqueId = itemContentUniqueId self.warpContentsOnEdges = warpContentsOnEdges self.displaySearchWithPlaceholder = displaySearchWithPlaceholder + self.searchInitiallyHidden = searchInitiallyHidden + self.searchIsPlaceholderOnly = searchIsPlaceholderOnly self.emptySearchResults = emptySearchResults self.enableLongPress = enableLongPress self.selectedItems = selectedItems @@ -2398,6 +2414,8 @@ public final class EmojiPagerContentComponent: Component { itemContentUniqueId: itemContentUniqueId, warpContentsOnEdges: self.warpContentsOnEdges, displaySearchWithPlaceholder: self.displaySearchWithPlaceholder, + searchInitiallyHidden: self.searchInitiallyHidden, + searchIsPlaceholderOnly: self.searchIsPlaceholderOnly, emptySearchResults: emptySearchResults, enableLongPress: self.enableLongPress, selectedItems: self.selectedItems @@ -2438,6 +2456,12 @@ public final class EmojiPagerContentComponent: Component { if lhs.displaySearchWithPlaceholder != rhs.displaySearchWithPlaceholder { return false } + if lhs.searchInitiallyHidden != rhs.searchInitiallyHidden { + return false + } + if lhs.searchIsPlaceholderOnly != rhs.searchIsPlaceholderOnly { + return false + } if lhs.emptySearchResults != rhs.emptySearchResults { return false } @@ -3233,6 +3257,8 @@ public final class EmojiPagerContentComponent: Component { private let mirrorContentScrollView: UIView private var warpView: WarpView? private var mirrorContentWarpView: WarpView? + + private let scrollViewClippingView: UIView private let scrollView: ContentScrollView private var scrollGradientLayer: SimpleGradientLayer? private let boundsChangeTrackerLayer = SimpleLayer() @@ -3276,6 +3302,9 @@ public final class EmojiPagerContentComponent: Component { self.standaloneShimmerEffect = nil } + self.scrollViewClippingView = UIView() + self.scrollViewClippingView.clipsToBounds = true + self.mirrorContentScrollView = UIView() self.mirrorContentScrollView.layer.anchorPoint = CGPoint() self.mirrorContentScrollView.clipsToBounds = true @@ -3311,7 +3340,8 @@ public final class EmojiPagerContentComponent: Component { self.scrollView.delegate = self self.scrollView.clipsToBounds = false self.scrollView.scrollsToTop = false - self.addSubview(self.scrollView) + self.addSubview(self.scrollViewClippingView) + self.scrollViewClippingView.addSubview(self.scrollView) self.scrollView.addSubview(self.placeholdersContainerView) @@ -4750,6 +4780,10 @@ public final class EmojiPagerContentComponent: Component { self.updateVisibleItems(transition: .immediate, attemptSynchronousLoads: false, previousItemPositions: nil, updatedItemPositions: nil) self.updateScrollingOffset(isReset: false, transition: .immediate) + + if self.isSearchActivated { + self.visibleSearchHeader?.endEditing(true) + } } public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { @@ -5899,9 +5933,15 @@ public final class EmojiPagerContentComponent: Component { self.ignoreScrolling = true let scrollOriginY: CGFloat = 0.0 - let scrollSize = CGSize(width: availableSize.width, height: availableSize.height) + + let scrollSize = CGSize(width: availableSize.width, height: availableSize.height) transition.setPosition(view: self.scrollView, position: CGPoint(x: 0.0, y: scrollOriginY)) + + transition.setFrame(view: self.scrollViewClippingView, frame: CGRect(origin: CGPoint(x: 0.0, y: self.isSearchActivated ? itemLayout.searchHeight : 0.0), size: availableSize)) + + transition.setBounds(view: self.scrollViewClippingView, bounds: CGRect(origin: CGPoint(x: 0.0, y: self.isSearchActivated ? itemLayout.searchHeight : 0.0), size: availableSize)) + let previousSize = self.scrollView.bounds.size var resetScrolling = false if self.scrollView.bounds.isEmpty && component.displaySearchWithPlaceholder != nil { @@ -5944,8 +5984,13 @@ public final class EmojiPagerContentComponent: Component { }) } - if self.scrollView.contentSize != itemLayout.contentSize { - self.scrollView.contentSize = itemLayout.contentSize + var effectiveContentSize = itemLayout.contentSize + if self.isSearchActivated { + effectiveContentSize.height = max(itemLayout.contentSize.height, availableSize.height + 1.0) + } + + if self.scrollView.contentSize != effectiveContentSize { + self.scrollView.contentSize = effectiveContentSize } var scrollIndicatorInsets = pagerEnvironment.containerInsets if self.warpView != nil { @@ -6003,7 +6048,7 @@ public final class EmojiPagerContentComponent: Component { } if resetScrolling { - if component.displaySearchWithPlaceholder != nil && !self.isSearchActivated { + if component.displaySearchWithPlaceholder != nil && !self.isSearchActivated && component.searchInitiallyHidden { self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 50.0), size: scrollSize) } else { self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: scrollSize) @@ -6063,7 +6108,16 @@ public final class EmojiPagerContentComponent: Component { if self.isSearchActivated { if visibleSearchHeader.superview != self { self.addSubview(visibleSearchHeader) - self.mirrorContentClippingView?.addSubview(visibleSearchHeader.tintContainerView) + if self.mirrorContentClippingView != nil { + self.mirrorContentClippingView?.addSubview(visibleSearchHeader.tintContainerView) + } else { + self.mirrorContentScrollView.superview?.addSubview(visibleSearchHeader.tintContainerView) + } + } + } else { + if visibleSearchHeader.superview != self.scrollView { + self.scrollView.addSubview(visibleSearchHeader) + self.mirrorContentScrollView.addSubview(visibleSearchHeader.tintContainerView) } } } else { @@ -6071,9 +6125,14 @@ public final class EmojiPagerContentComponent: Component { guard let strongSelf = self else { return } - strongSelf.isSearchActivated = true - strongSelf.pagerEnvironment?.onWantsExclusiveModeUpdated(true) - strongSelf.component?.inputInteractionHolder.inputInteraction?.requestUpdate(.immediate) + + if let component = strongSelf.component, component.searchIsPlaceholderOnly { + component.inputInteractionHolder.inputInteraction?.openSearch() + } else { + strongSelf.isSearchActivated = true + strongSelf.pagerEnvironment?.onWantsExclusiveModeUpdated(true) + strongSelf.component?.inputInteractionHolder.inputInteraction?.requestUpdate(.immediate) + } }, deactivated: { [weak self] in guard let strongSelf = self else { return @@ -6083,7 +6142,11 @@ public final class EmojiPagerContentComponent: Component { strongSelf.isSearchActivated = false strongSelf.pagerEnvironment?.onWantsExclusiveModeUpdated(false) - strongSelf.component?.inputInteractionHolder.inputInteraction?.requestUpdate(.immediate) + if strongSelf.component?.searchInitiallyHidden == false { + strongSelf.component?.inputInteractionHolder.inputInteraction?.requestUpdate(.easeInOut(duration: 0.5)) + } else { + strongSelf.component?.inputInteractionHolder.inputInteraction?.requestUpdate(.immediate) + } }, updateQuery: { [weak self] query, languageCode in guard let strongSelf = self else { return @@ -6101,7 +6164,7 @@ public final class EmojiPagerContentComponent: Component { } let searchHeaderFrame = CGRect(origin: CGPoint(x: itemLayout.searchInsets.left, y: itemLayout.searchInsets.top), size: CGSize(width: itemLayout.width - itemLayout.searchInsets.left - itemLayout.searchInsets.right, height: itemLayout.searchHeight)) - visibleSearchHeader.update(theme: keyboardChildEnvironment.theme, strings: keyboardChildEnvironment.strings, text: displaySearchWithPlaceholder, useOpaqueTheme: useOpaqueTheme, isActive: self.isSearchActivated, size: searchHeaderFrame.size, transition: transition) + visibleSearchHeader.update(theme: keyboardChildEnvironment.theme, strings: keyboardChildEnvironment.strings, text: displaySearchWithPlaceholder, useOpaqueTheme: useOpaqueTheme, isActive: self.isSearchActivated, size: searchHeaderFrame.size, canFocus: !component.searchIsPlaceholderOnly, transition: transition) transition.setFrame(view: visibleSearchHeader, frame: searchHeaderFrame, completion: { [weak self] completed in guard let strongSelf = self, completed, let visibleSearchHeader = strongSelf.visibleSearchHeader else { return @@ -6120,6 +6183,7 @@ public final class EmojiPagerContentComponent: Component { } } + if let emptySearchResults = component.emptySearchResults { let visibleEmptySearchResultsView: EmptySearchResultsView var emptySearchResultsTransition = transition @@ -6140,6 +6204,7 @@ public final class EmojiPagerContentComponent: Component { text: emptySearchResults.text, file: emptySearchResults.iconFile, size: emptySearchResultsSize, + searchInitiallyHidden: component.searchInitiallyHidden, transition: emptySearchResultsTransition ) emptySearchResultsTransition.setFrame(view: visibleEmptySearchResultsView, frame: CGRect(origin: CGPoint(x: 0.0, y: itemLayout.searchInsets.top + itemLayout.searchHeight), size: emptySearchResultsSize)) @@ -6899,12 +6964,16 @@ public final class EmojiPagerContentComponent: Component { } var displaySearchWithPlaceholder: String? + var searchInitiallyHidden = true if isReactionSelection { displaySearchWithPlaceholder = strings.EmojiSearch_SearchReactionsPlaceholder } else if isStatusSelection { displaySearchWithPlaceholder = strings.EmojiSearch_SearchStatusesPlaceholder } else if isTopicIconSelection { displaySearchWithPlaceholder = strings.EmojiSearch_SearchTopicIconsPlaceholder + } else { + displaySearchWithPlaceholder = "Search Emoji" + searchInitiallyHidden = false } return EmojiPagerContentComponent( @@ -6952,6 +7021,8 @@ public final class EmojiPagerContentComponent: Component { itemContentUniqueId: nil, warpContentsOnEdges: isReactionSelection || isStatusSelection, displaySearchWithPlaceholder: displaySearchWithPlaceholder, + searchInitiallyHidden: searchInitiallyHidden, + searchIsPlaceholderOnly: false, emptySearchResults: nil, enableLongPress: (isReactionSelection && !isQuickReactionSelection) || isStatusSelection, selectedItems: selectedItems diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift index 0fd71a1118..72329b5ca1 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift @@ -337,16 +337,16 @@ public final class EntityKeyboardComponent: Component { } )))) contentIcons.append(PagerComponentContentIcon(id: "gifs", imageName: "Chat/Input/Media/EntityInputGifsIcon")) - contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "gifs", component: AnyComponent(Button( - content: AnyComponent(BundleIconComponent( - name: "Chat/Input/Media/EntityInputSearchIcon", - tintColor: component.theme.chat.inputMediaPanel.panelIconColor, - maxSize: nil - )), - action: { [weak self] in - self?.openSearch() - } - ).minSize(CGSize(width: 38.0, height: 38.0))))) +// contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "gifs", component: AnyComponent(Button( +// content: AnyComponent(BundleIconComponent( +// name: "Chat/Input/Media/EntityInputSearchIcon", +// tintColor: component.theme.chat.inputMediaPanel.panelIconColor, +// maxSize: nil +// )), +// action: { [weak self] in +// self?.openSearch() +// } +// ).minSize(CGSize(width: 38.0, height: 38.0))))) } if let stickerContent = component.stickerContent { @@ -451,16 +451,16 @@ public final class EntityKeyboardComponent: Component { } )))) contentIcons.append(PagerComponentContentIcon(id: "stickers", imageName: "Chat/Input/Media/EntityInputStickersIcon")) - contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "stickers", component: AnyComponent(Button( - content: AnyComponent(BundleIconComponent( - name: "Chat/Input/Media/EntityInputSearchIcon", - tintColor: component.theme.chat.inputMediaPanel.panelIconColor, - maxSize: nil - )), - action: { [weak self] in - self?.openSearch() - } - ).minSize(CGSize(width: 38.0, height: 38.0))))) +// contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "stickers", component: AnyComponent(Button( +// content: AnyComponent(BundleIconComponent( +// name: "Chat/Input/Media/EntityInputSearchIcon", +// tintColor: component.theme.chat.inputMediaPanel.panelIconColor, +// maxSize: nil +// )), +// action: { [weak self] in +// self?.openSearch() +// } +// ).minSize(CGSize(width: 38.0, height: 38.0))))) contentAccessoryRightButtons.append(AnyComponentWithIdentity(id: "stickers", component: AnyComponent(Button( content: AnyComponent(BundleIconComponent( name: "Chat/Input/Media/EntityInputSettingsIcon", @@ -749,7 +749,7 @@ public final class EntityKeyboardComponent: Component { component.hideTopPanelUpdated(self.isTopPanelHidden, transition) } - private func openSearch() { + public func openSearch() { guard let component = self.component else { return } diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/GifPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/GifPagerContentComponent.swift index e2a42b9831..159a9223e2 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/GifPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/GifPagerContentComponent.swift @@ -136,15 +136,18 @@ public final class GifPagerContentComponent: Component { public let performItemAction: (Item, UIView, CGRect) -> Void public let openGifContextMenu: (Item, UIView, CGRect, ContextGesture, Bool) -> Void public let loadMore: (String) -> Void + public let openSearch: () -> Void public init( performItemAction: @escaping (Item, UIView, CGRect) -> Void, openGifContextMenu: @escaping (Item, UIView, CGRect, ContextGesture, Bool) -> Void, - loadMore: @escaping (String) -> Void + loadMore: @escaping (String) -> Void, + openSearch: @escaping () -> Void ) { self.performItemAction = performItemAction self.openGifContextMenu = openGifContextMenu self.loadMore = loadMore + self.openSearch = openSearch } } @@ -178,6 +181,8 @@ public final class GifPagerContentComponent: Component { public let items: [Item] public let isLoading: Bool public let loadMoreToken: String? + public let displaySearchWithPlaceholder: String? + public let searchInitiallyHidden: Bool public init( context: AccountContext, @@ -185,7 +190,9 @@ public final class GifPagerContentComponent: Component { subject: Subject, items: [Item], isLoading: Bool, - loadMoreToken: String? + loadMoreToken: String?, + displaySearchWithPlaceholder: String?, + searchInitiallyHidden: Bool ) { self.context = context self.inputInteraction = inputInteraction @@ -193,6 +200,8 @@ public final class GifPagerContentComponent: Component { self.items = items self.isLoading = isLoading self.loadMoreToken = loadMoreToken + self.displaySearchWithPlaceholder = displaySearchWithPlaceholder + self.searchInitiallyHidden = searchInitiallyHidden } public static func ==(lhs: GifPagerContentComponent, rhs: GifPagerContentComponent) -> Bool { @@ -214,7 +223,12 @@ public final class GifPagerContentComponent: Component { if lhs.loadMoreToken != rhs.loadMoreToken { return false } - + if lhs.displaySearchWithPlaceholder != rhs.displaySearchWithPlaceholder { + return false + } + if lhs.searchInitiallyHidden != rhs.searchInitiallyHidden { + return false + } return true } @@ -240,6 +254,9 @@ public final class GifPagerContentComponent: Component { let itemsPerRow: Int let contentSize: CGSize + var searchInsets: UIEdgeInsets + var searchHeight: CGFloat + init(width: CGFloat, containerInsets: UIEdgeInsets, itemCount: Int) { self.width = width self.containerInsets = containerInsets @@ -247,6 +264,9 @@ public final class GifPagerContentComponent: Component { self.horizontalSpacing = 1.0 self.verticalSpacing = 1.0 + self.searchHeight = 54.0 + self.searchInsets = UIEdgeInsets(top: max(0.0, containerInsets.top + 1.0), left: containerInsets.left, bottom: 0.0, right: containerInsets.right) + let defaultItemSize: CGFloat = 120.0 let itemHorizontalSpace = width - self.containerInsets.left - self.containerInsets.right @@ -408,17 +428,103 @@ public final class GifPagerContentComponent: Component { } } - private final class ContentScrollView: UIScrollView, PagerExpandableScrollView { + public final class ContentScrollLayer: CALayer { + public var mirrorLayer: CALayer? + + override public init() { + super.init() + } + + override public init(layer: Any) { + super.init(layer: layer) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override public var position: CGPoint { + get { + return super.position + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.position = value + } + super.position = value + } + } + + override public var bounds: CGRect { + get { + return super.bounds + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.bounds = value + } + super.bounds = value + } + } + + override public func add(_ animation: CAAnimation, forKey key: String?) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.add(animation, forKey: key) + } + + super.add(animation, forKey: key) + } + + override public func removeAllAnimations() { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.removeAllAnimations() + } + + super.removeAllAnimations() + } + + override public func removeAnimation(forKey: String) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.removeAnimation(forKey: forKey) + } + + super.removeAnimation(forKey: forKey) + } } - private let backgroundView: BlurredBackgroundView + private final class ContentScrollView: UIScrollView, PagerExpandableScrollView { + override static var layerClass: AnyClass { + return ContentScrollLayer.self + } + + private let mirrorView: UIView + + init(mirrorView: UIView) { + self.mirrorView = mirrorView + + super.init(frame: CGRect()) + + (self.layer as? ContentScrollLayer)?.mirrorLayer = mirrorView.layer + self.canCancelContentTouches = true + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func touchesShouldCancel(in view: UIView) -> Bool { + return true + } + } private let shimmerHostView: PortalSourceView private let standaloneShimmerEffect: StandaloneShimmerEffect + private let backgroundView: BlurredBackgroundView + private var vibrancyEffectView: UIVisualEffectView? + private let mirrorContentScrollView: UIView private let scrollView: ContentScrollView private let placeholdersContainerView: UIView + private var visibleSearchHeader: EmojiSearchHeaderView? private var visibleItemPlaceholderViews: [ItemKey: ItemPlaceholderView] = [:] private var visibleItemLayers: [ItemKey: ItemLayer] = [:] private var ignoreScrolling: Bool = false @@ -438,7 +544,11 @@ public final class GifPagerContentComponent: Component { self.placeholdersContainerView = UIView() - self.scrollView = ContentScrollView() + self.mirrorContentScrollView = UIView() + self.mirrorContentScrollView.layer.anchorPoint = CGPoint() + self.mirrorContentScrollView.clipsToBounds = true + self.scrollView = ContentScrollView(mirrorView: self.mirrorContentScrollView) + self.scrollView.layer.anchorPoint = CGPoint() super.init(frame: frame) @@ -636,6 +746,11 @@ public final class GifPagerContentComponent: Component { var validIds = Set() + var searchInset: CGFloat = 0.0 + if let _ = component.displaySearchWithPlaceholder { + searchInset += itemLayout.searchHeight + } + if let itemRange = itemLayout.visibleItems(for: self.scrollView.bounds) { for index in itemRange.lowerBound ..< itemRange.upperBound { var item: Item? @@ -657,7 +772,7 @@ public final class GifPagerContentComponent: Component { validIds.insert(itemId) - let itemFrame = itemLayout.frame(at: index) + let itemFrame = itemLayout.frame(at: index).offsetBy(dx: 0.0, dy: searchInset) let itemTransition: Transition = .immediate var updateItemLayerPlaceholder = false @@ -768,9 +883,23 @@ public final class GifPagerContentComponent: Component { guard let theme = self.theme else { return } + if self.vibrancyEffectView == nil { + let style: UIBlurEffect.Style + style = .extraLight + let blurEffect = UIBlurEffect(style: style) + let vibrancyEffect = UIVibrancyEffect(blurEffect: blurEffect) + let vibrancyEffectView = UIVisualEffectView(effect: vibrancyEffect) + self.vibrancyEffectView = vibrancyEffectView + self.backgroundView.addSubview(vibrancyEffectView) + vibrancyEffectView.contentView.addSubview(self.mirrorContentScrollView) + } self.backgroundView.updateColor(color: theme.chat.inputMediaPanel.backgroundColor, enableBlur: true, forceKeepBlur: false, transition: transition.containedViewLayoutTransition) transition.setFrame(view: self.backgroundView, frame: backgroundFrame) self.backgroundView.update(size: backgroundFrame.size, transition: transition.containedViewLayoutTransition) + + if let vibrancyEffectView = self.vibrancyEffectView { + transition.setFrame(view: vibrancyEffectView, frame: CGRect(origin: CGPoint(x: 0.0, y: -backgroundFrame.minY), size: CGSize(width: backgroundFrame.width, height: backgroundFrame.height + backgroundFrame.minY))) + } } func update(component: GifPagerContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { @@ -802,7 +931,12 @@ public final class GifPagerContentComponent: Component { self.itemLayout = itemLayout self.ignoreScrolling = true - transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize)) + let scrollOriginY: CGFloat = 0.0 + let scrollSize = CGSize(width: availableSize.width, height: availableSize.height) + + transition.setPosition(view: self.scrollView, position: CGPoint(x: 0.0, y: scrollOriginY)) + self.scrollView.bounds = CGRect(origin: self.scrollView.bounds.origin, size: scrollSize) + if self.scrollView.contentSize != itemLayout.contentSize { self.scrollView.contentSize = itemLayout.contentSize } @@ -817,6 +951,44 @@ public final class GifPagerContentComponent: Component { self.previousScrollingOffset = self.scrollView.contentOffset.y self.ignoreScrolling = false + if let displaySearchWithPlaceholder = component.displaySearchWithPlaceholder { + let visibleSearchHeader: EmojiSearchHeaderView + if let current = self.visibleSearchHeader { + visibleSearchHeader = current + } else { + visibleSearchHeader = EmojiSearchHeaderView(activated: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.component?.inputInteraction.openSearch() + }, deactivated: { + }, updateQuery: {_, _ in + }) + self.visibleSearchHeader = visibleSearchHeader + self.scrollView.addSubview(visibleSearchHeader) + self.mirrorContentScrollView.addSubview(visibleSearchHeader.tintContainerView) + } + + let searchHeaderFrame = CGRect(origin: CGPoint(x: itemLayout.searchInsets.left, y: itemLayout.searchInsets.top), size: CGSize(width: itemLayout.width - itemLayout.searchInsets.left - itemLayout.searchInsets.right, height: itemLayout.searchHeight)) + visibleSearchHeader.update(theme: keyboardChildEnvironment.theme, strings: keyboardChildEnvironment.strings, text: displaySearchWithPlaceholder, useOpaqueTheme: false, isActive: false, size: searchHeaderFrame.size, canFocus: false, transition: transition) + transition.setFrame(view: visibleSearchHeader, frame: searchHeaderFrame, completion: { [weak self] completed in + guard let strongSelf = self, completed, let visibleSearchHeader = strongSelf.visibleSearchHeader else { + return + } + + if visibleSearchHeader.superview != strongSelf.scrollView { + strongSelf.scrollView.addSubview(visibleSearchHeader) + strongSelf.mirrorContentScrollView.addSubview(visibleSearchHeader.tintContainerView) + } + }) + } else { + if let visibleSearchHeader = self.visibleSearchHeader { + self.visibleSearchHeader = nil + visibleSearchHeader.removeFromSuperview() + visibleSearchHeader.tintContainerView.removeFromSuperview() + } + } + self.updateVisibleItems(attemptSynchronousLoads: true) return availableSize diff --git a/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift b/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift index d127f5eb98..90482642c7 100644 --- a/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift +++ b/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift @@ -17,6 +17,72 @@ import Postbox import PremiumUI import ProgressNavigationButtonNode +private final class SwitchComponent: Component { + typealias EnvironmentType = Empty + + let value: Bool + let valueUpdated: (Bool) -> Void + + init( + value: Bool, + valueUpdated: @escaping (Bool) -> Void + ) { + self.value = value + self.valueUpdated = valueUpdated + } + + static func ==(lhs: SwitchComponent, rhs: SwitchComponent) -> Bool { + if lhs.value != rhs.value { + return false + } + return true + } + + final class View: UIView { + private let switchView: UISwitch + + private var component: SwitchComponent? + + override init(frame: CGRect) { + self.switchView = UISwitch() + + super.init(frame: frame) + + self.addSubview(self.switchView) + + self.switchView.addTarget(self, action: #selector(self.valueChanged(_:)), for: .valueChanged) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc func valueChanged(_ sender: Any) { + self.component?.valueUpdated(self.switchView.isOn) + } + + func update(component: SwitchComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.component = component + + self.switchView.setOn(component.value, animated: !transition.animation.isImmediate) + + self.switchView.sizeToFit() + self.switchView.frame = CGRect(origin: .zero, size: self.switchView.frame.size) + + return self.switchView.frame.size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + + private final class TitleFieldComponent: Component { typealias EnvironmentType = Empty @@ -384,14 +450,16 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { let mode: ForumCreateTopicScreen.Mode let titleUpdated: (String) -> Void let iconUpdated: (Int64?) -> Void + let isHiddenUpdated: (Bool) -> Void let openPremium: () -> Void - init(context: AccountContext, peerId: EnginePeer.Id, mode: ForumCreateTopicScreen.Mode, titleUpdated: @escaping (String) -> Void, iconUpdated: @escaping (Int64?) -> Void, openPremium: @escaping () -> Void) { + init(context: AccountContext, peerId: EnginePeer.Id, mode: ForumCreateTopicScreen.Mode, titleUpdated: @escaping (String) -> Void, iconUpdated: @escaping (Int64?) -> Void, isHiddenUpdated: @escaping (Bool) -> Void, openPremium: @escaping () -> Void) { self.context = context self.peerId = peerId self.mode = mode self.titleUpdated = titleUpdated self.iconUpdated = iconUpdated + self.isHiddenUpdated = isHiddenUpdated self.openPremium = openPremium } @@ -412,6 +480,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { private let context: AccountContext private let titleUpdated: (String) -> Void private let iconUpdated: (Int64?) -> Void + private let isHiddenUpdated: (Bool) -> Void private let openPremium: () -> Void var emojiContent: EmojiPagerContentComponent? @@ -426,13 +495,15 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { var title: String var fileId: Int64 var iconColor: Int32 + var isHidden: Bool private var hasPremium: Bool = false - init(context: AccountContext, mode: ForumCreateTopicScreen.Mode, titleUpdated: @escaping (String) -> Void, iconUpdated: @escaping (Int64?) -> Void, openPremium: @escaping () -> Void) { + init(context: AccountContext, mode: ForumCreateTopicScreen.Mode, titleUpdated: @escaping (String) -> Void, iconUpdated: @escaping (Int64?) -> Void, isHiddenUpdated: @escaping (Bool) -> Void, openPremium: @escaping () -> Void) { self.context = context self.titleUpdated = titleUpdated self.iconUpdated = iconUpdated + self.isHiddenUpdated = isHiddenUpdated self.openPremium = openPremium switch mode { @@ -440,13 +511,14 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { self.isGeneral = false self.title = "" self.fileId = 0 - self.iconColor = ForumCreateTopicScreen.iconColors.randomElement() ?? 0x0 - case let .edit(threadId, info): + self.isHidden = false + case let .edit(threadId, info, isHidden): self.isGeneral = threadId == 1 self.title = info.title self.fileId = info.icon ?? 0 self.iconColor = info.iconColor + self.isHidden = isHidden } super.init() @@ -511,6 +583,12 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { self.updateEmojiContent() } + func updateIsHidden(_ isHidden: Bool) { + self.isHidden = isHidden + self.updated(transition: .immediate) + self.isHiddenUpdated(isHidden) + } + func updateEmojiContent() { self.emojiContentDisposable.set(( EmojiPagerContentComponent.emojiInputData( @@ -575,6 +653,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { mode: self.mode, titleUpdated: self.titleUpdated, iconUpdated: self.iconUpdated, + isHiddenUpdated: self.isHiddenUpdated, openPremium: self.openPremium ) } @@ -585,6 +664,11 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { let titleBackground = Child(RoundedRectangle.self) let titleField = Child(TitleFieldComponent.self) + let hideBackground = Child(RoundedRectangle.self) + let hideTitle = Child(MultilineTextComponent.self) + let hideSwitch = Child(SwitchComponent.self) + let hideInfo = Child(MultilineTextComponent.self) + let iconHeader = Child(MultilineTextComponent.self) let iconBackground = Child(RoundedRectangle.self) let iconSelector = Child(TopicIconSelectionComponent.self) @@ -624,7 +708,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { horizontalAlignment: .natural, maximumNumberOfLines: 1 ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - environment.safeInsets.left - environment.safeInsets.right, height: CGFloat.greatestFiniteMagnitude), transition: .immediate ) context.add(titleHeader @@ -637,7 +721,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { color: environment.theme.list.itemBlocksBackgroundColor, cornerRadius: 10.0 ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 44.0), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - environment.safeInsets.left - environment.safeInsets.right, height: 44.0), transition: context.transition ) context.add(titleBackground @@ -662,7 +746,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { state?.switchIcon() } ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 44.0), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - environment.safeInsets.left - environment.safeInsets.right, height: 44.0), transition: context.transition ) context.add(titleField @@ -671,8 +755,79 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { contentHeight += titleBackground.size.height + sectionSpacing - if case let .edit(threadId, _) = context.component.mode, threadId == 1 { + if case let .edit(threadId, _, _) = context.component.mode, threadId == 1 { + let hideBackground = hideBackground.update( + component: RoundedRectangle( + color: environment.theme.list.itemBlocksBackgroundColor, + cornerRadius: 10.0 + ), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - environment.safeInsets.left - environment.safeInsets.right, height: 44.0), + transition: context.transition + ) + context.add(hideBackground + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentHeight + hideBackground.size.height / 2.0)) + ) + let hideTitle = hideTitle.update( + component: MultilineTextComponent( + text: .plain(NSAttributedString( + string: environment.strings.CreateTopic_ShowGeneral, + font: Font.regular(17.0), + textColor: environment.theme.list.itemPrimaryTextColor, + paragraphAlignment: .natural) + ), + horizontalAlignment: .natural, + maximumNumberOfLines: 0 + ), + availableSize: CGSize( + width: context.availableSize.width - sideInset * 2.0 - environment.safeInsets.left - environment.safeInsets.right, + height: CGFloat.greatestFiniteMagnitude + ), + transition: .immediate + ) + context.add(hideTitle + .position(CGPoint(x: environment.safeInsets.left + sideInset + 16.0 + hideTitle.size.width / 2.0, y: contentHeight + hideBackground.size.height / 2.0)) + ) + + let hideSwitch = hideSwitch.update( + component: SwitchComponent( + value: !state.isHidden, + valueUpdated: { [weak state] newValue in + state?.updateIsHidden(!newValue) + } + ), + availableSize: CGSize( + width: context.availableSize.width - sideInset * 2.0 - environment.safeInsets.left - environment.safeInsets.right, + height: CGFloat.greatestFiniteMagnitude + ), + transition: .immediate + ) + context.add(hideSwitch + .position(CGPoint(x: context.availableSize.width - environment.safeInsets.right - sideInset - 16.0 - hideSwitch.size.width / 2.0, y: contentHeight + hideBackground.size.height / 2.0)) + ) + + contentHeight += hideBackground.size.height + + let hideInfo = hideInfo.update( + component: MultilineTextComponent( + text: .plain(NSAttributedString( + string: environment.strings.CreateTopic_ShowGeneralInfo, + font: Font.regular(13.0), + textColor: environment.theme.list.freeTextColor, + paragraphAlignment: .natural) + ), + horizontalAlignment: .natural, + maximumNumberOfLines: 0 + ), + availableSize: CGSize( + width: context.availableSize.width - sideInset * 2.0 - environment.safeInsets.left - environment.safeInsets.right, + height: CGFloat.greatestFiniteMagnitude + ), + transition: .immediate + ) + context.add(hideInfo + .position(CGPoint(x: environment.safeInsets.left + sideInset + 16.0 + hideInfo.size.width / 2.0, y: contentHeight + 7.0 + hideInfo.size.height / 2.0)) + ) } else { let iconHeader = iconHeader.update( component: MultilineTextComponent( @@ -686,13 +841,13 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { maximumNumberOfLines: 1 ), availableSize: CGSize( - width: context.availableSize.width - sideInset * 2.0, + width: context.availableSize.width - sideInset * 2.0 - environment.safeInsets.left - environment.safeInsets.right, height: CGFloat.greatestFiniteMagnitude ), transition: .immediate ) context.add(iconHeader - .position(CGPoint(x: sideInset * 2.0 + iconHeader.size.width / 2.0, y: contentHeight + iconHeader.size.height / 2.0)) + .position(CGPoint(x: environment.safeInsets.left + sideInset + 16.0 + iconHeader.size.width / 2.0, y: contentHeight + iconHeader.size.height / 2.0)) ) contentHeight += iconHeader.size.height + headerSpacing @@ -704,7 +859,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { cornerRadius: 10.0 ), availableSize: CGSize( - width: context.availableSize.width - sideInset * 2.0, + width: context.availableSize.width - sideInset * 2.0 - environment.safeInsets.left - environment.safeInsets.right, height: context.availableSize.height - contentHeight - bottomInset ), transition: context.transition @@ -726,7 +881,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { separatorColor: environment.theme.list.blocksBackgroundColor ), environment: {}, - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: availableHeight), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - environment.safeInsets.left - environment.safeInsets.right, height: availableHeight), transition: context.transition ) context.add(iconSelector @@ -746,6 +901,8 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { }, openFeatured: { }, + openSearch: { + }, addGroupAction: { groupId, isPremiumLocked in guard let collectionId = groupId.base as? ItemCollectionId else { return @@ -805,7 +962,7 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { public enum Mode: Equatable { case create - case edit(threadId: Int64, threadInfo: EngineMessageHistoryThread.Info) + case edit(threadId: Int64, threadInfo: EngineMessageHistoryThread.Info, isHidden: Bool) } private let context: AccountContext @@ -813,8 +970,8 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { private var doneBarItem: UIBarButtonItem? - private var state: (String, Int64?) = ("", nil) - public var completion: (String, Int64?) -> Void = { _, _ in } + private var state: (String, Int64?, Bool?) = ("", nil, nil) + public var completion: (String, Int64?, Bool?) -> Void = { _, _, _ in } public var isInProgress: Bool = false { didSet { @@ -835,12 +992,15 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { var titleUpdatedImpl: ((String) -> Void)? var iconUpdatedImpl: ((Int64?) -> Void)? + var isHiddenUpdatedImpl: ((Bool) -> Void)? var openPremiumImpl: (() -> Void)? super.init(context: context, component: ForumCreateTopicScreenComponent(context: context, peerId: peerId, mode: mode, titleUpdated: { title in titleUpdatedImpl?(title) }, iconUpdated: { fileId in iconUpdatedImpl?(fileId) + }, isHiddenUpdated: { isHidden in + isHiddenUpdatedImpl?(isHidden) }, openPremium: { openPremiumImpl?() }), navigationBarAppearance: .transparent) @@ -852,11 +1012,11 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { case .create: title = presentationData.strings.CreateTopic_CreateTitle doneTitle = presentationData.strings.CreateTopic_Create - case let .edit(_, topic): + case let .edit(threadId, topic, isHidden): title = presentationData.strings.CreateTopic_EditTitle doneTitle = presentationData.strings.Common_Done - self.state = (topic.title, topic.icon) + self.state = (topic.title, topic.icon, threadId == 1 ? isHidden : nil) } self.title = title @@ -872,12 +1032,12 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { } titleUpdatedImpl = { [weak self] title in - guard let self else { + guard let strongSelf = self else { return } - self.doneBarItem?.isEnabled = !title.isEmpty + strongSelf.doneBarItem?.isEnabled = !title.isEmpty - self.state = (title, self.state.1) + strongSelf.state = (title, strongSelf.state.1, strongSelf.state.2) } iconUpdatedImpl = { [weak self] fileId in @@ -885,7 +1045,15 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { return } - strongSelf.state = (strongSelf.state.0, fileId) + strongSelf.state = (strongSelf.state.0, fileId, strongSelf.state.2) + } + + isHiddenUpdatedImpl = { [weak self] isHidden in + guard let strongSelf = self else { + return + } + + strongSelf.state = (strongSelf.state.0, strongSelf.state.1, isHidden) } openPremiumImpl = { [weak self] in @@ -916,6 +1084,6 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { } @objc private func createPressed() { - self.completion(self.state.0, self.state.1) + self.completion(self.state.0, self.state.1, self.state.2) } } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index c8f4c11eb7..2202a6b25a 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -9587,6 +9587,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } let _ = strongSelf.context.engine.peers.setForumChannelTopicClosed(id: peerId, threadId: threadId, isClosed: false).start() + }, requestLayout: { [weak self] transition in + if let strongSelf = self, let layout = strongSelf.validLayout { + strongSelf.containerLayoutUpdated(layout, transition: transition) + } }, chatController: { [weak self] in return self }, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get(), inlineSearch: self.performingInlineSearch.get())) diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index c1e06fa9f0..e839dfc6d6 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -1349,7 +1349,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { let heightAndOverflow = inputNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, standardInputHeight: inputHeight, inputHeight: layout.inputHeight ?? 0.0, maximumHeight: maximumInputNodeHeight, inputPanelHeight: inputPanelNodeBaseHeight, transition: immediatelyLayoutInputNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState, deviceMetrics: layout.deviceMetrics, isVisible: self.isInFocus, isExpanded: self.inputPanelContainerNode.stableIsExpanded) - let boundedHeight = min(heightAndOverflow.0, layout.standardInputHeight) + let boundedHeight = inputNode.followsDefaultHeight ? min(heightAndOverflow.0, layout.standardInputHeight) : heightAndOverflow.0 inputNodeHeightAndOverflow = ( boundedHeight, diff --git a/submodules/TelegramUI/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Sources/ChatEntityKeyboardInputNode.swift index cd7df1e188..fc57ae680c 100644 --- a/submodules/TelegramUI/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatEntityKeyboardInputNode.swift @@ -537,7 +537,9 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { itemLayoutType: .detailed, itemContentUniqueId: nil, warpContentsOnEdges: false, - displaySearchWithPlaceholder: nil, + displaySearchWithPlaceholder: "Search Stickers", + searchInitiallyHidden: false, + searchIsPlaceholderOnly: true, emptySearchResults: nil, enableLongPress: false, selectedItems: Set() @@ -585,6 +587,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { openGifContextMenu: { _, _, _, _, _ in }, loadMore: { _ in + }, + openSearch: { } ) @@ -597,7 +601,9 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { subject: .recent, items: [], isLoading: false, - loadMoreToken: nil + loadMoreToken: nil, + displaySearchWithPlaceholder: nil, + searchInitiallyHidden: false ) )) @@ -645,7 +651,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { availableGifSearchEmojies.append(EntityKeyboardComponent.GifSearchEmoji(emoji: reaction, file: file, title: title)) } } - + return InputData( emoji: emoji, stickers: stickers, @@ -664,6 +670,9 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { private var inputDataDisposable: Disposable? private var hasRecentGifsDisposable: Disposable? + private let emojiSearchDisposable = MetaDisposable() + private let emojiSearchResult = Promise<(groups: [EmojiPagerContentComponent.ItemGroup], id: AnyHashable)?>(nil) + private let controllerInteraction: ChatControllerInteraction? private var inputNodeInteraction: ChatMediaInputNodeInteraction? @@ -672,6 +681,12 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { private var isMarkInputCollapsed: Bool = false + private var isEmojiSearchActive: Bool = false { + didSet { + self.followsDefaultHeight = !self.isEmojiSearchActive + } + } + var externalTopPanelContainerImpl: PagerExternalTopPanelContainer? override var externalTopPanelContainer: UIView? { return self.externalTopPanelContainerImpl @@ -753,7 +768,9 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { subject: subject, items: items, isLoading: false, - loadMoreToken: nil + loadMoreToken: nil, + displaySearchWithPlaceholder: "Search GIFs", + searchInitiallyHidden: false ) ) } @@ -782,7 +799,9 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { subject: subject, items: items, isLoading: isLoading, - loadMoreToken: nil + loadMoreToken: nil, + displaySearchWithPlaceholder: nil, + searchInitiallyHidden: false ) ) } @@ -813,7 +832,9 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { subject: subject, items: items, isLoading: isLoading, - loadMoreToken: loadMoreToken + loadMoreToken: loadMoreToken, + displaySearchWithPlaceholder: nil, + searchInitiallyHidden: false ) ) } @@ -892,7 +913,9 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { subject: subject, items: items, isLoading: isLoading, - loadMoreToken: loadMoreToken + loadMoreToken: loadMoreToken, + displaySearchWithPlaceholder: nil, + searchInitiallyHidden: false ) ) } @@ -918,6 +941,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { private weak var currentUndoOverlayController: UndoOverlayController? + init(context: AccountContext, currentInputData: InputData, updatedInputData: Signal, defaultToEmojiTab: Bool, controllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?, chatPeerId: PeerId?) { self.context = context self.currentInputData = currentInputData @@ -1049,6 +1073,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { }, openFeatured: { }, + openSearch: { + }, addGroupAction: { [weak self, weak controllerInteraction] groupId, isPremiumLocked in guard let controllerInteraction = controllerInteraction, let collectionId = groupId.base as? ItemCollectionId else { return @@ -1092,6 +1118,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { return } if groupId == AnyHashable("recent") { + controllerInteraction.dismissTextInput() let presentationData = context.sharedContext.currentPresentationData.with { $0 } let actionSheet = ActionSheetController(theme: ActionSheetControllerTheme(presentationTheme: presentationData.theme, fontSize: presentationData.listsFontSize)) var items: [ActionSheetItem] = [] @@ -1128,9 +1155,136 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { navigationController: { [weak controllerInteraction] in return controllerInteraction?.navigationController() }, - requestUpdate: { _ in + requestUpdate: { [weak self] transition in + guard let _ = self else { + return + } +// if !transition.animation.isImmediate { +// strongSelf.interfaceInteraction?.requestLayout(transition.containedViewLayoutTransition) +// } }, - updateSearchQuery: { _, _ in + updateSearchQuery: { [weak self] rawQuery, languageCode in + guard let strongSelf = self else { + return + } + + let query = rawQuery.trimmingCharacters(in: .whitespacesAndNewlines) + + if query.isEmpty { + strongSelf.emojiSearchDisposable.set(nil) + strongSelf.emojiSearchResult.set(.single(nil)) + } else { + let context = strongSelf.context + + var signal = context.engine.stickers.searchEmojiKeywords(inputLanguageCode: languageCode, query: query, completeMatch: false) + if !languageCode.lowercased().hasPrefix("en") { + signal = signal + |> mapToSignal { keywords in + return .single(keywords) + |> then( + context.engine.stickers.searchEmojiKeywords(inputLanguageCode: "en-US", query: query, completeMatch: query.count < 3) + |> map { englishKeywords in + return keywords + englishKeywords + } + ) + } + } + + let hasPremium = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) + |> map { peer -> Bool in + guard case let .user(user) = peer else { + return false + } + return user.isPremium + } + |> distinctUntilChanged + + let resultSignal = signal + |> mapToSignal { keywords -> Signal<[EmojiPagerContentComponent.ItemGroup], NoError> in + return combineLatest( + context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [], namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000), + context.engine.stickers.availableReactions(), + hasPremium + ) + |> take(1) + |> map { view, availableReactions, hasPremium -> [EmojiPagerContentComponent.ItemGroup] in + var result: [(String, TelegramMediaFile?, String)] = [] + + var allEmoticons: [String: String] = [:] + for keyword in keywords { + for emoticon in keyword.emoticons { + allEmoticons[emoticon] = keyword.keyword + } + } + + for entry in view.entries { + guard let item = entry.item as? StickerPackItem else { + continue + } + for attribute in item.file.attributes { + switch attribute { + case let .CustomEmoji(_, alt, _): + if !item.file.isPremiumEmoji || hasPremium { + if !alt.isEmpty, let keyword = allEmoticons[alt] { + result.append((alt, item.file, keyword)) + } else if alt == query { + result.append((alt, item.file, alt)) + } + } + default: + break + } + } + } + + var items: [EmojiPagerContentComponent.Item] = [] + + var existingIds = Set() + for item in result { + if let itemFile = item.1 { + if existingIds.contains(itemFile.fileId) { + continue + } + existingIds.insert(itemFile.fileId) + let animationData = EntityKeyboardAnimationData(file: itemFile) + let item = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: itemFile, subgroupId: nil, + icon: .none, + accentTint: false + ) + items.append(item) + } + } + + return [EmojiPagerContentComponent.ItemGroup( + supergroupId: "search", + groupId: "search", + title: nil, + subtitle: nil, + actionButtonTitle: nil, + isFeatured: false, + isPremiumLocked: false, + isEmbedded: false, + hasClear: false, + collapsedLineCount: nil, + displayPremiumBadges: false, + headerItem: nil, + items: items + )] + } + } + + strongSelf.emojiSearchDisposable.set((resultSignal + |> delay(0.15, queue: .mainQueue()) + |> deliverOnMainQueue).start(next: { [weak self] result in + guard let strongSelf = self else { + return + } + strongSelf.emojiSearchResult.set(.single((result, AnyHashable(query)))) + })) + } }, chatPeerId: chatPeerId, peekBehavior: nil, @@ -1231,6 +1385,11 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { } )) }, + openSearch: { [weak self] in + if let strongSelf = self, let pagerView = strongSelf.entityKeyboardView.componentView as? EntityKeyboardComponent.View { + pagerView.openSearch() + } + }, addGroupAction: { groupId, isPremiumLocked in guard let controllerInteraction = controllerInteraction, let collectionId = groupId.base as? ItemCollectionId else { return @@ -1281,6 +1440,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { return } if groupId == AnyHashable("recent") { + controllerInteraction.dismissTextInput() let presentationData = context.sharedContext.currentPresentationData.with { $0 } let actionSheet = ActionSheetController(theme: ActionSheetControllerTheme(presentationTheme: presentationData.theme, fontSize: presentationData.listsFontSize)) var items: [ActionSheetItem] = [] @@ -1346,15 +1506,27 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { self.inputDataDisposable = (combineLatest(queue: .mainQueue(), updatedInputData, - self.gifComponent.get() + self.gifComponent.get(), + self.emojiSearchResult.get() ) - |> deliverOnMainQueue).start(next: { [weak self] inputData, gifs in + |> deliverOnMainQueue).start(next: { [weak self] inputData, gifs, emojiSearchResult in guard let strongSelf = self else { return } var inputData = inputData inputData.gifs = gifs + if let emojiSearchResult = emojiSearchResult { + var emptySearchResults: EmojiPagerContentComponent.EmptySearchResults? + if !emojiSearchResult.groups.contains(where: { !$0.items.isEmpty }) { + emptySearchResults = EmojiPagerContentComponent.EmptySearchResults( + text: "No emoji found", //strongSelf.presentationData.strings.EmojiSearch_SearchStatusesEmptyResult, + iconFile: nil + ) + } + inputData.emoji = inputData.emoji.withUpdatedItemGroups(itemGroups: emojiSearchResult.groups, itemContentUniqueId: emojiSearchResult.id, emptySearchResults: emptySearchResults) + } + var transition: Transition = .immediate var useAnimation = false @@ -1436,6 +1608,11 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { return } gifContext.loadMore(token: token) + }, + openSearch: { [weak self] in + if let strongSelf = self, let pagerView = strongSelf.entityKeyboardView.componentView as? EntityKeyboardComponent.View { + pagerView.openSearch() + } } ) @@ -1474,6 +1651,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { deinit { self.inputDataDisposable?.dispose() self.hasRecentGifsDisposable?.dispose() + self.emojiSearchDisposable.dispose() } private func reloadGifContext() { @@ -1515,7 +1693,10 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { let wasMarkedInputCollapsed = self.isMarkInputCollapsed self.isMarkInputCollapsed = false - let expandedHeight = standardInputHeight + var expandedHeight = standardInputHeight + if self.isEmojiSearchActive && !isExpanded { + expandedHeight += 118.0 + } var hiddenInputHeight: CGFloat = 0.0 if self.hideInput && !self.adjustLayoutForHiddenInput { @@ -1563,7 +1744,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { theme: interfaceState.theme, strings: interfaceState.strings, isContentInFocus: isVisible, - containerInsets: UIEdgeInsets(top: 0.0, left: leftInset, bottom: bottomInset, right: rightInset), + containerInsets: UIEdgeInsets(top: self.isEmojiSearchActive ? -34.0 : 0.0, left: leftInset, bottom: bottomInset, right: rightInset), topPanelInsets: UIEdgeInsets(), emojiContent: self.currentInputData.emoji, stickerContent: stickerContent, @@ -1591,7 +1772,12 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { strongSelf.hideInputUpdated?(transition.containedViewLayoutTransition) } }, - hideTopPanelUpdated: { _, _ in + hideTopPanelUpdated: { [weak self] hideTopPanel, transition in + guard let strongSelf = self else { + return + } + strongSelf.isEmojiSearchActive = hideTopPanel + strongSelf.performLayout(transition: transition) }, switchToTextInput: { [weak self] in self?.switchToTextInput?() @@ -1637,7 +1823,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { deviceMetrics: deviceMetrics, hiddenInputHeight: hiddenInputHeight, displayBottomPanel: true, - isExpanded: isExpanded + isExpanded: isExpanded && !self.isEmojiSearchActive )), environment: {}, containerSize: CGSize(width: width, height: expandedHeight) @@ -1722,7 +1908,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode { private func processInputData(inputData: InputData) -> InputData { return InputData( - emoji: inputData.emoji.withUpdatedItemGroups(itemGroups: self.processStableItemGroupList(category: .emoji, itemGroups: inputData.emoji.itemGroups), itemContentUniqueId: nil, emptySearchResults: nil), + emoji: inputData.emoji.withUpdatedItemGroups(itemGroups: self.processStableItemGroupList(category: .emoji, itemGroups: inputData.emoji.itemGroups), itemContentUniqueId: inputData.emoji.itemContentUniqueId, emptySearchResults: inputData.emoji.emptySearchResults), stickers: inputData.stickers.flatMap { stickers in return stickers.withUpdatedItemGroups(itemGroups: self.processStableItemGroupList(category: .stickers, itemGroups: stickers.itemGroups), itemContentUniqueId: nil, emptySearchResults: nil) }, @@ -2027,6 +2213,8 @@ final class EntityInputView: UIView, AttachmentTextInputPanelInputView, UIInputV }, openFeatured: { }, + openSearch: { + }, addGroupAction: { _, _ in }, clearGroup: { [weak self] groupId in @@ -2034,6 +2222,7 @@ final class EntityInputView: UIView, AttachmentTextInputPanelInputView, UIInputV return } if groupId == AnyHashable("recent") { + strongSelf.window?.endEditing(true) let presentationData = context.sharedContext.currentPresentationData.with { $0 } let actionSheet = ActionSheetController(theme: ActionSheetControllerTheme(presentationTheme: presentationData.theme, fontSize: presentationData.listsFontSize)) var items: [ActionSheetItem] = [] diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsController.swift b/submodules/TelegramUI/Sources/ChatRecentActionsController.swift index c84f4a18bf..5fdfa15497 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsController.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsController.swift @@ -156,6 +156,7 @@ final class ChatRecentActionsController: TelegramBaseController { }, insertText: { _ in }, backwardsDeleteText: { }, restartTopic: { + }, requestLayout: { _ in }, chatController: { return nil }, statuses: nil) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 41f706cdd1..47811f9687 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -374,6 +374,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode { }, insertText: { _ in }, backwardsDeleteText: { }, restartTopic: { + }, requestLayout: { _ in }, chatController: { return nil }, statuses: nil) @@ -2906,15 +2907,22 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate case .edit: if case let .replyThread(message) = strongSelf.chatLocation { let threadId = Int64(message.messageId.id) - if let threadInfo = strongSelf.data?.threadData?.info { - let controller = ForumCreateTopicScreen(context: strongSelf.context, peerId: strongSelf.peerId, mode: .edit(threadId: threadId, threadInfo: threadInfo)) + if let threadData = strongSelf.data?.threadData { + let controller = ForumCreateTopicScreen(context: strongSelf.context, peerId: strongSelf.peerId, mode: .edit(threadId: threadId, threadInfo: threadData.info, isHidden: threadData.isHidden)) controller.navigationPresentation = .modal let context = strongSelf.context - controller.completion = { [weak controller] title, fileId in + controller.completion = { [weak controller] title, fileId, isHidden in let _ = (context.engine.peers.editForumChannelTopic(id: peerId, threadId: threadId, title: title, iconFileId: fileId) |> deliverOnMainQueue).start(completed: { controller?.dismiss() }) + + if let isHidden = isHidden { + let _ = (context.engine.peers.setForumChannelTopicHidden(id: peerId, threadId: threadId, isHidden: isHidden) + |> deliverOnMainQueue).start(completed: { + controller?.dismiss() + }) + } } strongSelf.controller?.push(controller) } diff --git a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift index d83fb93dcf..5dbb7126a4 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift @@ -370,6 +370,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { }, insertText: { _ in }, backwardsDeleteText: { }, restartTopic: { + }, requestLayout: { _ in }, chatController: { return nil }, statuses: nil) diff --git a/submodules/TelegramUI/Sources/StickerPaneSearchContentNode.swift b/submodules/TelegramUI/Sources/StickerPaneSearchContentNode.swift index cb55258fa2..23d07053d8 100644 --- a/submodules/TelegramUI/Sources/StickerPaneSearchContentNode.swift +++ b/submodules/TelegramUI/Sources/StickerPaneSearchContentNode.swift @@ -390,7 +390,7 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode { } subscriber.putNext(result) }, completed: { - subscriber.putCompletion() +// subscriber.putCompletion() }) }