From 2c2ecfe1fc1a5f935ca8d23c05afd473eb2097eb Mon Sep 17 00:00:00 2001 From: Peter Iakovlev Date: Tue, 12 Feb 2019 16:38:23 +0400 Subject: [PATCH] Elevate trending stickers panel item when it contains unread items --- TelegramUI/ChatListController.swift | 2 +- TelegramUI/ChatMediaInputNode.swift | 26 +++++++++--- TelegramUI/ChatMediaInputPanelEntries.swift | 4 +- TelegramUI/ChatMediaInputTrendingItem.swift | 44 +++++++++++++++++---- TelegramUI/ChatMediaInputTrendingPane.swift | 3 ++ TelegramUI/MediaInputPaneTrendingItem.swift | 21 +++++++++- 6 files changed, 84 insertions(+), 16 deletions(-) diff --git a/TelegramUI/ChatListController.swift b/TelegramUI/ChatListController.swift index 5fc1f639f7..2246ec6041 100644 --- a/TelegramUI/ChatListController.swift +++ b/TelegramUI/ChatListController.swift @@ -665,7 +665,7 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie override public func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - #if DEBUG + #if false && DEBUG DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.0, execute: { [weak self] in guard let strongSelf = self else { return diff --git a/TelegramUI/ChatMediaInputNode.swift b/TelegramUI/ChatMediaInputNode.swift index ebd0e2b3ce..1378121a96 100644 --- a/TelegramUI/ChatMediaInputNode.swift +++ b/TelegramUI/ChatMediaInputNode.swift @@ -150,9 +150,12 @@ private func preparedChatMediaInputGridEntryTransition(account: Account, view: I return ChatMediaInputGridTransition(deletions: deletions, insertions: insertions, updates: updates, updateFirstIndexInSectionOffset: firstIndexInSectionOffset, stationaryItems: stationaryItems, scrollToItem: scrollToItem, updateOpaqueState: opaqueState, animated: animated) } -private func chatMediaInputPanelEntries(view: ItemCollectionsView, savedStickers: OrderedItemListView?, recentStickers: OrderedItemListView?, peerSpecificPack: PeerSpecificPackData?, canInstallPeerSpecificPack: CanInstallPeerSpecificPack, theme: PresentationTheme) -> [ChatMediaInputPanelEntry] { +private func chatMediaInputPanelEntries(view: ItemCollectionsView, savedStickers: OrderedItemListView?, recentStickers: OrderedItemListView?, peerSpecificPack: PeerSpecificPackData?, canInstallPeerSpecificPack: CanInstallPeerSpecificPack, hasUnreadTrending: Bool, theme: PresentationTheme) -> [ChatMediaInputPanelEntry] { var entries: [ChatMediaInputPanelEntry] = [] entries.append(.recentGifs(theme)) + if hasUnreadTrending { + entries.append(.trending(true, theme)) + } if let savedStickers = savedStickers, !savedStickers.items.isEmpty { entries.append(.savedStickers(theme)) } @@ -195,7 +198,9 @@ private func chatMediaInputPanelEntries(view: ItemCollectionsView, savedStickers entries.append(.peerSpecific(theme: theme, peer: peer)) } - entries.append(.trending(false, theme)) + if !hasUnreadTrending { + entries.append(.trending(false, theme)) + } entries.append(.settings(theme)) return entries } @@ -658,9 +663,20 @@ final class ChatMediaInputNode: ChatInputNode { peerSpecificPack = .single((nil, .none)) } + let hasUnreadTrending = context.account.viewTracker.featuredStickerPacks() + |> map { packs -> Bool in + for pack in packs { + if pack.unread { + return true + } + } + return false + } + |> distinctUntilChanged + let previousView = Atomic(value: nil) - let transitions = combineLatest(itemCollectionsView, peerSpecificPack, self.themeAndStringsPromise.get()) - |> map { viewAndUpdate, peerSpecificPack, themeAndStrings -> (ItemCollectionsView, ChatMediaInputPanelTransition, Bool, ChatMediaInputGridTransition, Bool) in + let transitions = combineLatest(itemCollectionsView, peerSpecificPack, hasUnreadTrending, self.themeAndStringsPromise.get()) + |> map { viewAndUpdate, peerSpecificPack, hasUnreadTrending, themeAndStrings -> (ItemCollectionsView, ChatMediaInputPanelTransition, Bool, ChatMediaInputGridTransition, Bool) in let (view, viewUpdate) = viewAndUpdate let previous = previousView.swap(view) var update = viewUpdate @@ -678,7 +694,7 @@ final class ChatMediaInputNode: ChatInputNode { savedStickers = orderedView } } - let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: peerSpecificPack.0, canInstallPeerSpecificPack: peerSpecificPack.1, theme: theme) + let panelEntries = chatMediaInputPanelEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: peerSpecificPack.0, canInstallPeerSpecificPack: peerSpecificPack.1, hasUnreadTrending: hasUnreadTrending, theme: theme) let gridEntries = chatMediaInputGridEntries(view: view, savedStickers: savedStickers, recentStickers: recentStickers, peerSpecificPack: peerSpecificPack.0, canInstallPeerSpecificPack: peerSpecificPack.1, strings: strings, theme: theme) let (previousPanelEntries, previousGridEntries) = previousEntries.swap((panelEntries, gridEntries)) return (view, preparedChatMediaInputPanelEntryTransition(account: context.account, from: previousPanelEntries, to: panelEntries, inputNodeInteraction: inputNodeInteraction), previousPanelEntries.isEmpty, preparedChatMediaInputGridEntryTransition(account: context.account, view: view, from: previousGridEntries, to: gridEntries, update: update, interfaceInteraction: controllerInteraction, inputNodeInteraction: inputNodeInteraction), previousGridEntries.isEmpty) diff --git a/TelegramUI/ChatMediaInputPanelEntries.swift b/TelegramUI/ChatMediaInputPanelEntries.swift index 4b8c6f5cdf..c9b318f51e 100644 --- a/TelegramUI/ChatMediaInputPanelEntries.swift +++ b/TelegramUI/ChatMediaInputPanelEntries.swift @@ -255,8 +255,8 @@ enum ChatMediaInputPanelEntry: Comparable, Identifiable { let collectionId = ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.recentStickers.rawValue, id: 0) inputNodeInteraction.navigateToCollectionId(collectionId) }) - case let .trending(_, theme): - return ChatMediaInputTrendingItem(inputNodeInteraction: inputNodeInteraction, theme: theme, selected: { + case let .trending(elevated, theme): + return ChatMediaInputTrendingItem(inputNodeInteraction: inputNodeInteraction, elevated: elevated, theme: theme, selected: { let collectionId = ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.trending.rawValue, id: 0) inputNodeInteraction.navigateToCollectionId(collectionId) }) diff --git a/TelegramUI/ChatMediaInputTrendingItem.swift b/TelegramUI/ChatMediaInputTrendingItem.swift index 70e1186c6e..0629d22518 100644 --- a/TelegramUI/ChatMediaInputTrendingItem.swift +++ b/TelegramUI/ChatMediaInputTrendingItem.swift @@ -8,14 +8,16 @@ import Postbox final class ChatMediaInputTrendingItem: ListViewItem { let inputNodeInteraction: ChatMediaInputNodeInteraction let selectedItem: () -> Void + let elevated: Bool let theme: PresentationTheme var selectable: Bool { return true } - init(inputNodeInteraction: ChatMediaInputNodeInteraction, theme: PresentationTheme, selected: @escaping () -> Void) { + init(inputNodeInteraction: ChatMediaInputNodeInteraction, elevated: Bool, theme: PresentationTheme, selected: @escaping () -> Void) { self.inputNodeInteraction = inputNodeInteraction + self.elevated = elevated self.selectedItem = selected self.theme = theme } @@ -26,7 +28,7 @@ final class ChatMediaInputTrendingItem: ListViewItem { node.contentSize = CGSize(width: 41.0, height: 41.0) node.insets = ChatMediaInputNode.setupPanelIconInsets(item: self, previousItem: previousItem, nextItem: nextItem) node.inputNodeInteraction = self.inputNodeInteraction - node.updateTheme(theme: self.theme) + node.updateTheme(elevated: self.elevated, theme: self.theme) node.updateIsHighlighted() node.updateAppearanceTransition(transition: .immediate) Queue.mainQueue().async { @@ -40,7 +42,7 @@ final class ChatMediaInputTrendingItem: ListViewItem { public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { Queue.mainQueue().async { completion(ListViewItemNodeLayout(contentSize: node().contentSize, insets: ChatMediaInputNode.setupPanelIconInsets(item: self, previousItem: previousItem, nextItem: nextItem)), { _ in - (node() as? ChatMediaInputTrendingItemNode)?.updateTheme(theme: self.theme) + (node() as? ChatMediaInputTrendingItemNode)?.updateTheme(elevated: self.elevated, theme: self.theme) }) } } @@ -62,8 +64,11 @@ final class ChatMediaInputTrendingItemNode: ListViewItemNode { var currentCollectionId: ItemCollectionId? var inputNodeInteraction: ChatMediaInputNodeInteraction? + var elevated: Bool = false var theme: PresentationTheme? + let badgeBackground: ASImageNode + init() { self.highlightNode = ASImageNode() self.highlightNode.isLayerBacked = true @@ -74,6 +79,11 @@ final class ChatMediaInputTrendingItemNode: ListViewItemNode { self.imageNode.contentMode = .center self.imageNode.contentsScale = UIScreenScale + self.badgeBackground = ASImageNode() + self.badgeBackground.displaysAsynchronously = false + self.badgeBackground.displayWithoutProcessing = true + self.badgeBackground.isHidden = true + self.highlightNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - highlightSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - highlightSize.height) / 2.0)), size: highlightSize) self.imageNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) @@ -82,22 +92,34 @@ final class ChatMediaInputTrendingItemNode: ListViewItemNode { self.addSubnode(self.highlightNode) self.addSubnode(self.imageNode) + self.addSubnode(self.badgeBackground) self.currentCollectionId = ItemCollectionId(namespace: ChatMediaInputPanelAuxiliaryNamespace.trending.rawValue, id: 0) - - let imageSize = CGSize(width: 26.0, height: 26.0) - self.imageNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0) + UIScreenPixel), size: imageSize) } deinit { } - func updateTheme(theme: PresentationTheme) { + func updateTheme(elevated: Bool, theme: PresentationTheme) { if self.theme !== theme { self.theme = theme self.highlightNode.image = PresentationResourcesChat.chatMediaInputPanelHighlightedIconImage(theme) self.imageNode.image = PresentationResourcesChat.chatInputMediaPanelTrendingIconImage(theme) + self.badgeBackground.image = generateFilledCircleImage(diameter: 16.0, color: theme.chat.inputPanel.mediaRecordingDotColor) + + let imageSize = CGSize(width: 26.0, height: 26.0) + let imageFrame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0) + UIScreenPixel), size: imageSize) + self.imageNode.frame = imageFrame + + if let image = self.badgeBackground.image { + self.badgeBackground.frame = CGRect(origin: CGPoint(x: imageFrame.maxX - image.size.width + 2.0, y: imageFrame.maxY - image.size.width + 3.0), size: image.size) + } + } + + if self.elevated != elevated { + self.elevated = elevated + self.badgeBackground.isHidden = !self.elevated } } @@ -112,5 +134,13 @@ final class ChatMediaInputTrendingItemNode: ListViewItemNode { transition.updateSublayerTransformScale(node: self, scale: inputNodeInteraction.appearanceTransition) } } + + override func animateAdded(_ currentTimestamp: Double, duration: Double) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + + override func animateRemoved(_ currentTimestamp: Double, duration: Double) { + self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) + } } diff --git a/TelegramUI/ChatMediaInputTrendingPane.swift b/TelegramUI/ChatMediaInputTrendingPane.swift index 10bd233bf2..49756082a7 100644 --- a/TelegramUI/ChatMediaInputTrendingPane.swift +++ b/TelegramUI/ChatMediaInputTrendingPane.swift @@ -59,6 +59,9 @@ private final class TrendingPaneEntry: Identifiable, Comparable { if lhs.installed != rhs.installed { return false } + if lhs.unread != rhs.unread { + return false + } return true } diff --git a/TelegramUI/MediaInputPaneTrendingItem.swift b/TelegramUI/MediaInputPaneTrendingItem.swift index b4d7c7a97a..958651dd68 100644 --- a/TelegramUI/MediaInputPaneTrendingItem.swift +++ b/TelegramUI/MediaInputPaneTrendingItem.swift @@ -99,6 +99,24 @@ class MediaInputPaneTrendingItemNode: ListViewItemNode { private var item: MediaInputPaneTrendingItem? private let preloadDisposable = MetaDisposable() + private let readDisposable = MetaDisposable() + + override var visibility: ListViewItemNodeVisibility { + didSet { + if self.visibility != oldValue { + if self.visibility == .visible { + if let item = self.item, item.unread { + self.readDisposable.set(( + markFeaturedStickerPacksAsSeenInteractively(postbox: item.account.postbox, ids: [item.info.id]) + |> delay(1.0, queue: .mainQueue()) + ).start()) + } + } else { + self.readDisposable.set(nil) + } + } + } + } init() { self.titleNode = TextNode() @@ -160,6 +178,7 @@ class MediaInputPaneTrendingItemNode: ListViewItemNode { deinit { self.preloadDisposable.dispose() + self.readDisposable.dispose() } override func didLoad() { @@ -236,7 +255,7 @@ class MediaInputPaneTrendingItemNode: ListViewItemNode { strongSelf.titleNode.frame = titleFrame strongSelf.descriptionNode.frame = CGRect(origin: CGPoint(x: params.leftInset + leftInset, y: 23.0), size: descriptionLayout.size) - if false && item.unread { + if item.unread { strongSelf.unreadNode.isHidden = false } else { strongSelf.unreadNode.isHidden = true