UI improvements

This commit is contained in:
Ali 2023-01-20 18:18:44 +04:00
parent f9127e3ebe
commit 263778c906
10 changed files with 420 additions and 94 deletions

View File

@ -154,10 +154,12 @@ public enum PagerComponentPanelHideBehavior {
public final class PagerComponentContentIcon: Equatable {
public let id: AnyHashable
public let imageName: String
public let title: String
public init(id: AnyHashable, imageName: String) {
public init(id: AnyHashable, imageName: String, title: String) {
self.id = id
self.imageName = imageName
self.title = title
}
public static func ==(lhs: PagerComponentContentIcon, rhs: PagerComponentContentIcon) -> Bool {
@ -170,6 +172,9 @@ public final class PagerComponentContentIcon: Equatable {
if lhs.imageName != rhs.imageName {
return false
}
if lhs.title != rhs.title {
return false
}
return true
}
}
@ -301,6 +306,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
private struct PaneTransitionGestureState {
var fraction: CGFloat = 0.0
var nextIndex: Int? = nil
}
private var contentClippingView: UIView
@ -371,6 +377,15 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
}
paneTransitionGestureState.fraction = fraction
let nextIndex: Int
if fraction > 0.0 {
nextIndex = max(0, centralIndex - 1)
} else {
nextIndex = min(component.contents.count - 1, centralIndex + 1)
}
paneTransitionGestureState.nextIndex = nextIndex
self.paneTransitionGestureState = paneTransitionGestureState
self.state?.updated(transition: .immediate)
}
@ -422,8 +437,26 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
}
public func navigateToContentId(_ id: AnyHashable) {
guard let centralId = self.centralId, let component = self.component, let _ = component.contents.firstIndex(where: { $0.id == centralId }), let nextIndex = component.contents.firstIndex(where: { $0.id == id }) else {
return
}
var updateTopPanelExpanded = false
if self.centralId != id {
var paneTransitionGestureState = PaneTransitionGestureState()
paneTransitionGestureState.fraction = 0.0
paneTransitionGestureState.nextIndex = nextIndex
self.paneTransitionGestureState = paneTransitionGestureState
self.state?.updated(transition: .immediate)
self.paneTransitionGestureState = nil
/*paneTransitionGestureState.fraction = 1.0
self.paneTransitionGestureState = paneTransitionGestureState
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))*/
self.centralId = id
if self.isTopPanelExpanded {
@ -470,8 +503,8 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
}
}
if centralId == nil {
centralId = component.contents.first?.id
}
centralId = component.contents.first?.id
}
}
if self.centralId != centralId {
@ -494,8 +527,42 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
scrollingPanelOffsetFraction = 0.0
}
var topPanelVisibility: CGFloat = 1.0
if let centralId = centralId, let index = component.contents.firstIndex(where: { $0.id == centralId }) {
if let paneTransitionGestureState = self.paneTransitionGestureState {
var nextIndex: Int
if paneTransitionGestureState.fraction > 0.0 {
nextIndex = max(0, index - 1)
} else {
nextIndex = min(component.contents.count - 1, index + 1)
}
if let nextIndexValue = paneTransitionGestureState.nextIndex {
nextIndex = nextIndexValue
}
let nextId = component.contents[nextIndex].id
var centralPanelFraction: CGFloat = 1.0
if !component.contentTopPanels.contains(where: { $0.id == centralId }) {
centralPanelFraction = 0.0
}
var nextPanelFraction: CGFloat = 1.0
if !component.contentTopPanels.contains(where: { $0.id == nextId }) {
nextPanelFraction = 0.0
}
topPanelVisibility = centralPanelFraction * (1.0 - abs(paneTransitionGestureState.fraction)) + nextPanelFraction * abs(paneTransitionGestureState.fraction)
} else {
if !component.contentTopPanels.contains(where: { $0.id == centralId }) {
topPanelVisibility = 0.0
}
}
}
var topPanelHeight: CGFloat = 0.0
if let topPanel = component.topPanel {
let effectiveTopPanelOffsetFraction = scrollingPanelOffsetFraction
let topPanelView: ComponentHostView<PagerComponentPanelEnvironment<TopPanelEnvironment>>
var topPanelTransition = panelStateTransition
if let current = self.topPanelView {
@ -511,6 +578,11 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
topPanelSuperview.addSubview(topPanelView)
}
var effectivePanelActiveContentId = centralId
if let centralId = centralId, !component.contentTopPanels.contains(where: { $0.id == centralId }) {
effectivePanelActiveContentId = component.contentTopPanels.first?.id
}
let topPanelSize = topPanelView.update(
transition: topPanelTransition,
component: topPanel,
@ -522,7 +594,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
contentIcons: [],
contentAccessoryLeftButtons: [],
contentAccessoryRightButtons: [],
activeContentId: centralId,
activeContentId: effectivePanelActiveContentId,
navigateToContentId: navigateToContentId,
visibilityFractionUpdated: self.topPanelVisibilityFractionUpdated,
isExpandedUpdated: { [weak self] isExpanded, transition in
@ -538,9 +610,11 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
self.topPanelHeight = topPanelSize.height
var topPanelOffset = topPanelSize.height * scrollingPanelOffsetFraction
var topPanelOffset = topPanelSize.height * effectiveTopPanelOffsetFraction
var topPanelVisibilityFraction: CGFloat = 1.0 - scrollingPanelOffsetFraction
topPanelOffset = min(topPanelSize.height, topPanelOffset + topPanelSize.height * (1.0 - topPanelVisibility))
var topPanelVisibilityFraction: CGFloat = 1.0 - effectiveTopPanelOffsetFraction
switch component.panelHideBehavior {
case .hide:
topPanelVisibilityFraction = 0.0
@ -549,6 +623,7 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
case .hideOnScroll:
break
}
topPanelVisibilityFraction *= topPanelVisibility
self.topPanelVisibilityFractionUpdated.invoke((topPanelVisibilityFraction, topPanelTransition))
@ -645,6 +720,8 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
self.bottomPanelHeight = 0.0
}
contentInsets.top *= topPanelVisibility
let effectiveTopPanelHeight: CGFloat
switch component.panelHideBehavior {
case .hide:
@ -699,14 +776,16 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
var checkingContentFrame = CGRect(origin: CGPoint(x: contentSize.width * CGFloat(indexOffset), y: 0.0), size: contentSize)
var contentFrame = CGRect(origin: CGPoint(x: contentSize.width * CGFloat(clippedIndexOffset), y: 0.0), size: contentSize)
var isInBounds = CGRect(origin: CGPoint(), size: availableSize).intersects(checkingContentFrame)
if let paneTransitionGestureState = self.paneTransitionGestureState {
checkingContentFrame.origin.x += paneTransitionGestureState.fraction * availableSize.width
contentFrame.origin.x += paneTransitionGestureState.fraction * availableSize.width
if let nextIndex = paneTransitionGestureState.nextIndex, nextIndex == index {
isInBounds = true
}
}
let content = component.contents[index]
let isInBounds = CGRect(origin: CGPoint(), size: availableSize).intersects(checkingContentFrame)
var isPartOfTransition = false
if case .none = transition.animation {
} else if self.contentViews[content.id] != nil {

View File

@ -732,7 +732,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
reactionContextNode.updateLayout(size: layout.size, insets: UIEdgeInsets(top: topInset, left: layout.safeInsets.left, bottom: 0.0, right: layout.safeInsets.right), anchorRect: reactionAnchorRect, isCoveredByInput: isCoveredByInput, isAnimatingOut: isAnimatingOut, transition: reactionContextNodeTransition)
self.proposedReactionsPositionLock = contentRect.minY - 18.0 - reactionContextNode.contentHeight - 46.0
self.proposedReactionsPositionLock = contentRect.minY - 18.0 - reactionContextNode.contentHeight - (46.0 + 54.0 - 4.0)
} else {
self.proposedReactionsPositionLock = nil
}

View File

@ -906,7 +906,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
expandItemSize = 30.0
expandTintOffset = 0.0
}
let baseNextFrame = CGRect(origin: CGPoint(x: self.scrollNode.view.bounds.width - expandItemSize - 9.0, y: containerHeight - contentHeight + floor((contentHeight - expandItemSize) / 2.0) + (self.isExpanded ? (46.0) : 0.0)), size: CGSize(width: expandItemSize, height: expandItemSize + self.extensionDistance))
let baseNextFrame = CGRect(origin: CGPoint(x: self.scrollNode.view.bounds.width - expandItemSize - 9.0, y: containerHeight - contentHeight + floor((contentHeight - expandItemSize) / 2.0) + (self.isExpanded ? (46.0 + 54.0 - 4.0) : 0.0)), size: CGSize(width: expandItemSize, height: expandItemSize + self.extensionDistance))
transition.updateFrame(view: expandItemView, frame: baseNextFrame)
transition.updateFrame(view: expandItemView.tintView, frame: baseNextFrame.offsetBy(dx: 0.0, dy: expandTintOffset))
@ -1007,7 +1007,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
visibleItemCount: itemCount
)
var scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: self.isExpanded ? (46.0) : 0.0), size: actualBackgroundFrame.size)
var scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: self.isExpanded ? (46.0 + 54.0 - 4.0) : 0.0), size: actualBackgroundFrame.size)
scrollFrame.origin.y += floorToScreenPixels(self.extensionDistance / 2.0)
transition.updateFrame(node: self.contentContainer, frame: visualBackgroundFrame, beginWithCurrentState: true)
@ -1107,7 +1107,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
if let mirrorContentClippingView = emojiView.mirrorContentClippingView {
mirrorContentClippingView.clipsToBounds = false
Transition(transition).animateBoundsOrigin(view: mirrorContentClippingView, from: CGPoint(x: 0.0, y: 46.0), to: CGPoint(), additive: true, completion: { [weak mirrorContentClippingView] _ in
Transition(transition).animateBoundsOrigin(view: mirrorContentClippingView, from: CGPoint(x: 0.0, y: 46.0 + 54.0 - 4.0), to: CGPoint(), additive: true, completion: { [weak mirrorContentClippingView] _ in
mirrorContentClippingView?.clipsToBounds = true
})
}
@ -1138,7 +1138,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
componentTransition.setFrame(view: componentView, frame: CGRect(origin: componentFrame.origin, size: CGSize(width: componentFrame.width, height: componentFrame.height)))
if animateIn {
transition.animatePositionAdditive(layer: componentView.layer, offset: CGPoint(x: 0.0, y: -(46.0) + floorToScreenPixels(self.animateFromExtensionDistance / 2.0)))
transition.animatePositionAdditive(layer: componentView.layer, offset: CGPoint(x: 0.0, y: -(46.0 + 54.0 - 4.0) + floorToScreenPixels(self.animateFromExtensionDistance / 2.0)))
}
}
}

