From 04d97739cdd32c03751b5dd2fab31ca087e6b09d Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 2 Mar 2023 16:24:52 +0400 Subject: [PATCH] UI improvements --- .../Sources/Node/ChatListItem.swift | 4 +- .../Sources/ChatMediaInputTrendingPane.swift | 16 +++--- .../Sources/FeaturedStickersScreen.swift | 30 +++++------ .../Sources/StickerPaneSearchGlobaltem.swift | 17 +++--- .../StickerPaneSearchStickerItem.swift | 34 ++++++------ .../Sources/ItemListRevealOptionsNode.swift | 23 +++++--- .../Sources/Items/ItemListEditableItem.swift | 8 +-- .../Sources/ManagedAnimationNode.swift | 7 +++ .../Sources/PasscodeEntryController.swift | 1 + .../Sources/PasscodeEntryControllerNode.swift | 53 ++++++++++++++++--- .../Form/FormEditableBlockItemNode.swift | 4 +- .../StickerPaneSearchContentNode.swift | 14 ++--- .../Sources/HashtagChatInputPanelItem.swift | 2 +- .../Sources/MentionChatInputPanelItem.swift | 2 +- 14 files changed, 138 insertions(+), 77 deletions(-) diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index e84d200bc5..d68e04cf0d 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -3371,9 +3371,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { strongSelf.updateLayout(size: CGSize(width: layout.contentSize.width, height: itemHeight), leftInset: params.leftInset, rightInset: params.rightInset) if item.editing { - strongSelf.setRevealOptions((left: [], right: [])) + strongSelf.setRevealOptions((left: [], right: []), enableAnimations: item.context.sharedContext.energyUsageSettings.fullTranslucency) } else { - strongSelf.setRevealOptions((left: peerLeftRevealOptions, right: peerRevealOptions)) + strongSelf.setRevealOptions((left: peerLeftRevealOptions, right: peerRevealOptions), enableAnimations: item.context.sharedContext.energyUsageSettings.fullTranslucency) } if !strongSelf.customAnimationInProgress { strongSelf.setRevealOptionsOpened(item.hasActiveRevealControls, animated: true) diff --git a/submodules/FeaturedStickersScreen/Sources/ChatMediaInputTrendingPane.swift b/submodules/FeaturedStickersScreen/Sources/ChatMediaInputTrendingPane.swift index 02e207b48a..9a784833c0 100644 --- a/submodules/FeaturedStickersScreen/Sources/ChatMediaInputTrendingPane.swift +++ b/submodules/FeaturedStickersScreen/Sources/ChatMediaInputTrendingPane.swift @@ -86,9 +86,9 @@ public final class TrendingPanePackEntry: Identifiable, Comparable { return lhs.index < rhs.index } - public func item(account: Account, interaction: TrendingPaneInteraction, grid: Bool) -> GridItem { + public func item(context: AccountContext, interaction: TrendingPaneInteraction, grid: Bool) -> GridItem { let info = self.info - return StickerPaneSearchGlobalItem(account: account, theme: self.theme, strings: self.strings, listAppearance: false, info: self.info, topItems: self.topItems, topSeparator: self.topSeparator, regularInsets: false, installed: self.installed, unread: self.unread, open: { + return StickerPaneSearchGlobalItem(context: context, theme: self.theme, strings: self.strings, listAppearance: false, info: self.info, topItems: self.topItems, topSeparator: self.topSeparator, regularInsets: false, installed: self.installed, unread: self.unread, open: { interaction.openPack(info) }, install: { interaction.installPack(info) @@ -147,14 +147,14 @@ private enum TrendingPaneEntry: Identifiable, Comparable { } } - func item(account: Account, interaction: TrendingPaneInteraction, grid: Bool) -> GridItem { + func item(context: AccountContext, interaction: TrendingPaneInteraction, grid: Bool) -> GridItem { switch self { case let .search(theme, strings): return PaneSearchBarPlaceholderItem(theme: theme, strings: strings, type: .stickers, activate: { interaction.openSearch() }) case let .pack(pack): - return pack.item(account: account, interaction: interaction, grid: grid) + return pack.item(context: context, interaction: interaction, grid: grid) } } } @@ -166,12 +166,12 @@ private struct TrendingPaneTransition { let initial: Bool } -private func preparedTransition(from fromEntries: [TrendingPaneEntry], to toEntries: [TrendingPaneEntry], account: Account, interaction: TrendingPaneInteraction, initial: Bool) -> TrendingPaneTransition { +private func preparedTransition(from fromEntries: [TrendingPaneEntry], to toEntries: [TrendingPaneEntry], context: AccountContext, interaction: TrendingPaneInteraction, initial: Bool) -> TrendingPaneTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices - let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(account: account, interaction: interaction, grid: false), previousIndex: $0.2) } - let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction, grid: false)) } + let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(context: context, interaction: interaction, grid: false), previousIndex: $0.2) } + let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, interaction: interaction, grid: false)) } return TrendingPaneTransition(deletions: deletions, insertions: insertions, updates: updates, initial: initial) } @@ -356,7 +356,7 @@ public final class ChatMediaInputTrendingPane: ChatMediaInputPane { let entries = trendingPaneEntries(trendingEntries: trendingEntries, installedPacks: installedPacks, theme: presentationData.theme, strings: presentationData.strings, isPane: isPane) let previous = previousEntries.swap(entries) - return preparedTransition(from: previous ?? [], to: entries, account: context.account, interaction: interaction, initial: previous == nil) + return preparedTransition(from: previous ?? [], to: entries, context: context, interaction: interaction, initial: previous == nil) } |> deliverOnMainQueue).start(next: { [weak self] transition in guard let strongSelf = self else { diff --git a/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift b/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift index 75fbb91720..5f431eaf7e 100644 --- a/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift +++ b/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift @@ -96,9 +96,9 @@ private final class FeaturedPackEntry: Identifiable, Comparable { return lhs.index < rhs.index } - func item(account: Account, interaction: FeaturedInteraction, isOther: Bool) -> GridItem { + func item(context: AccountContext, interaction: FeaturedInteraction, isOther: Bool) -> GridItem { let info = self.info - return StickerPaneSearchGlobalItem(account: account, theme: self.theme, strings: self.strings, listAppearance: true, fillsRow: false, info: self.info, topItems: self.topItems, topSeparator: self.topSeparator, regularInsets: self.regularInsets, installed: self.installed, unread: self.unread, open: { + return StickerPaneSearchGlobalItem(context: context, theme: self.theme, strings: self.strings, listAppearance: true, fillsRow: false, info: self.info, topItems: self.topItems, topSeparator: self.topSeparator, regularInsets: self.regularInsets, installed: self.installed, unread: self.unread, open: { interaction.openPack(info) }, install: { interaction.installPack(info, !self.installed) @@ -143,10 +143,10 @@ private enum FeaturedEntry: Identifiable, Comparable { } } - func item(account: Account, interaction: FeaturedInteraction) -> GridItem { + func item(context: AccountContext, interaction: FeaturedInteraction) -> GridItem { switch self { case let .pack(pack, isOther): - return pack.item(account: account, interaction: interaction, isOther: isOther) + return pack.item(context: context, interaction: interaction, isOther: isOther) } } } @@ -159,12 +159,12 @@ private struct FeaturedTransition { let scrollToItem: GridNodeScrollToItem? } -private func preparedTransition(from fromEntries: [FeaturedEntry], to toEntries: [FeaturedEntry], account: Account, interaction: FeaturedInteraction, initial: Bool, scrollToItem: GridNodeScrollToItem?) -> FeaturedTransition { +private func preparedTransition(from fromEntries: [FeaturedEntry], to toEntries: [FeaturedEntry], context: AccountContext, interaction: FeaturedInteraction, initial: Bool, scrollToItem: GridNodeScrollToItem?) -> FeaturedTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices - let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(account: account, interaction: interaction), previousIndex: $0.2) } - let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction)) } + let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(context: context, interaction: interaction), previousIndex: $0.2) } + let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, interaction: interaction)) } return FeaturedTransition(deletions: deletions, insertions: insertions, updates: updates, initial: initial, scrollToItem: scrollToItem) } @@ -401,7 +401,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode { } } - return preparedTransition(from: previous ?? [], to: entries, account: context.account, interaction: interaction, initial: initial, scrollToItem: scrollToItem) + return preparedTransition(from: previous ?? [], to: entries, context: context, interaction: interaction, initial: initial, scrollToItem: scrollToItem) } |> deliverOnMainQueue).start(next: { [weak self] transition in guard let strongSelf = self else { @@ -1034,14 +1034,14 @@ private enum FeaturedSearchEntry: Identifiable, Comparable { } } - func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, interaction: StickerPaneSearchInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction, itemContext: StickerPaneSearchGlobalItemContext) -> GridItem { + func item(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, interaction: StickerPaneSearchInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction, itemContext: StickerPaneSearchGlobalItemContext) -> GridItem { switch self { case let .sticker(_, code, stickerItem, theme): - return StickerPaneSearchStickerItem(account: account, code: code, stickerItem: stickerItem, inputNodeInteraction: inputNodeInteraction, theme: theme, selected: { node, rect in + return StickerPaneSearchStickerItem(context: context, code: code, stickerItem: stickerItem, inputNodeInteraction: inputNodeInteraction, theme: theme, selected: { node, rect in interaction.sendSticker(.standalone(media: stickerItem.file), node.view, rect) }) case let .global(_, info, topItems, installed, topSeparator): - return StickerPaneSearchGlobalItem(account: account, theme: theme, strings: strings, listAppearance: true, fillsRow: true, info: info, topItems: topItems, topSeparator: topSeparator, regularInsets: false, installed: installed, unread: false, open: { + return StickerPaneSearchGlobalItem(context: context, theme: theme, strings: strings, listAppearance: true, fillsRow: true, info: info, topItems: topItems, topSeparator: topSeparator, regularInsets: false, installed: installed, unread: false, open: { interaction.open(info) }, install: { interaction.install(info, topItems, !installed) @@ -1062,7 +1062,7 @@ private struct FeaturedSearchGridTransition { let animated: Bool } -private func preparedFeaturedSearchEntryTransition(account: Account, theme: PresentationTheme, strings: PresentationStrings, from fromEntries: [FeaturedSearchEntry], to toEntries: [FeaturedSearchEntry], interaction: StickerPaneSearchInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction, itemContext: StickerPaneSearchGlobalItemContext) -> FeaturedSearchGridTransition { +private func preparedFeaturedSearchEntryTransition(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, from fromEntries: [FeaturedSearchEntry], to toEntries: [FeaturedSearchEntry], interaction: StickerPaneSearchInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction, itemContext: StickerPaneSearchGlobalItemContext) -> FeaturedSearchGridTransition { let stationaryItems: GridNodeStationaryItems = .none let scrollToItem: GridNodeScrollToItem? = nil var animated = false @@ -1071,8 +1071,8 @@ private func preparedFeaturedSearchEntryTransition(account: Account, theme: Pres let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices - let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction, inputNodeInteraction: inputNodeInteraction, itemContext: itemContext), previousIndex: $0.2) } - let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction, inputNodeInteraction: inputNodeInteraction, itemContext: itemContext)) } + let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(context: context, theme: theme, strings: strings, interaction: interaction, inputNodeInteraction: inputNodeInteraction, itemContext: itemContext), previousIndex: $0.2) } + let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, theme: theme, strings: strings, interaction: interaction, inputNodeInteraction: inputNodeInteraction, itemContext: itemContext)) } let firstIndexInSectionOffset = 0 @@ -1375,7 +1375,7 @@ private final class FeaturedPaneSearchContentNode: ASDisplayNode { } let previousEntries = strongSelf.currentEntries.swap(entries) - let transition = preparedFeaturedSearchEntryTransition(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, from: previousEntries ?? [], to: entries, interaction: interaction, inputNodeInteraction: strongSelf.inputNodeInteraction, itemContext: strongSelf.itemContext) + let transition = preparedFeaturedSearchEntryTransition(context: strongSelf.context, theme: strongSelf.theme, strings: strongSelf.strings, from: previousEntries ?? [], to: entries, interaction: interaction, inputNodeInteraction: strongSelf.inputNodeInteraction, itemContext: strongSelf.itemContext) strongSelf.enqueueTransition(transition) if displayResults { diff --git a/submodules/FeaturedStickersScreen/Sources/StickerPaneSearchGlobaltem.swift b/submodules/FeaturedStickersScreen/Sources/StickerPaneSearchGlobaltem.swift index 2208362791..c6342832f8 100644 --- a/submodules/FeaturedStickersScreen/Sources/StickerPaneSearchGlobaltem.swift +++ b/submodules/FeaturedStickersScreen/Sources/StickerPaneSearchGlobaltem.swift @@ -8,6 +8,7 @@ import Postbox import TelegramPresentationData import StickerPackPreviewUI import ListSectionHeaderNode +import AccountContext final class StickerPaneSearchGlobalSection: GridSection { let title: String? @@ -78,7 +79,7 @@ public final class StickerPaneSearchGlobalItemContext { } public final class StickerPaneSearchGlobalItem: GridItem { - public let account: Account + public let context: AccountContext public let theme: PresentationTheme public let strings: PresentationStrings public let listAppearance: Bool @@ -110,8 +111,8 @@ public final class StickerPaneSearchGlobalItem: GridItem { return (128.0 + additionalHeight, self.fillsRow) } - public init(account: Account, theme: PresentationTheme, strings: PresentationStrings, listAppearance: Bool, fillsRow: Bool = true, info: StickerPackCollectionInfo, topItems: [StickerPackItem], topSeparator: Bool, regularInsets: Bool, installed: Bool, installing: Bool = false, unread: Bool, open: @escaping () -> Void, install: @escaping () -> Void, getItemIsPreviewed: @escaping (StickerPackItem) -> Bool, itemContext: StickerPaneSearchGlobalItemContext, sectionTitle: String? = nil) { - self.account = account + public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, listAppearance: Bool, fillsRow: Bool = true, info: StickerPackCollectionInfo, topItems: [StickerPackItem], topSeparator: Bool, regularInsets: Bool, installed: Bool, installing: Bool = false, unread: Bool, open: @escaping () -> Void, install: @escaping () -> Void, getItemIsPreviewed: @escaping (StickerPackItem) -> Bool, itemContext: StickerPaneSearchGlobalItemContext, sectionTitle: String? = nil) { + self.context = context self.theme = theme self.strings = strings self.listAppearance = listAppearance @@ -199,7 +200,7 @@ public class StickerPaneSearchGlobalItemNode: GridItemNode { if let item = self.item, self.isVisibleInGrid, !self.preloadedThumbnail { self.preloadedThumbnail = true - self.preloadedStickerPackThumbnailDisposable.set(preloadedStickerPackThumbnail(account: item.account, info: item.info, items: item.topItems).start()) + self.preloadedStickerPackThumbnailDisposable.set(preloadedStickerPackThumbnail(account: item.context.account, info: item.info, items: item.topItems).start()) } } } @@ -320,7 +321,7 @@ public class StickerPaneSearchGlobalItemNode: GridItemNode { public func setup(item: StickerPaneSearchGlobalItem) { if item.topItems.count < Int(item.info.count) && item.topItems.count < 5 && self.item?.info.id != item.info.id { - self.preloadDisposable.set(preloadedFeaturedStickerSet(network: item.account.network, postbox: item.account.postbox, id: item.info.id).start()) + self.preloadDisposable.set(preloadedFeaturedStickerSet(network: item.context.account.network, postbox: item.context.account.postbox, id: item.info.id).start()) } self.item = item @@ -334,7 +335,7 @@ public class StickerPaneSearchGlobalItemNode: GridItemNode { return } - self.canPlayMedia = item.itemContext.canPlayMedia + self.canPlayMedia = item.itemContext.canPlayMedia && item.context.sharedContext.energyUsageSettings.loopStickers } public func highlight() { @@ -486,7 +487,7 @@ public class StickerPaneSearchGlobalItemNode: GridItemNode { strongSelf.addSubnode(node) } if file.fileId != node.file?.fileId { - node.setup(account: item.account, item: topItems[i], itemSize: itemSize, synchronousLoads: synchronousLoads) + node.setup(account: item.context.account, item: topItems[i], itemSize: itemSize, synchronousLoads: synchronousLoads) } if item.theme !== node.theme { node.update(theme: item.theme, listAppearance: item.listAppearance) @@ -508,7 +509,7 @@ public class StickerPaneSearchGlobalItemNode: GridItemNode { } } - self.canPlayMedia = item.itemContext.canPlayMedia + self.canPlayMedia = item.itemContext.canPlayMedia && item.context.sharedContext.energyUsageSettings.loopStickers } @objc func installPressed() { diff --git a/submodules/FeaturedStickersScreen/Sources/StickerPaneSearchStickerItem.swift b/submodules/FeaturedStickersScreen/Sources/StickerPaneSearchStickerItem.swift index 5fb72f5320..d2f00853fa 100644 --- a/submodules/FeaturedStickersScreen/Sources/StickerPaneSearchStickerItem.swift +++ b/submodules/FeaturedStickersScreen/Sources/StickerPaneSearchStickerItem.swift @@ -67,7 +67,7 @@ final class StickerPaneSearchStickerSectionNode: ASDisplayNode { } public final class StickerPaneSearchStickerItem: GridItem { - public let account: Account + public let context: AccountContext public let code: String? public let stickerItem: FoundStickerItem public let selected: (ASDisplayNode, CGRect) -> Void @@ -75,8 +75,8 @@ public final class StickerPaneSearchStickerItem: GridItem { public let section: GridSection? - public init(account: Account, code: String?, stickerItem: FoundStickerItem, inputNodeInteraction: ChatMediaInputNodeInteraction, theme: PresentationTheme, selected: @escaping (ASDisplayNode, CGRect) -> Void) { - self.account = account + public init(context: AccountContext, code: String?, stickerItem: FoundStickerItem, inputNodeInteraction: ChatMediaInputNodeInteraction, theme: PresentationTheme, selected: @escaping (ASDisplayNode, CGRect) -> Void) { + self.context = context self.stickerItem = stickerItem self.inputNodeInteraction = inputNodeInteraction self.selected = selected @@ -87,7 +87,7 @@ public final class StickerPaneSearchStickerItem: GridItem { public func node(layout: GridNodeLayout, synchronousLoad: Bool) -> GridItemNode { let node = StickerPaneSearchStickerItemNode() node.inputNodeInteraction = self.inputNodeInteraction - node.setup(account: self.account, stickerItem: self.stickerItem, code: self.code) + node.setup(context: self.context, stickerItem: self.stickerItem, code: self.code) node.selected = self.selected return node } @@ -98,7 +98,7 @@ public final class StickerPaneSearchStickerItem: GridItem { return } node.inputNodeInteraction = self.inputNodeInteraction - node.setup(account: self.account, stickerItem: self.stickerItem, code: self.code) + node.setup(context: self.context, stickerItem: self.stickerItem, code: self.code) node.selected = self.selected } } @@ -106,7 +106,7 @@ public final class StickerPaneSearchStickerItem: GridItem { private let textFont = Font.regular(20.0) public final class StickerPaneSearchStickerItemNode: GridItemNode { - private var currentState: (Account, FoundStickerItem, CGSize)? + private var currentState: (AccountContext, FoundStickerItem, CGSize)? public let imageNode: TransformImageNode public private(set) var animationNode: AnimatedStickerNode? private let textNode: ASTextNode @@ -152,8 +152,8 @@ public final class StickerPaneSearchStickerItemNode: GridItemNode { self.imageNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:)))) } - func setup(account: Account, stickerItem: FoundStickerItem, code: String?) { - if self.currentState == nil || self.currentState!.0 !== account || self.currentState!.1 != stickerItem { + func setup(context: AccountContext, stickerItem: FoundStickerItem, code: String?) { + if self.currentState == nil || self.currentState!.0 !== context || self.currentState!.1 != stickerItem { self.textNode.attributedText = NSAttributedString(string: code ?? "", font: textFont, textColor: .black) if let dimensions = stickerItem.file.dimensions { @@ -166,20 +166,20 @@ public final class StickerPaneSearchStickerItemNode: GridItemNode { } let dimensions = stickerItem.file.dimensions ?? PixelDimensions(width: 512, height: 512) let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0)) - self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: stickerItem.file.resource, isVideo: stickerItem.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .cached) - self.animationNode?.visibility = self.isVisibleInGrid - self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, userLocation: .other, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start()) + self.animationNode?.setup(source: AnimatedStickerResourceSource(account: context.account, resource: stickerItem.file.resource, isVideo: stickerItem.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .cached) + self.animationNode?.visibility = self.isVisibleInGrid && context.sharedContext.energyUsageSettings.loopStickers + self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: context.account, userLocation: .other, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start()) } else { if let animationNode = self.animationNode { animationNode.visibility = false self.animationNode = nil animationNode.removeFromSupernode() } - self.imageNode.setSignal(chatMessageSticker(account: account, userLocation: .other, file: stickerItem.file, small: true)) - self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, userLocation: .other, fileReference: stickerPackFileReference(stickerItem.file), resource: chatMessageStickerResource(file: stickerItem.file, small: true)).start()) + self.imageNode.setSignal(chatMessageSticker(account: context.account, userLocation: .other, file: stickerItem.file, small: true)) + self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: context.account, userLocation: .other, fileReference: stickerPackFileReference(stickerItem.file), resource: chatMessageStickerResource(file: stickerItem.file, small: true)).start()) } - self.currentState = (account, stickerItem, dimensions.cgSize) + self.currentState = (context, stickerItem, dimensions.cgSize) self.setNeedsLayout() } } @@ -217,7 +217,11 @@ public final class StickerPaneSearchStickerItemNode: GridItemNode { } public func updateVisibility() { - let isPlaying = self.isVisibleInGrid + guard let context = self.currentState?.0 else { + return + } + + let isPlaying = self.isVisibleInGrid && context.sharedContext.energyUsageSettings.loopStickers if self.isPlaying != isPlaying { self.isPlaying = isPlaying self.animationNode?.visibility = isPlaying diff --git a/submodules/ItemListUI/Sources/ItemListRevealOptionsNode.swift b/submodules/ItemListUI/Sources/ItemListRevealOptionsNode.swift index aaad314744..0554cbac28 100644 --- a/submodules/ItemListUI/Sources/ItemListRevealOptionsNode.swift +++ b/submodules/ItemListUI/Sources/ItemListRevealOptionsNode.swift @@ -83,19 +83,23 @@ private final class ItemListRevealOptionNode: ASDisplayNode { private let iconNode: ASImageNode? private let animationNode: SimpleAnimationNode? + private let enableAnimations: Bool + private var animationScale: CGFloat = 1.0 private var animationNodeOffset: CGFloat = 0.0 private var animationNodeFlip = false var alignment: ItemListRevealOptionAlignment? var isExpanded: Bool = false - init(title: String, icon: ItemListRevealOptionIcon, color: UIColor, textColor: UIColor) { + init(title: String, icon: ItemListRevealOptionIcon, color: UIColor, textColor: UIColor, enableAnimations: Bool) { self.backgroundNode = ASDisplayNode() self.highlightNode = ASDisplayNode() self.titleNode = ASTextNode() self.titleNode.attributedText = NSAttributedString(string: title, font: icon == .none ? titleFontWithoutIcon : titleFontWithIcon, textColor: textColor) + self.enableAnimations = enableAnimations + switch icon { case let .image(image): let iconNode = ASImageNode() @@ -113,6 +117,9 @@ private final class ItemListRevealOptionNode: ASDisplayNode { } } self.animationNode = SimpleAnimationNode(animationName: animation, replaceColors: colors, size: CGSize(width: 79.0, height: 79.0), playOnce: true) + if !enableAnimations { + self.animationNode!.seekToEnd() + } if flip { self.animationNode!.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) } @@ -217,10 +224,12 @@ private final class ItemListRevealOptionNode: ASDisplayNode { transition.updateFrame(node: self.titleNode, frame: titleFrame) } - if (abs(revealFactor) >= 0.4) { - animationNode.play() - } else if abs(revealFactor) < CGFloat.ulpOfOne && !transition.isAnimated { - animationNode.reset() + if self.enableAnimations { + if (abs(revealFactor) >= 0.4) { + animationNode.play() + } else if abs(revealFactor) < CGFloat.ulpOfOne && !transition.isAnimated { + animationNode.reset() + } } } else if let iconNode = self.iconNode, let imageSize = iconNode.image?.size { let iconOffset: CGFloat = -9.0 @@ -303,7 +312,7 @@ public final class ItemListRevealOptionsNode: ASDisplayNode { self.view.addGestureRecognizer(gestureRecognizer) } - public func setOptions(_ options: [ItemListRevealOption], isLeft: Bool) { + public func setOptions(_ options: [ItemListRevealOption], isLeft: Bool, enableAnimations: Bool) { if self.options != options || self.isLeft != isLeft { self.options = options self.isLeft = isLeft @@ -311,7 +320,7 @@ public final class ItemListRevealOptionsNode: ASDisplayNode { node.removeFromSupernode() } self.optionNodes = options.map { option in - return ItemListRevealOptionNode(title: option.title, icon: option.icon, color: option.color, textColor: option.textColor) + return ItemListRevealOptionNode(title: option.title, icon: option.icon, color: option.color, textColor: option.textColor, enableAnimations: enableAnimations) } if isLeft { for node in self.optionNodes.reversed() { diff --git a/submodules/ItemListUI/Sources/Items/ItemListEditableItem.swift b/submodules/ItemListUI/Sources/Items/ItemListEditableItem.swift index 0a17128b9b..f0811c1e3c 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListEditableItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListEditableItem.swift @@ -64,6 +64,7 @@ open class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerD private var leftRevealNode: ItemListRevealOptionsNode? private var rightRevealNode: ItemListRevealOptionsNode? private var revealOptions: (left: [ItemListRevealOption], right: [ItemListRevealOption]) = ([], []) + private var enableAnimations: Bool = true private var initialRevealOffset: CGFloat = 0.0 public private(set) var revealOffset: CGFloat = 0.0 @@ -117,13 +118,14 @@ open class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerD } } - open func setRevealOptions(_ options: (left: [ItemListRevealOption], right: [ItemListRevealOption])) { + open func setRevealOptions(_ options: (left: [ItemListRevealOption], right: [ItemListRevealOption]), enableAnimations: Bool = true) { if self.revealOptions == options { return } let previousOptions = self.revealOptions let wasEmpty = self.revealOptions.left.isEmpty && self.revealOptions.right.isEmpty self.revealOptions = options + self.enableAnimations = enableAnimations let isEmpty = options.left.isEmpty && options.right.isEmpty if options.left.isEmpty { if let _ = self.leftRevealNode { @@ -329,7 +331,7 @@ open class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerD }, tapticAction: { [weak self] in self?.hapticImpact() }) - revealNode.setOptions(self.revealOptions.left, isLeft: true) + revealNode.setOptions(self.revealOptions.left, isLeft: true, enableAnimations: self.enableAnimations) self.leftRevealNode = revealNode if let (size, leftInset, _) = self.validLayout { @@ -351,7 +353,7 @@ open class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerD }, tapticAction: { [weak self] in self?.hapticImpact() }) - revealNode.setOptions(self.revealOptions.right, isLeft: false) + revealNode.setOptions(self.revealOptions.right, isLeft: false, enableAnimations: self.enableAnimations) self.rightRevealNode = revealNode if let (size, _, rightInset) = self.validLayout { diff --git a/submodules/ManagedAnimationNode/Sources/ManagedAnimationNode.swift b/submodules/ManagedAnimationNode/Sources/ManagedAnimationNode.swift index 46ac39fc3e..82bacf44f3 100644 --- a/submodules/ManagedAnimationNode/Sources/ManagedAnimationNode.swift +++ b/submodules/ManagedAnimationNode/Sources/ManagedAnimationNode.swift @@ -330,6 +330,7 @@ open class ManagedAnimationNode: ASDisplayNode { public final class SimpleAnimationNode: ManagedAnimationNode { private let stillItem: ManagedAnimationItem + private let stillEndItem: ManagedAnimationItem private let animationItem: ManagedAnimationItem public let size: CGSize @@ -340,6 +341,7 @@ public final class SimpleAnimationNode: ManagedAnimationNode { self.size = size self.playOnce = playOnce self.stillItem = ManagedAnimationItem(source: .local(animationName), replaceColors: replaceColors, frames: .range(startFrame: 0, endFrame: 0), duration: 0.01) + self.stillEndItem = ManagedAnimationItem(source: .local(animationName), replaceColors: replaceColors, frames: .still(.end), duration: 0.01) self.animationItem = ManagedAnimationItem(source: .local(animationName), replaceColors: replaceColors) super.init(size: size) @@ -358,4 +360,9 @@ public final class SimpleAnimationNode: ManagedAnimationNode { self.didPlay = false self.trackTo(item: self.stillItem) } + + public func seekToEnd() { + self.didPlay = false + self.trackTo(item: self.stillEndItem) + } } diff --git a/submodules/PasscodeUI/Sources/PasscodeEntryController.swift b/submodules/PasscodeUI/Sources/PasscodeEntryController.swift index 38734d0a56..41f3518fab 100644 --- a/submodules/PasscodeUI/Sources/PasscodeEntryController.swift +++ b/submodules/PasscodeUI/Sources/PasscodeEntryController.swift @@ -39,6 +39,7 @@ public final class PasscodeEntryController: ViewController { private let applicationBindings: TelegramApplicationBindings private let accountManager: AccountManager + private var energyUsageSettings: EnergyUsageSettings? private let appLockContext: AppLockContext private let presentationDataSignal: Signal diff --git a/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift b/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift index daca869132..113573f540 100644 --- a/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift @@ -12,6 +12,7 @@ import AppBundle import PasscodeInputFieldNode import MonotonicTime import GradientBackground +import TelegramUIPreferences private extension CGRect { var center: CGPoint { @@ -60,6 +61,9 @@ final class PasscodeEntryControllerNode: ASDisplayNode { var checkPasscode: ((String) -> Void)? var requestBiometrics: (() -> Void)? + var energyUsageSettings: EnergyUsageSettings = .default + var energyUsageSettingsDisposable: Disposable? + init(accountManager: AccountManager, presentationData: PresentationData, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, passcodeType: PasscodeEntryFieldType, biometricsType: LocalAuthBiometricAuthentication?, arguments: PasscodeEntryControllerPresentationArguments, modalPresentation: Bool) { self.accountManager = accountManager self.presentationData = presentationData @@ -105,7 +109,9 @@ final class PasscodeEntryControllerNode: ASDisplayNode { if let strongSelf = self { strongSelf.inputFieldNode.append(character) if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode { - gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: false, completion: {}) + if strongSelf.energyUsageSettings.fullTranslucency { + gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: false, completion: {}) + } } } } @@ -113,7 +119,9 @@ final class PasscodeEntryControllerNode: ASDisplayNode { if let strongSelf = self { let _ = strongSelf.inputFieldNode.delete() if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode { - gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: true, completion: {}) + if strongSelf.energyUsageSettings.fullTranslucency { + gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: true, completion: {}) + } } } } @@ -158,6 +166,24 @@ final class PasscodeEntryControllerNode: ASDisplayNode { if self.arguments.cancel != nil { self.addSubnode(self.cancelButtonNode) } + + self.energyUsageSettingsDisposable = (accountManager.sharedData(keys: Set([ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings])) + |> deliverOnMainQueue).start(next: { [weak self] sharedData in + guard let self else { + return + } + if let mediaAutoDownloadSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings]?.get(MediaAutoDownloadSettings.self) { + if automaticEnergyUsageShouldBeOnNow(settings: mediaAutoDownloadSettings) { + self.energyUsageSettings = EnergyUsageSettings.powerSavingDefault + } else { + self.energyUsageSettings = mediaAutoDownloadSettings.energyUsageSettings + } + } + }) + } + + deinit { + self.energyUsageSettingsDisposable?.dispose() } override func didLoad() { @@ -183,7 +209,9 @@ final class PasscodeEntryControllerNode: ASDisplayNode { self.hapticFeedback.tap() let result = self.inputFieldNode.delete() if result, let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { - gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: true, completion: {}) + if self.energyUsageSettings.fullTranslucency { + gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), extendAnimation: false, backwards: true, completion: {}) + } } } @@ -359,7 +387,10 @@ final class PasscodeEntryControllerNode: ASDisplayNode { if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: true, backwards: false, completion: {}) + + if self.energyUsageSettings.fullTranslucency { + gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: true, backwards: false, completion: {}) + } } } self.titleNode.setAttributedText(NSAttributedString(string: self.strings.EnterPasscode_EnterPasscode, font: titleFont, textColor: .white), animation: .none) @@ -377,9 +408,11 @@ final class PasscodeEntryControllerNode: ASDisplayNode { } }) self.backgroundImageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { + if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode, self.energyUsageSettings.fullTranslucency { gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - gradientNode.animateEvent(transition: .animated(duration: 0.35, curve: .spring), extendAnimation: false, backwards: false, completion: {}) + if self.energyUsageSettings.fullTranslucency { + gradientNode.animateEvent(transition: .animated(duration: 0.35, curve: .spring), extendAnimation: false, backwards: false, completion: {}) + } self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } if !iconFrame.isEmpty { @@ -411,7 +444,9 @@ final class PasscodeEntryControllerNode: ASDisplayNode { self.subtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { - gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: false, backwards: false, completion: {}) + if self.energyUsageSettings.fullTranslucency { + gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: false, backwards: false, completion: {}) + } } self.inputFieldNode.animateIn() self.keyboardNode.animateIn() @@ -454,7 +489,9 @@ final class PasscodeEntryControllerNode: ASDisplayNode { self.hapticFeedback.error() if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { - gradientNode.animateEvent(transition: .animated(duration: 1.5, curve: .spring), extendAnimation: true, backwards: true, completion: {}) + if self.energyUsageSettings.fullTranslucency { + gradientNode.animateEvent(transition: .animated(duration: 1.5, curve: .spring), extendAnimation: true, backwards: true, completion: {}) + } } } diff --git a/submodules/PassportUI/Sources/Form/FormEditableBlockItemNode.swift b/submodules/PassportUI/Sources/Form/FormEditableBlockItemNode.swift index d2643b76c0..e28d1d5339 100644 --- a/submodules/PassportUI/Sources/Form/FormEditableBlockItemNode.swift +++ b/submodules/PassportUI/Sources/Form/FormEditableBlockItemNode.swift @@ -259,7 +259,7 @@ class FormEditableBlockItemNode: ASDisplayNode, FormCo }, tapticAction: { [weak self] in self?.hapticImpact() }) - revealNode.setOptions(self.revealOptions.left, isLeft: true) + revealNode.setOptions(self.revealOptions.left, isLeft: true, enableAnimations: true) self.leftRevealNode = revealNode if let (size, leftInset, _) = self.validLayout { @@ -281,7 +281,7 @@ class FormEditableBlockItemNode: ASDisplayNode, FormCo }, tapticAction: { [weak self] in self?.hapticImpact() }) - revealNode.setOptions(self.revealOptions.right, isLeft: false) + revealNode.setOptions(self.revealOptions.right, isLeft: false, enableAnimations: true) self.rightRevealNode = revealNode if let (size, _, rightInset) = self.validLayout { diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/StickerPaneSearchContentNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/StickerPaneSearchContentNode.swift index 4e8b5c162e..8f38792377 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/StickerPaneSearchContentNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/StickerPaneSearchContentNode.swift @@ -87,16 +87,16 @@ private enum StickerSearchEntry: Identifiable, Comparable { } } - func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, interaction: StickerPaneSearchInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction) -> GridItem { + func item(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, interaction: StickerPaneSearchInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction) -> GridItem { switch self { case let .sticker(_, code, stickerItem, theme): - return StickerPaneSearchStickerItem(account: account, code: code, stickerItem: stickerItem, inputNodeInteraction: inputNodeInteraction, theme: theme, selected: { node, rect in + return StickerPaneSearchStickerItem(context: context, code: code, stickerItem: stickerItem, inputNodeInteraction: inputNodeInteraction, theme: theme, selected: { node, rect in interaction.sendSticker(.standalone(media: stickerItem.file), node.view, rect) }) case let .global(_, info, topItems, installed, topSeparator): let itemContext = StickerPaneSearchGlobalItemContext() itemContext.canPlayMedia = true - return StickerPaneSearchGlobalItem(account: account, theme: theme, strings: strings, listAppearance: false, info: info, topItems: topItems, topSeparator: topSeparator, regularInsets: false, installed: installed, unread: false, open: { + return StickerPaneSearchGlobalItem(context: context, theme: theme, strings: strings, listAppearance: false, info: info, topItems: topItems, topSeparator: topSeparator, regularInsets: false, installed: installed, unread: false, open: { interaction.open(info) }, install: { interaction.install(info, topItems, !installed) @@ -117,7 +117,7 @@ private struct StickerPaneSearchGridTransition { let animated: Bool } -private func preparedChatMediaInputGridEntryTransition(account: Account, theme: PresentationTheme, strings: PresentationStrings, from fromEntries: [StickerSearchEntry], to toEntries: [StickerSearchEntry], interaction: StickerPaneSearchInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction) -> StickerPaneSearchGridTransition { +private func preparedChatMediaInputGridEntryTransition(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, from fromEntries: [StickerSearchEntry], to toEntries: [StickerSearchEntry], interaction: StickerPaneSearchInteraction, inputNodeInteraction: ChatMediaInputNodeInteraction) -> StickerPaneSearchGridTransition { let stationaryItems: GridNodeStationaryItems = .none let scrollToItem: GridNodeScrollToItem? = nil var animated = false @@ -126,8 +126,8 @@ private func preparedChatMediaInputGridEntryTransition(account: Account, theme: let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices - let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction, inputNodeInteraction: inputNodeInteraction), previousIndex: $0.2) } - let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction, inputNodeInteraction: inputNodeInteraction)) } + let insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(context: context, theme: theme, strings: strings, interaction: interaction, inputNodeInteraction: inputNodeInteraction), previousIndex: $0.2) } + let updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, theme: theme, strings: strings, interaction: interaction, inputNodeInteraction: inputNodeInteraction)) } let firstIndexInSectionOffset = 0 @@ -508,7 +508,7 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode { } let previousEntries = strongSelf.currentEntries.swap(entries) - let transition = preparedChatMediaInputGridEntryTransition(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, from: previousEntries ?? [], to: entries, interaction: interaction, inputNodeInteraction: strongSelf.inputNodeInteraction) + let transition = preparedChatMediaInputGridEntryTransition(context: strongSelf.context, theme: strongSelf.theme, strings: strongSelf.strings, from: previousEntries ?? [], to: entries, interaction: interaction, inputNodeInteraction: strongSelf.inputNodeInteraction) strongSelf.enqueueTransition(transition) } })) diff --git a/submodules/TelegramUI/Sources/HashtagChatInputPanelItem.swift b/submodules/TelegramUI/Sources/HashtagChatInputPanelItem.swift index e059fdb382..dfccdec0f2 100644 --- a/submodules/TelegramUI/Sources/HashtagChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/HashtagChatInputPanelItem.swift @@ -366,7 +366,7 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode { }, tapticAction: { [weak self] in self?.hapticImpact() }) - revealNode.setOptions(self.revealOptions, isLeft: false) + revealNode.setOptions(self.revealOptions, isLeft: false, enableAnimations: true) self.revealNode = revealNode if let (size, _, rightInset) = self.validLayout { diff --git a/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift b/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift index d7ab86afb6..5c128d945e 100644 --- a/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift @@ -414,7 +414,7 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { }, tapticAction: { [weak self] in self?.hapticImpact() }) - revealNode.setOptions(self.revealOptions, isLeft: false) + revealNode.setOptions(self.revealOptions, isLeft: false, enableAnimations: true) self.revealNode = revealNode if let (size, _, rightInset) = self.validLayout {