Emoji UI improvements

This commit is contained in:
Ali 2023-01-24 14:25:26 +01:00
parent 52c60a3f0d
commit b92d21f9d7
33 changed files with 318 additions and 123 deletions

View File

@ -252,6 +252,15 @@ public final class DefaultAnimatedStickerNodeImpl: ASDisplayNode, AnimatedSticke
private var overlayColor: (UIColor?, Bool)? = nil
private var size: CGSize?
public var dynamicColor: UIColor? {
didSet {
if let renderer = self.renderer?.renderer as? SoftwareAnimationRenderer {
renderer.renderAsTemplateImage = self.dynamicColor != nil
}
self.renderer?.renderer.view.tintColor = self.dynamicColor
}
}
public init(useMetalCache: Bool = false) {
self.queue = sharedQueue
self.eventsNode = AnimatedStickerNodeDisplayEvents()
@ -279,12 +288,12 @@ public final class DefaultAnimatedStickerNodeImpl: ASDisplayNode, AnimatedSticke
if #available(iOS 10.0, *) {
return CompressedAnimationRenderer()
} else {
return SoftwareAnimationRenderer()
return SoftwareAnimationRenderer(templateImageSupport: true)
}
})
private static let softwareRendererPool = AnimationRendererPool(generate: {
return SoftwareAnimationRenderer()
return SoftwareAnimationRenderer(templateImageSupport: true)
})
private weak var nodeToCopyFrameFrom: DefaultAnimatedStickerNodeImpl?
@ -295,6 +304,12 @@ public final class DefaultAnimatedStickerNodeImpl: ASDisplayNode, AnimatedSticke
self.renderer = DefaultAnimatedStickerNodeImpl.hardwareRendererPool.take()
} else {
self.renderer = DefaultAnimatedStickerNodeImpl.softwareRendererPool.take()
if let renderer = self.renderer?.renderer as? SoftwareAnimationRenderer {
renderer.renderAsTemplateImage = self.dynamicColor != nil
}
self.renderer?.renderer.view.tintColor = self.dynamicColor
if let contents = self.nodeToCopyFrameFrom?.renderer?.renderer.contents {
self.renderer?.renderer.contents = contents
}

View File

@ -7,20 +7,30 @@ import YuvConversion
import Accelerate
final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
private let templateImageSupport: Bool
private var highlightedContentNode: ASDisplayNode?
private var highlightedColor: UIColor?
private var highlightReplacesContent = false
public var renderAsTemplateImage: Bool = false
public var currentFrameImage: UIImage? {
if let contents = self.contents {
return UIImage(cgImage: contents as! CGImage)
} else {
return nil
public private(set) var currentFrameImage: UIImage?
init(templateImageSupport: Bool) {
self.templateImageSupport = templateImageSupport
super.init()
if templateImageSupport {
self.setViewBlock({
return UIImageView()
})
}
}
func render(queue: Queue, width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType, mulAlpha: Bool, completion: @escaping () -> Void) {
assert(bytesPerRow > 0)
let renderAsTemplateImage = self.renderAsTemplateImage
queue.async { [weak self] in
switch type {
case .argb:
@ -70,13 +80,21 @@ final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
break
}
})
if renderAsTemplateImage {
image = image?.withRenderingMode(.alwaysTemplate)
}
}
Queue.mainQueue().async {
guard let strongSelf = self else {
return
}
strongSelf.contents = image?.cgImage
strongSelf.currentFrameImage = image
if strongSelf.templateImageSupport {
(strongSelf.view as? UIImageView)?.image = image
} else {
strongSelf.contents = image?.cgImage
}
strongSelf.updateHighlightedContentNode()
if strongSelf.highlightedContentNode?.frame != strongSelf.bounds {
strongSelf.highlightedContentNode?.frame = strongSelf.bounds
@ -90,12 +108,14 @@ final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
guard let highlightedContentNode = self.highlightedContentNode, let highlightedColor = self.highlightedColor else {
return
}
if let contents = self.contents, CFGetTypeID(contents as CFTypeRef) == CGImage.typeID {
(highlightedContentNode.view as! UIImageView).image = UIImage(cgImage: contents as! CGImage).withRenderingMode(.alwaysTemplate)
}
(highlightedContentNode.view as! UIImageView).image = self.currentFrameImage?.withRenderingMode(.alwaysTemplate)
highlightedContentNode.tintColor = highlightedColor
if self.highlightReplacesContent {
self.contents = nil
if self.templateImageSupport {
(self.view as? UIImageView)?.image = nil
} else {
self.contents = nil
}
}
}

View File

@ -136,21 +136,30 @@ public struct Transition {
//view.layer.position = CGPoint(x: frame.midX, y: frame.midY)
view.layer.removeAnimation(forKey: "position")
view.layer.removeAnimation(forKey: "bounds")
view.layer.removeAnimation(forKey: "bounds.size")
completion?(true)
case .curve:
let previousFrame: CGRect
if (view.layer.animation(forKey: "position") != nil || view.layer.animation(forKey: "bounds") != nil), let presentation = view.layer.presentation() {
previousFrame = presentation.frame
let previousPosition: CGPoint
let previousBounds: CGRect
if (view.layer.animation(forKey: "position") != nil || view.layer.animation(forKey: "bounds") != nil || view.layer.animation(forKey: "bounds.size") != nil), let presentation = view.layer.presentation() {
previousPosition = presentation.position
previousBounds = presentation.bounds
} else {
previousFrame = view.frame
previousPosition = view.layer.position
previousBounds = view.layer.bounds
}
view.frame = frame
//view.bounds = CGRect(origin: previousBounds.origin, size: frame.size)
//view.center = CGPoint(x: frame.midX, y: frame.midY)
let anchorPoint = view.layer.anchorPoint
let updatedPosition = CGPoint(x: frame.minX + frame.width * anchorPoint.x, y: frame.minY + frame.height * anchorPoint.y)
self.animatePosition(view: view, from: CGPoint(x: previousFrame.midX, y: previousFrame.midY), to: CGPoint(x: frame.midX, y: frame.midY), completion: completion)
self.animateBoundsSize(view: view, from: previousFrame.size, to: frame.size)
self.animatePosition(view: view, from: previousPosition, to: updatedPosition, completion: completion)
if previousBounds.size != frame.size {
self.animateBoundsSize(view: view, from: previousBounds.size, to: frame.size)
}
}
}
@ -193,10 +202,12 @@ public struct Transition {
case .none:
view.bounds = bounds
view.layer.removeAnimation(forKey: "bounds")
view.layer.removeAnimation(forKey: "bounds.origin")
view.layer.removeAnimation(forKey: "bounds.size")
completion?(true)
case .curve:
let previousBounds: CGRect
if view.layer.animation(forKey: "bounds") != nil, let presentation = view.layer.presentation() {
if (view.layer.animation(forKey: "bounds") != nil || view.layer.animation(forKey: "bounds.origin") != nil || view.layer.animation(forKey: "bounds.size") != nil), let presentation = view.layer.presentation() {
previousBounds = presentation.bounds
} else {
previousBounds = view.layer.bounds
@ -207,6 +218,30 @@ public struct Transition {
}
}
public func setBoundsOrigin(view: UIView, origin: CGPoint, completion: ((Bool) -> Void)? = nil) {
if view.bounds.origin == origin {
completion?(true)
return
}
switch self.animation {
case .none:
view.bounds = CGRect(origin: origin, size: view.bounds.size)
view.layer.removeAnimation(forKey: "bounds")
view.layer.removeAnimation(forKey: "bounds.origin")
completion?(true)
case .curve:
let previousOrigin: CGPoint
if (view.layer.animation(forKey: "bounds") != nil || view.layer.animation(forKey: "bounds.origin") != nil), let presentation = view.layer.presentation() {
previousOrigin = presentation.bounds.origin
} else {
previousOrigin = view.layer.bounds.origin
}
view.bounds = CGRect(origin: origin, size: view.bounds.size)
self.animateBoundsOrigin(view: view, from: previousOrigin, to: origin, completion: completion)
}
}
public func setBoundsSize(view: UIView, size: CGSize, completion: ((Bool) -> Void)? = nil) {
if view.bounds.size == size {
completion?(true)

View File

@ -395,6 +395,12 @@ public final class NavigationButtonNode: ContextControllerSourceNode {
}
}
public func updateManualAlpha(alpha: CGFloat, transition: ContainedViewLayoutTransition) {
for node in self.nodes {
transition.updateAlpha(node: node, alpha: alpha)
}
}
func updateManualText(_ text: String, isBack: Bool = true) {
let node: NavigationButtonItemNode
if self.nodes.count > 0 {

View File

@ -552,7 +552,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
}
}))
]
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: item, menu: menuItems, openPremiumIntro: { [weak self] in
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(context: strongSelf.context, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: item, menu: menuItems, openPremiumIntro: { [weak self] in
guard let strongSelf = self else {
return
}
@ -640,7 +640,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
}
}))
]
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: .pack(item.file), menu: menuItems, openPremiumIntro: { [weak self] in
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(context: strongSelf.context, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: .pack(item.file), menu: menuItems, openPremiumIntro: { [weak self] in
guard let strongSelf = self else {
return
}

View File

@ -259,7 +259,7 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll
}
})))
}
return .single((itemNode.view, itemNode.bounds, StickerPreviewPeekContent(account: strongSelf.context.account, item: item, menu: menuItems)))
return .single((itemNode.view, itemNode.bounds, StickerPreviewPeekContent(context: strongSelf.context, item: item, menu: menuItems)))
}
}
return nil