View File

@ -68,13 +68,13 @@ public final class EntityKeyboardGifContent: Equatable {
public final class ChatEntityKeyboardInputNode: ChatInputNode {
public struct InputData: Equatable {
public var emoji: EmojiPagerContentComponent
public var emoji: EmojiPagerContentComponent?
public var stickers: EmojiPagerContentComponent?
public var gifs: EntityKeyboardGifContent?
public var availableGifSearchEmojies: [EntityKeyboardComponent.GifSearchEmoji]
public init(
emoji: EmojiPagerContentComponent,
emoji: EmojiPagerContentComponent?,
stickers: EmojiPagerContentComponent?,
gifs: EntityKeyboardGifContent?,
availableGifSearchEmojies: [EntityKeyboardComponent.GifSearchEmoji]
@ -159,6 +159,8 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
loadMore: { _ in
},
openSearch: {
},
updateSearchQuery: { _ in
}
)
@ -173,6 +175,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
isLoading: false,
loadMoreToken: nil,
displaySearchWithPlaceholder: nil,
searchCategories: nil,
searchInitiallyHidden: true
)
))
@ -243,6 +246,9 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
private let emojiSearchDisposable = MetaDisposable()
private let emojiSearchResult = Promise<(groups: [EmojiPagerContentComponent.ItemGroup], id: AnyHashable)?>(nil)
private let stickerSearchDisposable = MetaDisposable()
private let stickerSearchResult = Promise<(groups: [EmojiPagerContentComponent.ItemGroup], id: AnyHashable)?>(nil)
private let controllerInteraction: ChatControllerInteraction?
private var inputNodeInteraction: ChatMediaInputNodeInteraction?
@ -320,12 +326,17 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
return !savedGifs.isEmpty
}
let searchCategories: Signal<EmojiSearchCategories?, NoError> = context.engine.stickers.emojiSearchCategories(kind: .emoji)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let gifItems: Signal<EntityKeyboardGifContent, NoError>
switch subject {
case .recent:
gifItems = context.engine.data.subscribe(TelegramEngine.EngineData.Item.OrderedLists.ListItems(collectionId: Namespaces.OrderedItemList.CloudRecentGifs))
|> map { savedGifs -> EntityKeyboardGifContent in
gifItems = combineLatest(
context.engine.data.subscribe(TelegramEngine.EngineData.Item.OrderedLists.ListItems(collectionId: Namespaces.OrderedItemList.CloudRecentGifs)),
searchCategories
)
|> map { savedGifs, searchCategories -> EntityKeyboardGifContent in
var items: [GifPagerContentComponent.Item] = []
for gifItem in savedGifs {
items.append(GifPagerContentComponent.Item(
@ -342,14 +353,15 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
items: items,
isLoading: false,
loadMoreToken: nil,
displaySearchWithPlaceholder: presentationData.strings.GifSearch_SearchGifPlaceholder,
displaySearchWithPlaceholder: presentationData.strings.Common_Search,
searchCategories: searchCategories,
searchInitiallyHidden: true
)
)
}
case .trending:
gifItems = combineLatest(hasRecentGifs, trendingGifs)
|> map { hasRecentGifs, trendingGifs -> EntityKeyboardGifContent in
gifItems = combineLatest(hasRecentGifs, trendingGifs, searchCategories)
|> map { hasRecentGifs, trendingGifs, searchCategories -> EntityKeyboardGifContent in
var items: [GifPagerContentComponent.Item] = []
var isLoading = false
@ -373,14 +385,19 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
items: items,
isLoading: isLoading,
loadMoreToken: nil,
displaySearchWithPlaceholder: nil,
displaySearchWithPlaceholder: presentationData.strings.Common_Search,
searchCategories: searchCategories,
searchInitiallyHidden: true
)
)
}
case let .emojiSearch(query):
gifItems = combineLatest(hasRecentGifs, paneGifSearchForQuery(context: context, query: query, offset: nil, incompleteResults: true, staleCachedResults: true, delayRequest: false, updateActivity: nil))
|> map { hasRecentGifs, result -> EntityKeyboardGifContent in
gifItems = combineLatest(
hasRecentGifs,
paneGifSearchForQuery(context: context, query: query, offset: nil, incompleteResults: true, staleCachedResults: true, delayRequest: false, updateActivity: nil),
searchCategories
)
|> map { hasRecentGifs, result, searchCategories -> EntityKeyboardGifContent in
var items: [GifPagerContentComponent.Item] = []
var loadMoreToken: String?
@ -406,7 +423,8 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
items: items,
isLoading: isLoading,
loadMoreToken: loadMoreToken,
displaySearchWithPlaceholder: nil,
displaySearchWithPlaceholder: presentationData.strings.Common_Search,
searchCategories: searchCategories,
searchInitiallyHidden: true
)
)
@ -448,9 +466,12 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
return !savedGifs.isEmpty
}
let searchCategories: Signal<EmojiSearchCategories?, NoError> = context.engine.stickers.emojiSearchCategories(kind: .emoji)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let gifItems: Signal<EntityKeyboardGifContent, NoError>
gifItems = combineLatest(hasRecentGifs, paneGifSearchForQuery(context: context, query: query, offset: token, incompleteResults: true, staleCachedResults: true, delayRequest: false, updateActivity: nil))
|> map { hasRecentGifs, result -> EntityKeyboardGifContent in
gifItems = combineLatest(hasRecentGifs, paneGifSearchForQuery(context: context, query: query, offset: token, incompleteResults: true, staleCachedResults: true, delayRequest: false, updateActivity: nil), searchCategories)
|> map { hasRecentGifs, result, searchCategories -> EntityKeyboardGifContent in
var items: [GifPagerContentComponent.Item] = []
var existingIds = Set<MediaId>()
for item in componentValue.component.items {
@ -487,7 +508,8 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
items: items,
isLoading: isLoading,
loadMoreToken: loadMoreToken,
displaySearchWithPlaceholder: nil,
displaySearchWithPlaceholder: presentationData.strings.Common_Search,
searchCategories: searchCategories,
searchInitiallyHidden: true
)
)
@ -1002,7 +1024,6 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
}
strongSelf.emojiSearchDisposable.set((resultSignal
|> delay(0.15, queue: .mainQueue())
|> deliverOnMainQueue).start(next: { [weak self] result in
guard let strongSelf = self else {
return
@ -1214,7 +1235,66 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
},
requestUpdate: { _ in
},
updateSearchQuery: { _ in
updateSearchQuery: { [weak self] query in
guard let strongSelf = self else {
return
}
switch query {
case .none:
strongSelf.stickerSearchDisposable.set(nil)
strongSelf.stickerSearchResult.set(.single(nil))
case .text:
strongSelf.stickerSearchDisposable.set(nil)
strongSelf.stickerSearchResult.set(.single(nil))
case let .category(value):
let resultSignal = strongSelf.context.engine.stickers.searchStickers(query: value, scope: [.installed, .remote])
|> mapToSignal { files -> Signal<[EmojiPagerContentComponent.ItemGroup], NoError> in
var items: [EmojiPagerContentComponent.Item] = []
var existingIds = Set<MediaId>()
for item in files {
let itemFile = item.file
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,
tintMode: animationData.isTemplate ? .primary : .none
)
items.append(item)
}
return .single([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.stickerSearchDisposable.set((resultSignal
|> deliverOnMainQueue).start(next: { [weak self] result in
guard let strongSelf = self else {
return
}
strongSelf.stickerSearchResult.set(.single((result, AnyHashable(value))))
}))
}
},
updateScrollingToItemGroup: {
},
@ -1230,9 +1310,10 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
self.inputDataDisposable = (combineLatest(queue: .mainQueue(),
updatedInputData,
self.gifComponent.get(),
self.emojiSearchResult.get()
self.emojiSearchResult.get(),
self.stickerSearchResult.get()
)
|> deliverOnMainQueue).start(next: { [weak self] inputData, gifs, emojiSearchResult in
|> deliverOnMainQueue).start(next: { [weak self] inputData, gifs, emojiSearchResult, stickerSearchResult in
guard let strongSelf = self else {
return
}
@ -1249,7 +1330,23 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
iconFile: nil
)
}
inputData.emoji = inputData.emoji.withUpdatedItemGroups(panelItemGroups: inputData.emoji.panelItemGroups, contentItemGroups: emojiSearchResult.groups, itemContentUniqueId: emojiSearchResult.id, emptySearchResults: emptySearchResults)
if let emoji = inputData.emoji {
inputData.emoji = emoji.withUpdatedItemGroups(panelItemGroups: emoji.panelItemGroups, contentItemGroups: emojiSearchResult.groups, itemContentUniqueId: emojiSearchResult.id, emptySearchResults: emptySearchResults)
}
}
if let stickerSearchResult = stickerSearchResult {
var stickerSearchResults: EmojiPagerContentComponent.EmptySearchResults?
if !stickerSearchResult.groups.contains(where: { !$0.items.isEmpty }) {
//TODO:localize
stickerSearchResults = EmojiPagerContentComponent.EmptySearchResults(
text: "No stickers found",
iconFile: nil
)
}
if let stickers = inputData.stickers {
inputData.stickers = stickers.withUpdatedItemGroups(panelItemGroups: stickers.panelItemGroups, contentItemGroups: stickerSearchResult.groups, itemContentUniqueId: stickerSearchResult.id, emptySearchResults: stickerSearchResults)
}
}
var transition: Transition = .immediate
@ -1338,6 +1435,16 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
if let strongSelf = self, let pagerView = strongSelf.entityKeyboardView.componentView as? EntityKeyboardComponent.View {
pagerView.openSearch()
}
},
updateSearchQuery: { [weak self] query in
guard let self else {
return
}
if let query {
self.gifMode = .emojiSearch(query)
} else {
self.gifMode = .recent
}
}
)
@ -1377,6 +1484,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
self.inputDataDisposable?.dispose()
self.hasRecentGifsDisposable?.dispose()
self.emojiSearchDisposable.dispose()
self.stickerSearchDisposable.dispose()
}
private func reloadGifContext() {
@ -1439,27 +1547,38 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
mappedTransition = mappedTransition.withUserData(EntityKeyboardComponent.MarkInputCollapsed())
}
var emojiContent: EmojiPagerContentComponent? = self.currentInputData.emoji
var stickerContent: EmojiPagerContentComponent? = self.currentInputData.stickers
var gifContent: EntityKeyboardGifContent? = self.currentInputData.gifs
var stickersEnabled = true
var emojiEnabled = true
if let peer = interfaceState.renderedPeer?.peer as? TelegramChannel {
if peer.hasBannedPermission(.banSendStickers) != nil {
stickersEnabled = false
}
if peer.hasBannedPermission(.banSendText) != nil {
emojiEnabled = false
}
} else if let peer = interfaceState.renderedPeer?.peer as? TelegramGroup {
if peer.hasBannedPermission(.banSendStickers) {
stickersEnabled = false
}
if peer.hasBannedPermission(.banSendText) {
emojiEnabled = false
}
}
if !stickersEnabled || interfaceState.interfaceState.editMessage != nil {
stickerContent = nil
gifContent = nil
}
if !emojiEnabled && interfaceState.interfaceState.editMessage == nil {
emojiContent = nil
}
stickerContent?.inputInteractionHolder.inputInteraction = self.stickerInputInteraction
self.currentInputData.emoji.inputInteractionHolder.inputInteraction = self.emojiInputInteraction
self.currentInputData.emoji?.inputInteractionHolder.inputInteraction = self.emojiInputInteraction
let startTime = CFAbsoluteTimeGetCurrent()
@ -1471,7 +1590,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
isContentInFocus: isVisible,
containerInsets: UIEdgeInsets(top: self.isEmojiSearchActive ? -34.0 : 0.0, left: leftInset, bottom: bottomInset, right: rightInset),
topPanelInsets: UIEdgeInsets(),
emojiContent: self.currentInputData.emoji,
emojiContent: emojiContent,
stickerContent: stickerContent,
maskContent: nil,
gifContent: gifContent?.component,
@ -1639,7 +1758,9 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
private func processInputData(inputData: InputData) -> InputData {
return InputData(
emoji: inputData.emoji.withUpdatedItemGroups(panelItemGroups: self.processStableItemGroupList(category: .emoji, itemGroups: inputData.emoji.panelItemGroups), contentItemGroups: self.processStableItemGroupList(category: .emoji, itemGroups: inputData.emoji.contentItemGroups), itemContentUniqueId: inputData.emoji.itemContentUniqueId, emptySearchResults: inputData.emoji.emptySearchResults),
emoji: inputData.emoji.flatMap { emoji in
return emoji.withUpdatedItemGroups(panelItemGroups: self.processStableItemGroupList(category: .emoji, itemGroups: emoji.panelItemGroups), contentItemGroups: self.processStableItemGroupList(category: .emoji, itemGroups: emoji.contentItemGroups), itemContentUniqueId: emoji.itemContentUniqueId, emptySearchResults: emoji.emptySearchResults)
},
stickers: inputData.stickers.flatMap { stickers in
return stickers.withUpdatedItemGroups(panelItemGroups: self.processStableItemGroupList(category: .stickers, itemGroups: stickers.panelItemGroups), contentItemGroups: self.processStableItemGroupList(category: .stickers, itemGroups: stickers.contentItemGroups), itemContentUniqueId: nil, emptySearchResults: nil)
},

View File

@ -1569,7 +1569,7 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
return PassthroughLayer.self
}
private let activated: () -> Void
private let activated: (Bool) -> Void
private let deactivated: (Bool) -> Void
private let updateQuery: (EmojiPagerContentComponent.SearchQuery?) -> Void
@ -1606,7 +1606,7 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
return self.textField != nil
}
init(activated: @escaping () -> Void, deactivated: @escaping (Bool) -> Void, updateQuery: @escaping (EmojiPagerContentComponent.SearchQuery?) -> Void) {
init(activated: @escaping (Bool) -> Void, deactivated: @escaping (Bool) -> Void, updateQuery: @escaping (EmojiPagerContentComponent.SearchQuery?) -> Void) {
self.activated = activated
self.deactivated = deactivated
self.updateQuery = updateQuery
@ -1735,7 +1735,7 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
placeholderContentView.clearSelection(dispatchEvent: false)
}
self.activated()
self.activated(true)
self.textField?.becomeFirstResponder()
}
@ -1965,7 +1965,7 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
if let term {
self.updateQuery(.category(value: term))
self.activated()
self.activated(false)
} else {
self.deactivated(self.textField?.isFirstResponder ?? false)
self.updateQuery(nil)
@ -6175,7 +6175,7 @@ public final class EmojiPagerContentComponent: Component {
let scrollSize = CGSize(width: availableSize.width, height: availableSize.height)
transition.setPosition(view: self.scrollView, position: CGPoint(x: 0.0, y: scrollOriginY))
let clippingTopInset: CGFloat = itemLayout.searchInsets.top + itemLayout.searchHeight + 2.0
let clippingTopInset: CGFloat = itemLayout.searchInsets.top + itemLayout.searchHeight - 1.0
transition.setFrame(view: self.scrollViewClippingView, frame: CGRect(origin: CGPoint(x: 0.0, y: self.isSearchActivated ? clippingTopInset : 0.0), size: availableSize))
transition.setBounds(view: self.scrollViewClippingView, bounds: CGRect(origin: CGPoint(x: 0.0, y: self.isSearchActivated ? clippingTopInset : 0.0), size: availableSize))
@ -6364,12 +6364,12 @@ public final class EmojiPagerContentComponent: Component {
}*/
}
} else {
visibleSearchHeader = EmojiSearchHeaderView(activated: { [weak self] in
visibleSearchHeader = EmojiSearchHeaderView(activated: { [weak self] isTextInput in
guard let strongSelf = self, let visibleSearchHeader = strongSelf.visibleSearchHeader else {
return
}
if let component = strongSelf.component, component.searchIsPlaceholderOnly {
if let component = strongSelf.component, component.searchIsPlaceholderOnly, isTextInput {
component.inputInteractionHolder.inputInteraction?.openSearch()
} else {
strongSelf.isSearchActivated = true
@ -7435,15 +7435,18 @@ public final class EmojiPagerContentComponent: Component {
let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings
let searchCategories: Signal<EmojiSearchCategories?, NoError> = context.engine.stickers.emojiSearchCategories(kind: .emoji)
return combineLatest(
context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: stickerOrderedItemListCollectionIds, namespaces: stickerNamespaces, aroundIndex: nil, count: 10000000),
hasPremium(context: context, chatPeerId: chatPeerId, premiumIfSavedMessages: false),
hasTrending ? context.account.viewTracker.featuredStickerPacks() : .single([]),
context.engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: Namespaces.CachedItemCollection.featuredStickersConfiguration, id: ValueBoxKey(length: 0))),
ApplicationSpecificNotice.dismissedTrendingStickerPacks(accountManager: context.sharedContext.accountManager),
peerSpecificPack
peerSpecificPack,
searchCategories
)
|> map { view, hasPremium, featuredStickerPacks, featuredStickersConfiguration, dismissedTrendingStickerPacks, peerSpecificPack -> EmojiPagerContentComponent in
|> map { view, hasPremium, featuredStickerPacks, featuredStickersConfiguration, dismissedTrendingStickerPacks, peerSpecificPack, searchCategories -> EmojiPagerContentComponent in
let actuallyHasPremium = hasPremium
let hasPremium = forceHasPremium || hasPremium
struct ItemGroup {
@ -7880,7 +7883,7 @@ public final class EmojiPagerContentComponent: Component {
itemContentUniqueId: nil,
warpContentsOnEdges: false,
displaySearchWithPlaceholder: hasSearch ? strings.StickersSearch_SearchStickersPlaceholder : nil,
searchCategories: nil,
searchCategories: searchCategories,
searchInitiallyHidden: true,
searchIsPlaceholderOnly: searchIsPlaceholderOnly,
emptySearchResults: nil,

View File

@ -108,6 +108,7 @@ final class EmojiSearchSearchBarComponent: Component {
let contentSize: CGSize
let leftInset: CGFloat
let rightInset: CGFloat
let itemStartX: CGFloat
let textSpacing: CGFloat
let textFrame: CGRect
@ -123,11 +124,13 @@ final class EmojiSearchSearchBarComponent: Component {
self.textFrame = CGRect(origin: CGPoint(x: self.leftInset, y: floor((containerSize.height - textSize.height) * 0.5)), size: textSize)
self.contentSize = CGSize(width: self.leftInset + textSize.width + self.textSpacing + self.itemSize.width * CGFloat(self.itemCount) + self.itemSpacing * CGFloat(max(0, self.itemCount - 1)) + self.rightInset, height: containerSize.height)
self.itemStartX = max(self.textFrame.maxX + self.textSpacing, self.leftInset + floor(((containerSize.width - self.leftInset - self.rightInset) / 2.0) - self.itemSize.width))
self.contentSize = CGSize(width: self.itemStartX + self.itemSize.width * CGFloat(self.itemCount) + self.itemSpacing * CGFloat(max(0, self.itemCount - 1)) + self.rightInset, height: containerSize.height)
}
func visibleItems(for rect: CGRect) -> Range<Int>? {
let baseItemX: CGFloat = self.textFrame.maxX + self.textSpacing
let baseItemX: CGFloat = self.itemStartX
let offsetRect = rect.offsetBy(dx: -baseItemX, dy: 0.0)
var minVisibleIndex = Int(floor((offsetRect.minX - self.itemSpacing) / (self.itemSize.width + self.itemSpacing)))
minVisibleIndex = max(0, minVisibleIndex)
@ -142,7 +145,7 @@ final class EmojiSearchSearchBarComponent: Component {
}
func frame(at index: Int) -> CGRect {
return CGRect(origin: CGPoint(x: self.textFrame.maxX + self.textSpacing + CGFloat(index) * (self.itemSize.width + self.itemSpacing), y: floor((self.containerSize.height - self.itemSize.height) * 0.5)), size: self.itemSize)
return CGRect(origin: CGPoint(x: self.itemStartX + CGFloat(index) * (self.itemSize.width + self.itemSpacing), y: floor((self.containerSize.height - self.itemSize.height) * 0.5)), size: self.itemSize)
}
}
@ -184,8 +187,11 @@ final class EmojiSearchSearchBarComponent: Component {
private let scrollView: ContentScrollView
private let tintScrollView: UIView
private let tintTextView = ComponentView<Empty>()
private let textView = ComponentView<Empty>()
private let textContainerView: UIView
private let tintTextView = ComponentView<Empty>()
private let tintTextContainerView: UIView
private var visibleItemViews: [AnyHashable: ItemView] = [:]
private let selectedItemBackground: SimpleLayer
@ -209,6 +215,11 @@ final class EmojiSearchSearchBarComponent: Component {
self.tintScrollView.clipsToBounds = true
self.scrollView = ContentScrollView(mirrorView: self.tintScrollView)
self.textContainerView = UIView()
self.textContainerView.isUserInteractionEnabled = false
self.tintTextContainerView = UIView()
self.tintTextContainerView.isUserInteractionEnabled = false
self.roundMaskView = RoundMaskView()
self.tintRoundMaskView = RoundMaskView()
@ -231,8 +242,10 @@ final class EmojiSearchSearchBarComponent: Component {
self.scrollView.scrollsToTop = false
self.addSubview(self.scrollView)
self.addSubview(self.textContainerView)
self.tintContainerView.addSubview(self.tintScrollView)
self.tintContainerView.addSubview(self.tintTextContainerView)
self.mask = self.roundMaskView
self.tintContainerView.mask = self.tintRoundMaskView
@ -253,7 +266,7 @@ final class EmojiSearchSearchBarComponent: Component {
return
}
let location = recognizer.location(in: self.scrollView)
if location.x <= itemLayout.textFrame.maxX + itemLayout.textSpacing {
if (component.categories?.groups ?? []).isEmpty || location.x <= itemLayout.itemStartX - itemLayout.textSpacing {
component.activateTextInput()
} else {
for (id, itemView) in self.visibleItemViews {
@ -265,8 +278,10 @@ final class EmojiSearchSearchBarComponent: Component {
}
self.state?.updated(transition: .immediate)
if let categories = component.categories, let group = categories.groups.first(where: { $0.id == itemId }) {
if let _ = self.selectedItem, let categories = component.categories, let group = categories.groups.first(where: { $0.id == itemId }) {
component.searchTermUpdated(group.identifiers.joined(separator: ""))
} else {
component.searchTermUpdated(nil)
}
break
@ -418,6 +433,15 @@ final class EmojiSearchSearchBarComponent: Component {
self.selectedItemBackground.isHidden = true
self.selectedItemTintBackground.isHidden = true
}
let scrollBounds = self.scrollView.bounds
let textOffset = max(0.0, scrollBounds.minX - (itemLayout.itemStartX - itemLayout.textFrame.maxX - itemLayout.textSpacing))
transition.setPosition(view: self.textContainerView, position: self.scrollView.center)
transition.setBounds(view: self.textContainerView, bounds: CGRect(origin: CGPoint(x: textOffset, y: 0.0), size: scrollBounds.size))
transition.setPosition(view: self.tintTextContainerView, position: self.scrollView.center)
transition.setBounds(view: self.tintTextContainerView, bounds: CGRect(origin: CGPoint(x: textOffset, y: 0.0), size: scrollBounds.size))
}
func update(component: EmojiSearchSearchBarComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
@ -450,13 +474,13 @@ final class EmojiSearchSearchBarComponent: Component {
if let textComponentView = self.textView.view {
if textComponentView.superview == nil {
self.scrollView.addSubview(textComponentView)
self.textContainerView.addSubview(textComponentView)
}
transition.setFrame(view: textComponentView, frame: itemLayout.textFrame)
}
if let tintTextComponentView = self.tintTextView.view {
if tintTextComponentView.superview == nil {
self.tintScrollView.addSubview(tintTextComponentView)
self.tintTextContainerView.addSubview(tintTextComponentView)
}
transition.setFrame(view: tintTextComponentView, frame: itemLayout.textFrame)
}
@ -465,6 +489,9 @@ final class EmojiSearchSearchBarComponent: Component {
if self.scrollView.bounds.size != availableSize {
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize))
}
if case .active = component.textInputState {
transition.setBounds(view: self.scrollView, bounds: CGRect(origin: CGPoint(), size: availableSize))
}
if self.scrollView.contentSize != itemLayout.contentSize {
self.scrollView.contentSize = itemLayout.contentSize
}
@ -483,6 +510,10 @@ final class EmojiSearchSearchBarComponent: Component {
self.isUserInteractionEnabled = false
self.textView.view?.isHidden = hasText
self.tintTextView.view?.isHidden = hasText
/*if self.scrollView.contentOffset.x != 0.0 {
self.scrollView.setContentOffset(CGPoint(), animated: true)
}*/
case .inactive:
self.isUserInteractionEnabled = true
self.textView.view?.isHidden = false

View File

@ -368,7 +368,8 @@ public final class EntityKeyboardComponent: Component {
strongSelf.reorderPacks(category: .masks, items: items)
}
))))
contentIcons.append(PagerComponentContentIcon(id: "masks", imageName: "Chat/Input/Media/EntityInputMasksIcon"))
//TODO:localize
contentIcons.append(PagerComponentContentIcon(id: "masks", imageName: "Chat/Input/Media/EntityInputMasksIcon", title: "Masks"))
if let _ = component.maskContent?.inputInteractionHolder.inputInteraction?.openStickerSettings {
contentAccessoryRightButtons.append(AnyComponentWithIdentity(id: "masks", component: AnyComponent(Button(
content: AnyComponent(BundleIconComponent(
@ -385,7 +386,7 @@ public final class EntityKeyboardComponent: Component {
if let gifContent = component.gifContent {
contents.append(AnyComponentWithIdentity(id: "gifs", component: AnyComponent(gifContent)))
var topGifItems: [EntityKeyboardTopPanelComponent.Item] = []
/*var topGifItems: [EntityKeyboardTopPanelComponent.Item] = []
if component.hasRecentGifs {
topGifItems.append(EntityKeyboardTopPanelComponent.Item(
id: "recent",
@ -453,8 +454,9 @@ public final class EntityKeyboardComponent: Component {
activeContentItemIdUpdated: gifsContentItemIdUpdated,
reorderItems: { _ in
}
))))
contentIcons.append(PagerComponentContentIcon(id: "gifs", imageName: "Chat/Input/Media/EntityInputGifsIcon"))
))))*/
//TODO:localize
contentIcons.append(PagerComponentContentIcon(id: "gifs", imageName: "Chat/Input/Media/EntityInputGifsIcon", title: "GIFs"))
}
if let stickerContent = component.stickerContent {
@ -560,7 +562,8 @@ public final class EntityKeyboardComponent: Component {
strongSelf.reorderPacks(category: .stickers, items: items)
}
))))
contentIcons.append(PagerComponentContentIcon(id: "stickers", imageName: "Chat/Input/Media/EntityInputStickersIcon"))
//TODO:localize
contentIcons.append(PagerComponentContentIcon(id: "stickers", imageName: "Chat/Input/Media/EntityInputStickersIcon", title: "Stickers"))
if let _ = component.stickerContent?.inputInteractionHolder.inputInteraction?.openStickerSettings {
contentAccessoryRightButtons.append(AnyComponentWithIdentity(id: "stickers", component: AnyComponent(Button(
content: AnyComponent(BundleIconComponent(
@ -659,7 +662,8 @@ public final class EntityKeyboardComponent: Component {
strongSelf.reorderPacks(category: .emoji, items: items)
}
))))
contentIcons.append(PagerComponentContentIcon(id: "emoji", imageName: "Chat/Input/Media/EntityInputEmojiIcon"))
//TODO:localize
contentIcons.append(PagerComponentContentIcon(id: "emoji", imageName: "Chat/Input/Media/EntityInputEmojiIcon", title: "Emoji"))
if let _ = deleteBackwards {
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "emoji", component: AnyComponent(Button(
content: AnyComponent(BundleIconComponent(

View File

@ -10,25 +10,25 @@ import ComponentDisplayAdapters
import BundleIconComponent
private final class BottomPanelIconComponent: Component {
let imageName: String
let title: String
let isHighlighted: Bool
let theme: PresentationTheme
let action: () -> Void
init(
imageName: String,
title: String,
isHighlighted: Bool,
theme: PresentationTheme,
action: @escaping () -> Void
) {
self.imageName = imageName
self.title = title
self.isHighlighted = isHighlighted
self.theme = theme
self.action = action
}
static func ==(lhs: BottomPanelIconComponent, rhs: BottomPanelIconComponent) -> Bool {
if lhs.imageName != rhs.imageName {
if lhs.title != rhs.title {
return false
}
if lhs.isHighlighted != rhs.isHighlighted {
@ -67,13 +67,23 @@ private final class BottomPanelIconComponent: Component {
}
func update(component: BottomPanelIconComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
if self.component?.imageName != component.imageName {
self.contentView.image = UIImage(bundleImageName: component.imageName)
if self.component?.title != component.title {
let text = NSAttributedString(string: component.title, font: Font.medium(15.0), textColor: .white)
let textBounds = text.boundingRect(with: CGSize(width: 120.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil)
self.contentView.image = generateImage(CGSize(width: ceil(textBounds.width), height: ceil(textBounds.height)), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
UIGraphicsPushContext(context)
text.draw(in: textBounds)
UIGraphicsPopContext()
})?.withRenderingMode(.alwaysTemplate)
}
self.component = component
let size = CGSize(width: 28.0, height: 28.0)
let textInset: CGFloat = 18.0
let textSize = self.contentView.image?.size ?? CGSize()
let size = CGSize(width: textSize.width + textInset * 2.0, height: 28.0)
let color = component.isHighlighted ? component.theme.chat.inputMediaPanel.panelHighlightedIconColor : component.theme.chat.inputMediaPanel.panelIconColor
@ -87,8 +97,7 @@ private final class BottomPanelIconComponent: Component {
}
}
let contentSize = self.contentView.image?.size ?? size
transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(x: floor((size.width - contentSize.width) / 2.0), y: (size.height - contentSize.height) / 2.0), size: contentSize))
transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: (size.height - textSize.height) / 2.0 - 1.0), size: textSize))
return size
}
@ -304,7 +313,7 @@ final class EntityKeyboardBottomPanelComponent: Component {
var iconInfos: [AnyHashable: (size: CGSize, transition: Transition)] = [:]
var iconTotalSize = CGSize()
let iconSpacing: CGFloat = 22.0
let iconSpacing: CGFloat = 4.0
let navigateToContentId = panelEnvironment.navigateToContentId
@ -326,7 +335,7 @@ final class EntityKeyboardBottomPanelComponent: Component {
let iconSize = iconView.update(
transition: iconTransition,
component: AnyComponent(BottomPanelIconComponent(
imageName: icon.imageName,
title: icon.title,
isHighlighted: icon.id == activeContentId,
theme: component.theme,
action: {
@ -365,12 +374,7 @@ final class EntityKeyboardBottomPanelComponent: Component {
self.highlightedIconBackgroundView.isHidden = false
transition.setFrame(view: self.highlightedIconBackgroundView, frame: iconFrame)
let cornerRadius: CGFloat
if icon.id == AnyHashable("emoji") {
cornerRadius = min(iconFrame.width, iconFrame.height) / 2.0
} else {
cornerRadius = 10.0
}
let cornerRadius: CGFloat = min(iconFrame.width, iconFrame.height) / 2.0
transition.setCornerRadius(layer: self.highlightedIconBackgroundView.layer, cornerRadius: cornerRadius)
}

View File

@ -139,17 +139,20 @@ public final class GifPagerContentComponent: Component {
public let openGifContextMenu: (Item, UIView, CGRect, ContextGesture, Bool) -> Void
public let loadMore: (String) -> Void
public let openSearch: () -> Void
public let updateSearchQuery: (String?) -> Void
public init(
performItemAction: @escaping (Item, UIView, CGRect) -> Void,
openGifContextMenu: @escaping (Item, UIView, CGRect, ContextGesture, Bool) -> Void,
loadMore: @escaping (String) -> Void,
openSearch: @escaping () -> Void
openSearch: @escaping () -> Void,
updateSearchQuery: @escaping (String?) -> Void
) {
self.performItemAction = performItemAction
self.openGifContextMenu = openGifContextMenu
self.loadMore = loadMore
self.openSearch = openSearch
self.updateSearchQuery = updateSearchQuery
}
}
@ -184,6 +187,7 @@ public final class GifPagerContentComponent: Component {
public let isLoading: Bool
public let loadMoreToken: String?
public let displaySearchWithPlaceholder: String?
public let searchCategories: EmojiSearchCategories?
public let searchInitiallyHidden: Bool
public init(
@ -194,6 +198,7 @@ public final class GifPagerContentComponent: Component {
isLoading: Bool,
loadMoreToken: String?,
displaySearchWithPlaceholder: String?,
searchCategories: EmojiSearchCategories?,
searchInitiallyHidden: Bool
) {
self.context = context
@ -203,6 +208,7 @@ public final class GifPagerContentComponent: Component {
self.isLoading = isLoading
self.loadMoreToken = loadMoreToken
self.displaySearchWithPlaceholder = displaySearchWithPlaceholder
self.searchCategories = searchCategories
self.searchInitiallyHidden = searchInitiallyHidden
}
@ -228,6 +234,9 @@ public final class GifPagerContentComponent: Component {
if lhs.displaySearchWithPlaceholder != rhs.displaySearchWithPlaceholder {
return false
}
if lhs.searchCategories != rhs.searchCategories {
return false
}
if lhs.searchInitiallyHidden != rhs.searchInitiallyHidden {
return false
}
@ -307,7 +316,7 @@ public final class GifPagerContentComponent: Component {
func visibleItems(for rect: CGRect) -> Range<Int>? {
let offsetRect = rect.offsetBy(dx: -self.containerInsets.left, dy: -containerInsets.top)
var minVisibleRow = Int(floor((offsetRect.minY - self.verticalSpacing) / (self.itemSize + self.verticalSpacing)))
var minVisibleRow = Int(floor((offsetRect.minY - self.searchHeight - self.verticalSpacing) / (self.itemSize + self.verticalSpacing)))
minVisibleRow = max(0, minVisibleRow)
let maxVisibleRow = Int(ceil((offsetRect.maxY - self.verticalSpacing) / (self.itemSize + self.verticalSpacing)))
@ -430,6 +439,19 @@ public final class GifPagerContentComponent: Component {
}
}
private final class SearchHeaderContainer: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
var result: UIView?
for subview in self.subviews.reversed() {
if let value = subview.hitTest(self.convert(point, to: subview), with: event) {
result = value
break
}
}
return result
}
}
public final class ContentScrollLayer: CALayer {
public var mirrorLayer: CALayer?
@ -524,9 +546,12 @@ public final class GifPagerContentComponent: Component {
private var vibrancyEffectView: UIVisualEffectView?
private let mirrorContentScrollView: UIView
private let scrollView: ContentScrollView
private let scrollClippingView: UIView
private let placeholdersContainerView: UIView
private var visibleSearchHeader: EmojiSearchHeaderView?
private let searchHeaderContainer: SearchHeaderContainer
private let mirrorSearchHeaderContainer: UIView
private var visibleItemPlaceholderViews: [ItemKey: ItemPlaceholderView] = [:]
private var visibleItemLayers: [ItemKey: ItemLayer] = [:]
private var ignoreScrolling: Bool = false
@ -552,6 +577,14 @@ public final class GifPagerContentComponent: Component {
self.scrollView = ContentScrollView(mirrorView: self.mirrorContentScrollView)
self.scrollView.layer.anchorPoint = CGPoint()
self.searchHeaderContainer = SearchHeaderContainer()
self.searchHeaderContainer.layer.anchorPoint = CGPoint()
self.mirrorSearchHeaderContainer = UIView()
self.mirrorSearchHeaderContainer.layer.anchorPoint = CGPoint()
self.scrollClippingView = UIView()
self.scrollClippingView.clipsToBounds = true
super.init(frame: frame)
self.addSubview(self.backgroundView)
@ -570,9 +603,12 @@ public final class GifPagerContentComponent: Component {
self.scrollView.showsHorizontalScrollIndicator = false
self.scrollView.scrollsToTop = false
self.scrollView.delegate = self
self.addSubview(self.scrollView)
self.scrollClippingView.addSubview(self.scrollView)
self.addSubview(self.scrollClippingView)
self.scrollView.addSubview(self.placeholdersContainerView)
self.addSubview(self.searchHeaderContainer)
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
@ -665,7 +701,7 @@ public final class GifPagerContentComponent: Component {
return
}
self.updateVisibleItems(attemptSynchronousLoads: false)
self.updateVisibleItems(attemptSynchronousLoads: false, transition: .immediate, fromScrolling: true)
self.updateScrollingOffset(transition: .immediate)
@ -741,7 +777,7 @@ public final class GifPagerContentComponent: Component {
self.updateScrollingOffset(transition: transition)
}
private func updateVisibleItems(attemptSynchronousLoads: Bool) {
private func updateVisibleItems(attemptSynchronousLoads: Bool, transition: Transition, fromScrolling: Bool) {
guard let component = self.component, let itemLayout = self.itemLayout else {
return
}
@ -776,7 +812,7 @@ public final class GifPagerContentComponent: Component {
let itemFrame = itemLayout.frame(at: index).offsetBy(dx: 0.0, dy: searchInset)
let itemTransition: Transition = .immediate
var itemTransition: Transition = transition
var updateItemLayerPlaceholder = false
let itemLayer: ItemLayer
@ -784,6 +820,7 @@ public final class GifPagerContentComponent: Component {
itemLayer = current
} else {
updateItemLayerPlaceholder = true
itemTransition = .immediate
itemLayer = ItemLayer(
item: item,
@ -837,7 +874,10 @@ public final class GifPagerContentComponent: Component {
let itemPosition = CGPoint(x: itemFrame.midX, y: itemFrame.midY)
let itemBounds = CGRect(origin: CGPoint(), size: itemFrame.size)
itemTransition.setFrame(layer: itemLayer, frame: itemFrame)
//itemTransition.setFrame(layer: itemLayer, frame: itemFrame)
itemLayer.bounds = CGRect(origin: CGPoint(), size: itemFrame.size)
itemTransition.setPosition(layer: itemLayer, position: itemFrame.center)
itemLayer.isVisibleForAnimations = true
if let placeholderView = self.visibleItemPlaceholderViews[itemId] {
@ -871,6 +911,16 @@ public final class GifPagerContentComponent: Component {
for id in removedIds {
self.visibleItemLayers.removeValue(forKey: id)
}
transition.setPosition(view: self.searchHeaderContainer, position: self.scrollView.center)
var searchContainerBounds = self.scrollView.bounds
if case .emojiSearch = component.subject {
searchContainerBounds.origin.y = 0.0
}
transition.setBounds(view: self.searchHeaderContainer, bounds: searchContainerBounds)
transition.setPosition(view: self.mirrorSearchHeaderContainer, position: self.scrollView.center)
transition.setBounds(view: self.mirrorSearchHeaderContainer, bounds: searchContainerBounds)
}
private func updateShimmerIfNeeded() {
@ -900,6 +950,7 @@ public final class GifPagerContentComponent: Component {
self.vibrancyEffectView = vibrancyEffectView
self.backgroundView.addSubview(vibrancyEffectView)
vibrancyEffectView.contentView.addSubview(self.mirrorContentScrollView)
vibrancyEffectView.contentView.addSubview(self.mirrorSearchHeaderContainer)
}
}
self.backgroundView.updateColor(color: theme.chat.inputMediaPanel.backgroundColor, enableBlur: true, forceKeepBlur: false, transition: transition.containedViewLayoutTransition)
@ -965,30 +1016,45 @@ public final class GifPagerContentComponent: Component {
if let current = self.visibleSearchHeader {
visibleSearchHeader = current
} else {
visibleSearchHeader = EmojiSearchHeaderView(activated: { [weak self] in
visibleSearchHeader = EmojiSearchHeaderView(activated: { [weak self] isTextInput in
guard let strongSelf = self else {
return
}
strongSelf.component?.inputInteraction.openSearch()
if isTextInput {
strongSelf.component?.inputInteraction.openSearch()
}
}, deactivated: { _ in
}, updateQuery: { _ in
}, updateQuery: { [weak self] query in
guard let self, let component = self.component else {
return
}
switch query {
case .none:
component.inputInteraction.updateSearchQuery(nil)
case .text:
break
case let .category(value):
component.inputInteraction.updateSearchQuery(value)
}
})
self.visibleSearchHeader = visibleSearchHeader
self.scrollView.addSubview(visibleSearchHeader)
self.mirrorContentScrollView.addSubview(visibleSearchHeader.tintContainerView)
self.searchHeaderContainer.addSubview(visibleSearchHeader)
self.mirrorSearchHeaderContainer.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(context: component.context, theme: keyboardChildEnvironment.theme, strings: keyboardChildEnvironment.strings, text: displaySearchWithPlaceholder, useOpaqueTheme: false, isActive: false, size: searchHeaderFrame.size, canFocus: false, searchCategories: nil, transition: transition)
visibleSearchHeader.update(context: component.context, theme: keyboardChildEnvironment.theme, strings: keyboardChildEnvironment.strings, text: displaySearchWithPlaceholder, useOpaqueTheme: false, isActive: false, size: searchHeaderFrame.size, canFocus: false, searchCategories: component.searchCategories, transition: transition)
transition.setFrame(view: visibleSearchHeader, frame: searchHeaderFrame, completion: { [weak self] completed in
guard let strongSelf = self, completed, let visibleSearchHeader = strongSelf.visibleSearchHeader else {
let _ = self
let _ = completed
/*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)
}
strongSelf.mirrorSearchHeaderContainer.addSubview(visibleSearchHeader.tintContainerView)
}*/
})
} else {
if let visibleSearchHeader = self.visibleSearchHeader {
@ -998,7 +1064,15 @@ public final class GifPagerContentComponent: Component {
}
}
self.updateVisibleItems(attemptSynchronousLoads: true)
self.updateVisibleItems(attemptSynchronousLoads: true, transition: transition, fromScrolling: false)
var clippingInset: CGFloat = 0.0
if case .emojiSearch = component.subject {
clippingInset = itemLayout.searchInsets.top + itemLayout.searchHeight - 1.0
}
let clippingFrame = CGRect(origin: CGPoint(x: 0.0, y: clippingInset), size: CGSize(width: availableSize.width, height: availableSize.height - clippingInset))
transition.setPosition(view: self.scrollClippingView, position: clippingFrame.center)
transition.setBounds(view: self.scrollClippingView, bounds: clippingFrame)
return availableSize
}

View File

@ -468,7 +468,17 @@ final class DataUsageScreenComponent: Component {
transition.setBounds(view: self.headerOffsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: headerOffset), size: self.headerOffsetContainer.bounds.size))
if let controller = self.controller?(), let backButtonNode = controller.navigationBar?.backButtonNode {
animatedTransition.setAlpha(layer: backButtonNode.layer, alpha: navigationButtonAlpha)
if backButtonNode.isHidden {
backButtonNode.alpha = 0.0
backButtonNode.isHidden = false
}
animatedTransition.setAlpha(layer: backButtonNode.layer, alpha: navigationButtonAlpha, completion: { [weak backButtonNode] completed in
if let backButtonNode, completed {
if navigationButtonAlpha.isZero {
backButtonNode.isHidden = true
}
}
})
}
}
}