View File

@ -9,14 +9,15 @@ import StickerResources
import AnimatedStickerNode
import TelegramAnimatedStickerNode
import ContextUI
import AccountContext
final class StickerPreviewPeekContent: PeekControllerContent {
let account: Account
let context: AccountContext
let item: ImportStickerPack.Sticker
let menu: [ContextMenuItem]
init(account: Account, item: ImportStickerPack.Sticker, menu: [ContextMenuItem]) {
self.account = account
init(context: AccountContext, item: ImportStickerPack.Sticker, menu: [ContextMenuItem]) {
self.context = context
self.item = item
self.menu = menu
}
@ -34,7 +35,7 @@ final class StickerPreviewPeekContent: PeekControllerContent {
}
func node() -> PeekControllerContentNode & ASDisplayNode {
return StickerPreviewPeekContentNode(account: self.account, item: self.item)
return StickerPreviewPeekContentNode(account: self.context.account, item: self.item)
}
func topAccessoryNode() -> ASDisplayNode? {

View File

@ -169,6 +169,7 @@ private final class SubItemNode: HighlightTrackingButtonNode {
}
} else {
checkNode = CheckNode(theme: CheckNodeTheme(theme: presentationData.theme, style: .plain))
checkNode.isUserInteractionEnabled = false
self.checkNode = checkNode
self.addSubnode(checkNode)
}

View File

@ -460,7 +460,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
} else {
strongSelf.stableEmptyResultEmoji = nil
}
emojiContent = emojiContent.withUpdatedItemGroups(panelItemGroups: emojiContent.panelItemGroups, contentItemGroups: emojiSearchResult.groups, itemContentUniqueId: emojiSearchResult.id, emptySearchResults: emptySearchResults)
emojiContent = emojiContent.withUpdatedItemGroups(panelItemGroups: emojiContent.panelItemGroups, contentItemGroups: emojiSearchResult.groups, itemContentUniqueId: emojiSearchResult.id, emptySearchResults: emptySearchResults, searchState: .active)
} else {
strongSelf.stableEmptyResultEmoji = nil
}

View File

@ -12,14 +12,22 @@ import AccountContext
public func autodownloadDataSizeString(_ size: Int64, decimalSeparator: String = ".") -> String {
if size >= 1024 * 1024 * 1024 {
let remainder = (size % (1024 * 1024 * 1024)) / (1024 * 1024 * 102)
var remainder = (size % (1024 * 1024 * 1024)) / (1024 * 1024 * 102)
while remainder != 0 && remainder % 10 == 0 {
remainder /= 10
}
if remainder != 0 {
return "\(size / (1024 * 1024 * 1024))\(decimalSeparator)\(remainder) GB"
} else {
return "\(size / (1024 * 1024 * 1024)) GB"
}
} else if size >= 1024 * 1024 {
let remainder = (size % (1024 * 1024)) / (1024 * 102)
var remainder = (size % (1024 * 1024)) / (1024 * 102)
while remainder != 0 && remainder % 10 == 0 {
remainder /= 10
}
if size < 10 * 1024 * 1024 {
return "\(size / (1024 * 1024))\(decimalSeparator)\(remainder) MB"
} else {

View File

@ -239,7 +239,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
}
})))
}
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: .pack(item.file), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems, openPremiumIntro: {
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(context: strongSelf.context, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: .pack(item.file), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems, openPremiumIntro: {
}))
} else {

View File

@ -481,7 +481,7 @@ private final class StickerPackContainer: ASDisplayNode {
}
})))
}
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: .pack(item.file), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems, openPremiumIntro: { [weak self] in
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(context: strongSelf.context, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: .pack(item.file), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems, openPremiumIntro: { [weak self] in
guard let strongSelf = self else {
return
}

View File

@ -29,7 +29,7 @@ public enum StickerPreviewPeekItem: Equatable {
}
public final class StickerPreviewPeekContent: PeekControllerContent {
let account: Account
let context: AccountContext
let theme: PresentationTheme
let strings: PresentationStrings
public let item: StickerPreviewPeekItem
@ -37,8 +37,8 @@ public final class StickerPreviewPeekContent: PeekControllerContent {
let menu: [ContextMenuItem]
let openPremiumIntro: () -> Void
public init(account: Account, theme: PresentationTheme, strings: PresentationStrings, item: StickerPreviewPeekItem, isLocked: Bool = false, menu: [ContextMenuItem], openPremiumIntro: @escaping () -> Void) {
self.account = account
public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, item: StickerPreviewPeekItem, isLocked: Bool = false, menu: [ContextMenuItem], openPremiumIntro: @escaping () -> Void) {
self.context = context
self.theme = theme
self.strings = strings
self.item = item
@ -64,7 +64,7 @@ public final class StickerPreviewPeekContent: PeekControllerContent {
}
public func node() -> PeekControllerContentNode & ASDisplayNode {
return StickerPreviewPeekContentNode(account: self.account, item: self.item)
return StickerPreviewPeekContentNode(context: self.context, item: self.item, theme: self.theme)
}
public func topAccessoryNode() -> ASDisplayNode? {
@ -91,7 +91,7 @@ public final class StickerPreviewPeekContent: PeekControllerContent {
}
public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerContentNode {
private let account: Account
private let context: AccountContext
private let item: StickerPreviewPeekItem
private var textNode: ASTextNode
@ -105,8 +105,8 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
private let _ready = Promise<Bool>()
init(account: Account, item: StickerPreviewPeekItem) {
self.account = account
init(context: AccountContext, item: StickerPreviewPeekItem, theme: PresentationTheme) {
self.context = context
self.item = item
self.textNode = ASTextNode()
@ -133,14 +133,18 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
}
let fittedDimensions = dimensions.cgSize.aspectFitted(fitSize)
animationNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource, isVideo: item.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: isPremiumSticker ? .once : .loop, mode: .direct(cachePathPrefix: nil))
if item.file.isCustomTemplateEmoji {
animationNode.dynamicColor = theme.list.itemPrimaryTextColor
}
animationNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: item.file.resource, isVideo: item.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: isPremiumSticker ? .once : .loop, mode: .direct(cachePathPrefix: nil))
animationNode.visibility = true
animationNode.addSubnode(self.textNode)
if isPremiumSticker, let effect = item.file.videoThumbnails.first {
self.effectDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, userLocation: .other, fileReference: .standalone(media: item.file), resource: effect.resource).start())
self.effectDisposable.set(freeMediaFileResourceInteractiveFetched(account: context.account, userLocation: .other, fileReference: .standalone(media: item.file), resource: effect.resource).start())
let source = AnimatedStickerResourceSource(account: account, resource: effect.resource, fitzModifier: nil)
let source = AnimatedStickerResourceSource(account: context.account, resource: effect.resource, fitzModifier: nil)
let additionalAnimationNode = DefaultAnimatedStickerNodeImpl()
additionalAnimationNode.setup(source: source, width: Int(fittedDimensions.width * 2.0), height: Int(fittedDimensions.height * 2.0), playbackMode: .once, mode: .direct(cachePathPrefix: nil))
additionalAnimationNode.visibility = true
@ -151,7 +155,7 @@ public final class StickerPreviewPeekContentNode: ASDisplayNode, PeekControllerC
self.animationNode = nil
}
self.imageNode.setSignal(chatMessageSticker(account: account, userLocation: .other, file: item.file, small: false, fetched: true))
self.imageNode.setSignal(chatMessageSticker(account: context.account, userLocation: .other, file: item.file, small: false, fetched: true))
super.init()

View File

@ -633,6 +633,9 @@ public final class TelegramMediaFile: Media, Equatable, Codable {
if case .Sticker = attribute {
hasSticker = true
break
} else if case .CustomEmoji = attribute {
hasSticker = true
break
}
}
return hasSticker

View File

@ -850,9 +850,9 @@ final class AvatarEditorScreenComponent: Component {
}
if state?.keyboardContentId == AnyHashable("emoji") {
data.emoji = data.emoji.withUpdatedItemGroups(panelItemGroups: data.emoji.panelItemGroups, contentItemGroups: searchResult.groups, itemContentUniqueId: searchResult.id, emptySearchResults: emptySearchResults)
data.emoji = data.emoji.withUpdatedItemGroups(panelItemGroups: data.emoji.panelItemGroups, contentItemGroups: searchResult.groups, itemContentUniqueId: searchResult.id, emptySearchResults: emptySearchResults, searchState: .active)
} else {
data.stickers = data.stickers?.withUpdatedItemGroups(panelItemGroups: data.stickers?.panelItemGroups ?? searchResult.groups, contentItemGroups: searchResult.groups, itemContentUniqueId: searchResult.id, emptySearchResults: emptySearchResults)
data.stickers = data.stickers?.withUpdatedItemGroups(panelItemGroups: data.stickers?.panelItemGroups ?? searchResult.groups, contentItemGroups: searchResult.groups, itemContentUniqueId: searchResult.id, emptySearchResults: emptySearchResults, searchState: .active)
}
}

View File

@ -608,7 +608,11 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
strongSelf.currentUndoOverlayController = controller
controllerInteraction.presentController(controller, nil)
},
copyEmoji: { file in
copyEmoji: { [weak self] file in
guard let strongSelf = self else {
return
}
var text = "."
var emojiAttribute: ChatTextInputTextCustomEmojiAttribute?
loop: for attribute in file.attributes {
@ -625,6 +629,20 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
if let _ = emojiAttribute {
storeMessageTextInPasteboard(text, entities: [MessageTextEntity(range: 0 ..< (text as NSString).length, type: .CustomEmoji(stickerPack: nil, fileId: file.fileId.id))])
var animateInAsReplacement = false
if let currentUndoOverlayController = strongSelf.currentUndoOverlayController {
currentUndoOverlayController.dismissWithCommitActionAndReplacementAnimation()
strongSelf.currentUndoOverlayController = nil
animateInAsReplacement = true
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
//TODO:localize
let controller = UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, title: nil, text: "Emoji copied to clipboard.", undoText: nil, customAction: nil), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in return false })
strongSelf.currentUndoOverlayController = controller
controllerInteraction.presentController(controller, nil)
}
},
presentController: controllerInteraction.presentController,
@ -1425,7 +1443,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
)
}
if let emoji = inputData.emoji {
inputData.emoji = emoji.withUpdatedItemGroups(panelItemGroups: emoji.panelItemGroups, contentItemGroups: emojiSearchResult.groups, itemContentUniqueId: emojiSearchResult.id, emptySearchResults: emptySearchResults)
inputData.emoji = emoji.withUpdatedItemGroups(panelItemGroups: emoji.panelItemGroups, contentItemGroups: emojiSearchResult.groups, itemContentUniqueId: emojiSearchResult.id, emptySearchResults: emptySearchResults, searchState: .active)
}
}
@ -1439,7 +1457,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
)
}
if let stickers = inputData.stickers {
inputData.stickers = stickers.withUpdatedItemGroups(panelItemGroups: stickers.panelItemGroups, contentItemGroups: stickerSearchResult.groups, itemContentUniqueId: stickerSearchResult.id, emptySearchResults: stickerSearchResults)
inputData.stickers = stickers.withUpdatedItemGroups(panelItemGroups: stickers.panelItemGroups, contentItemGroups: stickerSearchResult.groups, itemContentUniqueId: stickerSearchResult.id, emptySearchResults: stickerSearchResults, searchState: .active)
}
}
@ -1861,10 +1879,10 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
private func processInputData(inputData: InputData) -> InputData {
return InputData(
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)
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, searchState: emoji.searchState)
},
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: stickers.itemContentUniqueId, emptySearchResults: nil)
return stickers.withUpdatedItemGroups(panelItemGroups: self.processStableItemGroupList(category: .stickers, itemGroups: stickers.panelItemGroups), contentItemGroups: self.processStableItemGroupList(category: .stickers, itemGroups: stickers.contentItemGroups), itemContentUniqueId: stickers.itemContentUniqueId, emptySearchResults: nil, searchState: stickers.searchState)
},
gifs: inputData.gifs,
availableGifSearchEmojies: inputData.availableGifSearchEmojies
@ -2503,7 +2521,7 @@ public final class EmojiContentPeekBehaviorImpl: EmojiContentPeekBehavior {
return nil
}
return (view, itemLayer.convert(itemLayer.bounds, to: view.layer), StickerPreviewPeekContent(account: context.account, theme: presentationData.theme, strings: presentationData.strings, item: .pack(file), isLocked: isLocked, menu: menuItems, openPremiumIntro: {
return (view, itemLayer.convert(itemLayer.bounds, to: view.layer), StickerPreviewPeekContent(context: context, theme: presentationData.theme, strings: presentationData.strings, item: .pack(file), isLocked: isLocked, menu: menuItems, openPremiumIntro: {
guard let strongSelf = self, let interaction = strongSelf.interaction else {
return
}
@ -2632,7 +2650,7 @@ public final class EmojiContentPeekBehaviorImpl: EmojiContentPeekBehavior {
return nil
}
return (view, itemLayer.convert(itemLayer.bounds, to: view.layer), StickerPreviewPeekContent(account: context.account, theme: presentationData.theme, strings: presentationData.strings, item: .pack(file), isLocked: isLocked && !isStarred, menu: menuItems, openPremiumIntro: {
return (view, itemLayer.convert(itemLayer.bounds, to: view.layer), StickerPreviewPeekContent(context: context, theme: presentationData.theme, strings: presentationData.strings, item: .pack(file), isLocked: isLocked && !isStarred, menu: menuItems, openPremiumIntro: {
guard let strongSelf = self, let interaction = strongSelf.interaction else {
return
}

View File

@ -371,7 +371,7 @@ public final class EmojiStatusSelectionController: ViewController {
} else {
strongSelf.stableEmptyResultEmoji = nil
}
emojiContent = emojiContent.withUpdatedItemGroups(panelItemGroups: emojiContent.panelItemGroups, contentItemGroups: emojiSearchResult.groups, itemContentUniqueId: emojiSearchResult.id, emptySearchResults: emptySearchResults)
emojiContent = emojiContent.withUpdatedItemGroups(panelItemGroups: emojiContent.panelItemGroups, contentItemGroups: emojiSearchResult.groups, itemContentUniqueId: emojiSearchResult.id, emptySearchResults: emptySearchResults, searchState: .active)
} else {
strongSelf.stableEmptyResultEmoji = nil
}

View File

@ -1330,6 +1330,15 @@ private final class GroupEmbeddedView: UIScrollView, UIScrollViewDelegate, Pager
self.layer.addSublayer(itemLayer)
}
switch item.tintMode {
case .accent:
itemLayer.layerTintColor = theme.list.itemAccentColor.cgColor
case .primary:
itemLayer.layerTintColor = theme.list.itemPrimaryTextColor.cgColor
case .none:
itemLayer.layerTintColor = nil
}
let itemFrame = itemLayout.frame(at: index)
itemLayer.frame = itemFrame
@ -1708,17 +1717,24 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
if case .ended = recognizer.state {
let location = recognizer.location(in: self)
if self.backIconView.frame.contains(location) {
if let placeholderContentView = self.placeholderContent.view as? EmojiSearchSearchBarComponent.View {
placeholderContentView.clearSelection(dispatchEvent : true)
}
self.clearCategorySearch()
} else {
self.activateTextInput()
}
}
}
func clearCategorySearch() {
if let placeholderContentView = self.placeholderContent.view as? EmojiSearchSearchBarComponent.View {
placeholderContentView.clearSelection(dispatchEvent : true)
}
}
private func activateTextInput() {
if self.textField == nil, let textFrame = self.textFrame, self.params?.canFocus == true {
guard let params = self.params else {
return
}
if self.textField == nil, let textFrame = self.textFrame, params.canFocus == true {
let backgroundFrame = self.backgroundLayer.frame
let textFieldFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textFrame.minX, height: backgroundFrame.height))
@ -1730,9 +1746,11 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged)
}
self.currentPresetSearchTerm = nil
if let placeholderContentView = self.placeholderContent.view as? EmojiSearchSearchBarComponent.View {
placeholderContentView.clearSelection(dispatchEvent: false)
if params.canFocus {
self.currentPresetSearchTerm = nil
if let placeholderContentView = self.placeholderContent.view as? EmojiSearchSearchBarComponent.View {
placeholderContentView.clearSelection(dispatchEvent: false)
}
}
self.activated(true)
@ -1968,9 +1986,9 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
self.currentPresetSearchTerm = term
if shouldChangeActivation {
self.update(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
if let term {
self.update(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
self.updateQuery(.category(value: term))
self.activated(false)
} else {
@ -2049,6 +2067,7 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
cancelButtonTitleComponentView.isUserInteractionEnabled = false
}
transition.setFrame(view: cancelButtonTitleComponentView, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX + cancelButtonSpacing, y: floor((size.height - cancelTextSize.height) / 2.0)), size: cancelTextSize))
transition.setAlpha(view: cancelButtonTitleComponentView, alpha: isActiveWithText ? 1.0 : 0.0)
}
if let cancelButtonTintTitleComponentView = self.cancelButtonTintTitle.view {
if cancelButtonTintTitleComponentView.superview == nil {
@ -2056,6 +2075,7 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
cancelButtonTintTitleComponentView.isUserInteractionEnabled = false
}
transition.setFrame(view: cancelButtonTintTitleComponentView, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX + cancelButtonSpacing, y: floor((size.height - cancelTextSize.height) / 2.0)), size: cancelTextSize))
transition.setAlpha(view: cancelButtonTintTitleComponentView, alpha: isActiveWithText ? 1.0 : 0.0)
}
var hasText = false
@ -2103,9 +2123,9 @@ private final class EmptySearchResultsView: UIView {
func update(context: AccountContext, theme: PresentationTheme, useOpaqueTheme: Bool, text: String, file: TelegramMediaFile?, size: CGSize, searchInitiallyHidden: Bool, transition: Transition) {
let titleColor: UIColor
if useOpaqueTheme {
titleColor = theme.chat.inputMediaPanel.panelContentControlOpaqueOverlayColor
titleColor = theme.chat.inputMediaPanel.panelContentOpaqueSearchOverlayColor
} else {
titleColor = theme.chat.inputMediaPanel.panelContentControlVibrantOverlayColor
titleColor = theme.chat.inputMediaPanel.panelContentVibrantSearchOverlayColor
}
let iconSize: CGSize
@ -2513,6 +2533,12 @@ public final class EmojiPagerContentComponent: Component {
case detailed
}
public enum SearchState {
case empty
case searching
case active
}
public final class EmptySearchResults: Equatable {
public let text: String
public let iconFile: TelegramMediaFile?
@ -2543,6 +2569,7 @@ public final class EmojiPagerContentComponent: Component {
public let contentItemGroups: [ItemGroup]
public let itemLayoutType: ItemLayoutType
public let itemContentUniqueId: AnyHashable?
public let searchState: SearchState
public let warpContentsOnEdges: Bool
public let displaySearchWithPlaceholder: String?
public let searchCategories: EmojiSearchCategories?
@ -2564,6 +2591,7 @@ public final class EmojiPagerContentComponent: Component {
contentItemGroups: [ItemGroup],
itemLayoutType: ItemLayoutType,
itemContentUniqueId: AnyHashable?,
searchState: SearchState,
warpContentsOnEdges: Bool,
displaySearchWithPlaceholder: String?,
searchCategories: EmojiSearchCategories?,
@ -2584,6 +2612,7 @@ public final class EmojiPagerContentComponent: Component {
self.contentItemGroups = contentItemGroups
self.itemLayoutType = itemLayoutType
self.itemContentUniqueId = itemContentUniqueId
self.searchState = searchState
self.warpContentsOnEdges = warpContentsOnEdges
self.displaySearchWithPlaceholder = displaySearchWithPlaceholder
self.searchCategories = searchCategories
@ -2595,7 +2624,7 @@ public final class EmojiPagerContentComponent: Component {
self.selectedItems = selectedItems
}
public func withUpdatedItemGroups(panelItemGroups: [ItemGroup], contentItemGroups: [ItemGroup], itemContentUniqueId: AnyHashable?, emptySearchResults: EmptySearchResults?) -> EmojiPagerContentComponent {
public func withUpdatedItemGroups(panelItemGroups: [ItemGroup], contentItemGroups: [ItemGroup], itemContentUniqueId: AnyHashable?, emptySearchResults: EmptySearchResults?, searchState: SearchState) -> EmojiPagerContentComponent {
return EmojiPagerContentComponent(
id: self.id,
context: self.context,
@ -2607,6 +2636,7 @@ public final class EmojiPagerContentComponent: Component {
contentItemGroups: contentItemGroups,
itemLayoutType: self.itemLayoutType,
itemContentUniqueId: itemContentUniqueId,
searchState: searchState,
warpContentsOnEdges: self.warpContentsOnEdges,
displaySearchWithPlaceholder: self.displaySearchWithPlaceholder,
searchCategories: self.searchCategories,
@ -2650,6 +2680,12 @@ public final class EmojiPagerContentComponent: Component {
if lhs.itemLayoutType != rhs.itemLayoutType {
return false
}
if lhs.itemContentUniqueId != rhs.itemContentUniqueId {
return false
}
if lhs.searchState != rhs.searchState {
return false
}
if lhs.warpContentsOnEdges != rhs.warpContentsOnEdges {
return false
}
@ -2662,6 +2698,9 @@ public final class EmojiPagerContentComponent: Component {
if lhs.searchInitiallyHidden != rhs.searchInitiallyHidden {
return false
}
if lhs.searchAlwaysActive != rhs.searchAlwaysActive {
return false
}
if lhs.searchIsPlaceholderOnly != rhs.searchIsPlaceholderOnly {
return false
}
@ -4313,6 +4352,12 @@ public final class EmojiPagerContentComponent: Component {
guard let component = self.component, let pagerEnvironment = self.pagerEnvironment, let itemLayout = self.itemLayout else {
return
}
if self.isSearchActivated {
self.visibleSearchHeader?.clearCategorySearch()
return
}
for groupIndex in 0 ..< itemLayout.itemGroupLayouts.count {
let group = itemLayout.itemGroupLayouts[groupIndex]
@ -5044,7 +5089,7 @@ public final class EmojiPagerContentComponent: Component {
scrollView.layer.removeAllAnimations()
}
if self.isSearchActivated, let component = self.component, !component.searchAlwaysActive, let visibleSearchHeader = self.visibleSearchHeader, visibleSearchHeader.currentPresetSearchTerm == nil {
if self.isSearchActivated, let component = self.component, component.searchState == .empty, !component.searchAlwaysActive, let visibleSearchHeader = self.visibleSearchHeader, visibleSearchHeader.currentPresetSearchTerm == nil {
scrollView.isScrollEnabled = false
DispatchQueue.main.async {
scrollView.isScrollEnabled = true
@ -6024,6 +6069,9 @@ public final class EmojiPagerContentComponent: Component {
guard let strongSelf = self else {
return nil
}
if !strongSelf.scrollViewClippingView.bounds.contains(strongSelf.convert(point, to: strongSelf.scrollViewClippingView)) {
return nil
}
guard let item = strongSelf.item(atPoint: point), let itemLayer = strongSelf.visibleItemLayers[item.1], let file = item.0.itemFile else {
return nil
}
@ -6459,12 +6507,15 @@ public final class EmojiPagerContentComponent: Component {
strongSelf.isSearchActivated = false
strongSelf.pagerEnvironment?.onWantsExclusiveModeUpdated(false)
if strongSelf.component?.searchInitiallyHidden == false {
if !isFirstResponder {
strongSelf.component?.inputInteractionHolder.inputInteraction?.requestUpdate(.easeInOut(duration: 0.2))
}
if !isFirstResponder {
strongSelf.component?.inputInteractionHolder.inputInteraction?.requestUpdate(
Transition(animation: .curve(duration: 0.4, curve: .spring)))
} else {
strongSelf.component?.inputInteractionHolder.inputInteraction?.requestUpdate(.immediate)
DispatchQueue.main.async {
self?.component?.inputInteractionHolder.inputInteraction?.requestUpdate(
Transition(animation: .curve(duration: 0.4, curve: .spring)))
}
}
}
}, updateQuery: { [weak self] query in
@ -6550,7 +6601,11 @@ public final class EmojiPagerContentComponent: Component {
visibleEmptySearchResultsView = EmptySearchResultsView(frame: CGRect())
self.visibleEmptySearchResultsView = visibleEmptySearchResultsView
self.addSubview(visibleEmptySearchResultsView)
self.mirrorContentClippingView?.addSubview(visibleEmptySearchResultsView.tintContainerView)
if let mirrorContentClippingView = self.mirrorContentClippingView {
mirrorContentClippingView.addSubview(visibleEmptySearchResultsView.tintContainerView)
} else if let vibrancyEffectView = self.vibrancyEffectView {
vibrancyEffectView.contentView.addSubview(visibleEmptySearchResultsView.tintContainerView)
}
}
let emptySearchResultsSize = CGSize(width: availableSize.width, height: availableSize.height - itemLayout.searchInsets.top - itemLayout.searchHeight)
visibleEmptySearchResultsView.update(
@ -7550,6 +7605,7 @@ public final class EmojiPagerContentComponent: Component {
contentItemGroups: allItemGroups,
itemLayoutType: .compact,
itemContentUniqueId: nil,
searchState: .empty,
warpContentsOnEdges: isReactionSelection || isStatusSelection || isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection,
displaySearchWithPlaceholder: displaySearchWithPlaceholder,
searchCategories: searchCategories,
@ -8071,6 +8127,7 @@ public final class EmojiPagerContentComponent: Component {
contentItemGroups: allItemGroups,
itemLayoutType: .detailed,
itemContentUniqueId: nil,
searchState: .empty,
warpContentsOnEdges: isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection,
displaySearchWithPlaceholder: hasSearch ? strings.StickersSearch_SearchStickersPlaceholder : nil,
searchCategories: searchCategories,

View File

@ -391,6 +391,7 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode
contentItemGroups: self.itemGroups,
itemLayoutType: .compact,
itemContentUniqueId: "main",
searchState: .empty,
warpContentsOnEdges: false,
displaySearchWithPlaceholder: "Search Emoji",
searchCategories: nil,
@ -410,7 +411,7 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode
iconFile: nil
)
}
emojiContent = emojiContent.withUpdatedItemGroups(panelItemGroups: emojiContent.panelItemGroups, contentItemGroups: emojiSearchResult.groups, itemContentUniqueId: emojiSearchResult.id, emptySearchResults: emptySearchResults)
emojiContent = emojiContent.withUpdatedItemGroups(panelItemGroups: emojiContent.panelItemGroups, contentItemGroups: emojiSearchResult.groups, itemContentUniqueId: emojiSearchResult.id, emptySearchResults: emptySearchResults, searchState: .active)
}
let _ = self.keyboardView.update(

View File

@ -131,9 +131,16 @@ final class EmojiSearchSearchBarComponent: Component {
self.textFrame = CGRect(origin: CGPoint(x: self.leftInset, y: floor((containerSize.height - textSize.height) * 0.5)), size: textSize)
self.itemStartX = self.textFrame.maxX + self.textSpacing
let itemsWidth: CGFloat = self.itemSize.width * CGFloat(self.itemCount) + self.itemSpacing * CGFloat(max(0, self.itemCount - 1))
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)
var itemStartX = self.textFrame.maxX + self.textSpacing
if itemStartX + itemsWidth + self.rightInset < containerSize.width {
itemStartX = containerSize.width - self.rightInset - itemsWidth
}
self.itemStartX = itemStartX
self.contentSize = CGSize(width: self.itemStartX + itemsWidth + self.rightInset, height: containerSize.height)
}
func visibleItems(for rect: CGRect) -> Range<Int>? {
@ -310,7 +317,7 @@ final class EmojiSearchSearchBarComponent: Component {
}
} else {
let transition = Transition(animation: .curve(duration: 0.4, curve: .spring))
transition.setBounds(view: self.scrollView, bounds: CGRect(origin: CGPoint(), size: self.scrollView.bounds.size))
transition.setBoundsOrigin(view: self.scrollView, origin: CGPoint())
self.updateScrolling(transition: transition, fromScrolling: false)
//self.scrollView.setContentOffset(CGPoint(), animated: true)
@ -329,7 +336,7 @@ final class EmojiSearchSearchBarComponent: Component {
self.selectedItem = nil
let transition = Transition(animation: .curve(duration: 0.4, curve: .spring))
transition.setBounds(view: self.scrollView, bounds: CGRect(origin: CGPoint(), size: self.scrollView.bounds.size))
transition.setBoundsOrigin(view: self.scrollView, origin: CGPoint())
self.updateScrolling(transition: transition, fromScrolling: false)
self.state?.updated(transition: transition)
@ -544,7 +551,7 @@ final class EmojiSearchSearchBarComponent: Component {
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))
transition.setBoundsOrigin(view: self.scrollView, origin: CGPoint())
}
if self.scrollView.contentSize != itemLayout.contentSize {
self.scrollView.contentSize = itemLayout.contentSize

View File

@ -98,7 +98,15 @@ private class GifVideoLayer: AVSampleBufferDisplayLayer {
}
override init(layer: Any) {
preconditionFailure()
guard let layer = layer as? GifVideoLayer else {
preconditionFailure()
}
self.context = layer.context
self.userLocation = layer.userLocation
self.file = layer.file
super.init(layer: layer)
}
required init?(coder: NSCoder) {
@ -375,6 +383,13 @@ public final class GifPagerContentComponent: Component {
}
}
override init(layer: Any) {
self.item = nil
self.onUpdateDisplayPlaceholder = { _, _ in }
super.init(layer: layer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

View File

@ -106,7 +106,7 @@ public final class LottieComponent: Component {
if delay != 0.0 {
self.isHidden = true
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.08, execute: { [weak self] in
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: { [weak self] in
guard let self else {
return
}

View File

@ -43,13 +43,13 @@ final class DataCategoriesComponent: Component {
let theme: PresentationTheme
let strings: PresentationStrings
let categories: [CategoryData]
let toggleCategoryExpanded: (DataUsageScreenComponent.Category) -> Void
let toggleCategoryExpanded: ((DataUsageScreenComponent.Category) -> Void)?
init(
theme: PresentationTheme,
strings: PresentationStrings,
categories: [CategoryData],
toggleCategoryExpanded: @escaping (DataUsageScreenComponent.Category) -> Void
toggleCategoryExpanded: ((DataUsageScreenComponent.Category) -> Void)?
) {
self.theme = theme
self.strings = strings
@ -138,11 +138,11 @@ final class DataCategoriesComponent: Component {
category: category,
isExpanded: category.isExpanded,
hasNext: i != component.categories.count - 1,
action: { [weak self] key in
action: component.toggleCategoryExpanded == nil ? nil : { [weak self] key in
guard let self, let component = self.component else {
return
}
component.toggleCategoryExpanded(key)
component.toggleCategoryExpanded?(key)
}
)),
environment: {},

View File

@ -52,7 +52,7 @@ private final class SubItemComponent: Component {
return true
}
class View: HighlightTrackingButton {
class View: UIView {
private let iconView: UIImageView
private let title = ComponentView<Empty>()
private let titleValue = ComponentView<Empty>()
@ -74,7 +74,7 @@ private final class SubItemComponent: Component {
self.addSubview(self.iconView)
self.highligthedChanged = { [weak self] isHighlighted in
/*self.highligthedChanged = { [weak self] isHighlighted in
guard let self, let component = self.component, let highlightBackgroundFrame = self.highlightBackgroundFrame else {
return
}
@ -103,7 +103,7 @@ private final class SubItemComponent: Component {
}
}
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
self.isUserInteractionEnabled = false
self.isEnabled = false*/
}
required init?(coder: NSCoder) {
@ -243,7 +243,7 @@ final class DataCategoryItemComponent: Component {
let category: DataCategoriesComponent.CategoryData
let isExpanded: Bool
let hasNext: Bool
let action: (DataUsageScreenComponent.Category) -> Void
let action: ((DataUsageScreenComponent.Category) -> Void)?
init(
theme: PresentationTheme,
@ -251,7 +251,7 @@ final class DataCategoryItemComponent: Component {
category: DataCategoriesComponent.CategoryData,
isExpanded: Bool,
hasNext: Bool,
action: @escaping (DataUsageScreenComponent.Category) -> Void
action: ((DataUsageScreenComponent.Category) -> Void)?
) {
self.theme = theme
self.strings = strings
@ -350,14 +350,14 @@ final class DataCategoryItemComponent: Component {
guard let component = self.component else {
return
}
component.action(component.category.key)
component.action?(component.category.key)
}
@objc private func checkPressed() {
guard let component = self.component else {
return
}
component.action(component.category.key)
component.action?(component.category.key)
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
@ -597,6 +597,8 @@ final class DataCategoryItemComponent: Component {
transition.setFrame(view: self.subcategoryClippingContainer, frame: CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: height)))
self.isEnabled = component.action != nil
return CGSize(width: availableSize.width, height: height)
}
}

View File

@ -468,7 +468,9 @@ 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 {
if backButtonNode.alpha != navigationButtonAlpha {
backButtonNode.updateManualAlpha(alpha: navigationButtonAlpha, transition: animatedTransition.containedViewLayoutTransition)
/*if backButtonNode.alpha != navigationButtonAlpha {
if backButtonNode.isHidden {
backButtonNode.alpha = 0.0
backButtonNode.isHidden = false
@ -480,7 +482,7 @@ final class DataUsageScreenComponent: Component {
}
}
})
}
}*/
}
}
}
@ -1035,8 +1037,7 @@ final class DataUsageScreenComponent: Component {
theme: environment.theme,
strings: environment.strings,
categories: totalCategories,
toggleCategoryExpanded: { _ in
}
toggleCategoryExpanded: nil
)),
environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude)

View File

@ -522,7 +522,7 @@ final class PieChartComponent: Component {
let fractionValue: Double = floor(displayValue * 100.0 * 10.0) / 10.0
let fractionString: String
if fractionValue == 0.0 {
if displayValue == 0.0 {
fractionString = ""
} else if fractionValue < 0.1 {
fractionString = "<0.1%"

View File

@ -1662,7 +1662,7 @@ final class ChatMediaInputNode: ChatInputNode {
}
}
})))
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: item, menu: menuItems, openPremiumIntro: { [weak self] in
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(context: strongSelf.context, theme: strongSelf.theme, strings: strongSelf.strings, item: item, menu: menuItems, openPremiumIntro: { [weak self] in
guard let strongSelf = self else {
return
}
@ -1819,7 +1819,7 @@ final class ChatMediaInputNode: ChatInputNode {
}
}))
)
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item.file), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems, openPremiumIntro: { [weak self] in
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(context: strongSelf.context, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item.file), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems, openPremiumIntro: { [weak self] in
guard let strongSelf = self else {
return
}

View File

@ -423,7 +423,7 @@ final class EmojisChatInputContextPanelNode: ChatInputContextPanelNode {
return nil
}
let content = StickerPreviewPeekContent(account: context.account, theme: presentationData.theme, strings: presentationData.strings, item: .pack(file), isLocked: isLocked, menu: menuItems, openPremiumIntro: { [weak self] in
let content = StickerPreviewPeekContent(context: context, theme: presentationData.theme, strings: presentationData.strings, item: .pack(file), isLocked: isLocked, menu: menuItems, openPremiumIntro: { [weak self] in
guard let self else {
return
}

View File

@ -46,8 +46,8 @@ private struct HorizontalListContextResultsChatInputContextPanelEntry: Comparabl
return lhs.index < rhs.index
}
func item(account: Account, resultSelected: @escaping (ChatContextResult, ASDisplayNode, CGRect) -> Bool) -> ListViewItem {
return HorizontalListContextResultsChatInputPanelItem(account: account, theme: self.theme, result: self.result, resultSelected: resultSelected)
func item(context: AccountContext, resultSelected: @escaping (ChatContextResult, ASDisplayNode, CGRect) -> Bool) -> ListViewItem {
return HorizontalListContextResultsChatInputPanelItem(context: context, theme: self.theme, result: self.result, resultSelected: resultSelected)
}
}
@ -69,12 +69,12 @@ private final class HorizontalListContextResultsOpaqueState {
}
}
private func preparedTransition(from fromEntries: [HorizontalListContextResultsChatInputContextPanelEntry], to toEntries: [HorizontalListContextResultsChatInputContextPanelEntry], hasMore: Bool, account: Account, resultSelected: @escaping (ChatContextResult, ASDisplayNode, CGRect) -> Bool) -> HorizontalListContextResultsChatInputContextPanelTransition {
private func preparedTransition(from fromEntries: [HorizontalListContextResultsChatInputContextPanelEntry], to toEntries: [HorizontalListContextResultsChatInputContextPanelEntry], hasMore: Bool, context: AccountContext, resultSelected: @escaping (ChatContextResult, ASDisplayNode, CGRect) -> Bool) -> HorizontalListContextResultsChatInputContextPanelTransition {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, resultSelected: resultSelected), directionHint: nil) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, resultSelected: resultSelected), directionHint: nil) }
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, resultSelected: resultSelected), directionHint: nil) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, resultSelected: resultSelected), directionHint: nil) }
return HorizontalListContextResultsChatInputContextPanelTransition(deletions: deletions, insertions: insertions, updates: updates, entryCount: toEntries.count, hasMore: hasMore)
}
@ -147,6 +147,7 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
}
var selectedItemNodeAndContent: (UIView, CGRect, PeekControllerContent)?
selectedItemNodeAndContent = nil
strongSelf.listView.forEachItemNode { itemNode in
if itemNode.frame.contains(convertedPoint), let itemNode = itemNode as? HorizontalListContextResultsChatInputPanelItemNode, let item = itemNode.item {
if case let .internalReference(internalReference) = item.result, let file = internalReference.file, file.isSticker {
@ -177,7 +178,7 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
}
})))
}
selectedItemNodeAndContent = (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(account: item.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .found(FoundStickerItem(file: file, stringRepresentations: [])), menu: menuItems, openPremiumIntro: { [weak self] in
selectedItemNodeAndContent = (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(context: item.context, theme: strongSelf.theme, strings: strongSelf.strings, item: .found(FoundStickerItem(file: file, stringRepresentations: [])), menu: menuItems, openPremiumIntro: { [weak self] in
guard let strongSelf = self else {
return
}
@ -230,7 +231,7 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
f(.default)
let _ = item.resultSelected(item.result, itemNode, itemNode.bounds)
})))
selectedItemNodeAndContent = (itemNode.view, itemNode.bounds, ChatContextResultPeekContent(account: item.account, contextResult: item.result, menu: menuItems))
selectedItemNodeAndContent = (itemNode.view, itemNode.bounds, ChatContextResultPeekContent(account: item.context.account, contextResult: item.result, menu: menuItems))
}
}
}
@ -313,7 +314,7 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
}
let firstTime = self.currentEntries == nil
let transition = preparedTransition(from: self.currentEntries ?? [], to: entries, hasMore: results.nextOffset != nil, account: self.context.account, resultSelected: { [weak self] result, node, rect in
let transition = preparedTransition(from: self.currentEntries ?? [], to: entries, hasMore: results.nextOffset != nil, context: self.context, resultSelected: { [weak self] result, node, rect in
if let strongSelf = self, let interfaceInteraction = strongSelf.interfaceInteraction {
return interfaceInteraction.sendContextResult(results, result, node, rect)
} else {

View File

@ -18,15 +18,15 @@ import SoftwareVideo
import MultiplexedVideoNode
final class HorizontalListContextResultsChatInputPanelItem: ListViewItem {
let account: Account
let context: AccountContext
let theme: PresentationTheme
let result: ChatContextResult
let resultSelected: (ChatContextResult, ASDisplayNode, CGRect) -> Bool
let selectable: Bool = true
public init(account: Account, theme: PresentationTheme, result: ChatContextResult, resultSelected: @escaping (ChatContextResult, ASDisplayNode, CGRect) -> Bool) {
self.account = account
public init(context: AccountContext, theme: PresentationTheme, result: ChatContextResult, resultSelected: @escaping (ChatContextResult, ASDisplayNode, CGRect) -> Bool) {
self.context = context
self.theme = theme
self.result = result
self.resultSelected = resultSelected
@ -265,9 +265,9 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
}
if let file = videoFile {
updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(file.resource)
updatedStatusSignal = item.context.account.postbox.mediaBox.resourceStatus(file.resource)
} else if let imageResource = imageResource {
updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(imageResource)
updatedStatusSignal = item.context.account.postbox.mediaBox.resourceStatus(imageResource)
}
case let .internalReference(internalReference):
if let image = internalReference.image {
@ -296,12 +296,12 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
if file.isVideo && file.isAnimated {
videoFile = file
imageResource = nil
updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(file.resource)
updatedStatusSignal = item.context.account.postbox.mediaBox.resourceStatus(file.resource)
} else if let imageResource = imageResource {
updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(imageResource)
updatedStatusSignal = item.context.account.postbox.mediaBox.resourceStatus(imageResource)
}
} else if let imageResource = imageResource {
updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(imageResource)
updatedStatusSignal = item.context.account.postbox.mediaBox.resourceStatus(imageResource)
}
}
@ -351,11 +351,11 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
if updatedImageResource {
if let imageResource = imageResource {
if let stickerFile = stickerFile {
updateImageSignal = chatMessageSticker(account: item.account, userLocation: .other, file: stickerFile, small: false, fetched: true)
updateImageSignal = chatMessageSticker(account: item.context.account, userLocation: .other, file: stickerFile, small: false, fetched: true)
} else {
let tmpRepresentation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(CGSize(width: fittedImageDimensions.width * 2.0, height: fittedImageDimensions.height * 2.0)), resource: imageResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [tmpRepresentation], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
updateImageSignal = chatMessagePhoto(postbox: item.account.postbox, userLocation: .other, photoReference: .standalone(media: tmpImage), synchronousLoad: true)
updateImageSignal = chatMessagePhoto(postbox: item.context.account.postbox, userLocation: .other, photoReference: .standalone(media: tmpImage), synchronousLoad: true)
}
} else {
updateImageSignal = .complete()
@ -391,7 +391,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
}
if let videoFile = videoFile {
let thumbnailLayer = SoftwareVideoThumbnailNode(account: item.account, fileReference: .standalone(media: videoFile), synchronousLoad: synchronousLoads)
let thumbnailLayer = SoftwareVideoThumbnailNode(account: item.context.account, fileReference: .standalone(media: videoFile), synchronousLoad: synchronousLoads)
thumbnailLayer.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
strongSelf.addSubnode(thumbnailLayer)
let layerHolder = takeSampleBufferLayer()
@ -399,7 +399,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
layerHolder.layer.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
strongSelf.layer.addSublayer(layerHolder.layer)
let manager = SoftwareVideoLayerFrameManager(account: item.account, userLocation: .other, userContentType: .other, fileReference: .standalone(media: videoFile), layerHolder: layerHolder)
let manager = SoftwareVideoLayerFrameManager(account: item.context.account, userLocation: .other, userContentType: .other, fileReference: .standalone(media: videoFile), layerHolder: layerHolder)
strongSelf.videoLayer = (thumbnailLayer, manager, layerHolder)
thumbnailLayer.ready = { [weak thumbnailLayer, weak manager] in
if let strongSelf = self, let thumbnailLayer = thumbnailLayer, let manager = manager {
@ -437,8 +437,8 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
}
let dimensions = animatedStickerFile.dimensions ?? PixelDimensions(width: 512, height: 512)
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0))
strongSelf.fetchDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, userLocation: .other, fileReference: stickerPackFileReference(animatedStickerFile), resource: animatedStickerFile.resource).start())
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: animatedStickerFile.resource, isVideo: animatedStickerFile.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .cached)
strongSelf.fetchDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.context.account, userLocation: .other, fileReference: stickerPackFileReference(animatedStickerFile), resource: animatedStickerFile.resource).start())
animationNode.setup(source: AnimatedStickerResourceSource(account: item.context.account, resource: animatedStickerFile.resource, isVideo: animatedStickerFile.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .cached)
}
}

View File

@ -243,7 +243,7 @@ final class HorizontalStickersChatContextPanelNode: ChatInputContextPanelNode {
}
}))
]
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item.file), menu: menuItems, openPremiumIntro: { [weak self] in
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(context: strongSelf.context, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item.file), menu: menuItems, openPremiumIntro: { [weak self] in
guard let strongSelf = self else {
return
}

View File

@ -198,7 +198,7 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
}
}))
)
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item.file), menu: menuItems, openPremiumIntro: { [weak self] in
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(context: strongSelf.context, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item.file), menu: menuItems, openPremiumIntro: { [weak self] in
guard let strongSelf = self, let controllerInteraction = strongSelf.getControllerInteraction?() else {
return
}

View File

@ -199,7 +199,7 @@ final class StickersChatInputContextPanelNode: ChatInputContextPanelNode {
}
}))
]
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item.file), menu: menuItems, openPremiumIntro: { [weak self] in
return (itemNode.view, itemNode.bounds, StickerPreviewPeekContent(context: strongSelf.context, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item.file), menu: menuItems, openPremiumIntro: { [weak self] in
guard let strongSelf = self else {
return
}