diff --git a/submodules/ManagedAnimationNode/Sources/ManagedAnimationNode.swift b/submodules/ManagedAnimationNode/Sources/ManagedAnimationNode.swift index 8e0607511a..73fd720359 100644 --- a/submodules/ManagedAnimationNode/Sources/ManagedAnimationNode.swift +++ b/submodules/ManagedAnimationNode/Sources/ManagedAnimationNode.swift @@ -38,13 +38,15 @@ public final class ManagedAnimationState { guard let path = item.source.path else { return nil } - guard let data = try? Data(contentsOf: URL(fileURLWithPath: path)) else { + guard var data = try? Data(contentsOf: URL(fileURLWithPath: path)) else { return nil } - guard let unpackedData = TGGUnzipData(data, 5 * 1024 * 1024) else { - return nil + if path.hasSuffix(".json") { + + } else if let unpackedData = TGGUnzipData(data, 5 * 1024 * 1024) { + data = unpackedData } - guard let instance = LottieInstance(data: unpackedData, fitzModifier: .none, cacheKey: item.source.cacheKey) else { + guard let instance = LottieInstance(data: data, fitzModifier: .none, cacheKey: item.source.cacheKey) else { return nil } resolvedInstance = instance @@ -92,7 +94,10 @@ public enum ManagedAnimationSource: Equatable { var path: String? { switch self { case let .local(name): - return getAppBundle().path(forResource: name, ofType: "tgs") + if let tgsPath = getAppBundle().path(forResource: name, ofType: "tgs") { + return tgsPath + } + return getAppBundle().path(forResource: name, ofType: "json") case let .resource(account, resource): return account.postbox.mediaBox.completedResourcePath(resource._asResource()) } diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift index 80eafddf00..8668e51e92 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift @@ -153,7 +153,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode { if self.animationNode == nil { let animationNode = AnimatedStickerNode() self.animationNode = animationNode - self.addSubnode(animationNode) + self.insertSubnode(animationNode, aboveSubnode: self.imageNode) animationNode.started = { [weak self] in self?.imageNode.isHidden = true self?.removePlaceholder(animated: false) @@ -196,16 +196,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode { let bounds = self.bounds let boundsSide = min(bounds.size.width - 14.0, bounds.size.height - 14.0) let boundingSize = CGSize(width: boundsSide, height: boundsSide) - - if let placeholderNode = self.placeholderNode { - let placeholderFrame = CGRect(origin: CGPoint(x: floor((bounds.width - boundingSize.width) / 2.0), y: floor((bounds.height - boundingSize.height) / 2.0)), size: boundingSize) - placeholderNode.frame = bounds - - if let theme = self.theme, let (_, stickerItem) = self.currentState, let item = stickerItem { - placeholderNode.update(backgroundColor: theme.list.itemBlocksBackgroundColor, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), data: item.file.immediateThumbnailData, size: placeholderFrame.size) - } - } - + if let (_, item) = self.currentState { if let item = item, let dimensions = item.file.dimensions?.cgSize { let imageSize = dimensions.aspectFitted(boundingSize) @@ -218,6 +209,18 @@ final class StickerPackPreviewGridItemNode: GridItemNode { } } } + + if let placeholderNode = self.placeholderNode { + let imageFrame = self.imageNode.frame + +// let placeholderFrame = CGRect(origin: CGPoint(x: floor((bounds.width - boundingSize.width) / 2.0), y: floor((bounds.height - boundingSize.height) / 2.0)), size: boundingSize) + let placeholderFrame = imageFrame + placeholderNode.frame = imageFrame + + if let theme = self.theme, let (_, stickerItem) = self.currentState, let item = stickerItem { + placeholderNode.update(backgroundColor: theme.list.itemBlocksBackgroundColor, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), data: item.file.immediateThumbnailData, size: placeholderFrame.size) + } + } } override func updateAbsoluteRect(_ absoluteRect: CGRect, within containerSize: CGSize) { diff --git a/submodules/TelegramUI/Resources/Animations/anim_moredots.json b/submodules/TelegramUI/Resources/Animations/anim_moredots.json new file mode 100644 index 0000000000..348ddc10fd --- /dev/null +++ b/submodules/TelegramUI/Resources/Animations/anim_moredots.json @@ -0,0 +1 @@ +{"v":"5.8.1","fr":60,"ip":0,"op":46,"w":300,"h":300,"nm":"more","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"more","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[150,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-55.228,0],[0,55.229],[55.229,0],[0,-55.228]],"o":[[55.229,0],[0,-55.228],[-55.228,0],[0,55.229]],"v":[[23.5,123.5],[123.5,23.5],[23.5,-76.5],[-76.5,23.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[126.5,126.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"c","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.284,0],[0,8.284],[8.284,0],[0,-8.284]],"o":[[8.284,0],[0,-8.284],[-8.284,0],[0,8.284]],"v":[[50,15],[65,0],[50,-15],[35,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[200,150],"to":[0,-5],"ti":[0,-2]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":20,"s":[200,120],"to":[0,2],"ti":[0,-5]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":30,"s":[200,162],"to":[0,5],"ti":[0,2]},{"t":40,"s":[200,150]}],"ix":2},"a":{"a":0,"k":[50,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":15,"s":[100,100]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":25,"s":[125,125]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":35,"s":[95,95]},{"t":45,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"3","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.284,0],[0,8.284],[8.284,0],[0,-8.284]],"o":[[8.284,0],[0,-8.284],[-8.284,0],[0,8.284]],"v":[[0.5,15],[15.5,0],[0.5,-15],[-14.5,0]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":5,"s":[150,150],"to":[0,-5],"ti":[0,-2]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":15,"s":[150,120],"to":[0,2],"ti":[0,-5]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":25,"s":[150,162],"to":[0,5],"ti":[0,2]},{"t":35,"s":[150,150]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":10,"s":[100,100]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":20,"s":[125,125]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":30,"s":[95,95]},{"t":40,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"2","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.284,0],[0,8.284],[8.284,0],[0,-8.284]],"o":[[8.284,0],[0,-8.284],[-8.284,0],[0,8.284]],"v":[[-49,15],[-34,0],[-49,-15],[-64,0]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":0,"s":[100,150],"to":[0,-5],"ti":[0,-2]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":10,"s":[100,120],"to":[0,2],"ti":[0,-5]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":20,"s":[100,162],"to":[0,5],"ti":[0,2]},{"t":30,"s":[100,150]}],"ix":2},"a":{"a":0,"k":[-50,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":5,"s":[100,100]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":15,"s":[125,125]},{"i":{"x":[0.5,0.5],"y":[1,1]},"o":{"x":[0.5,0.5],"y":[0,0]},"t":25,"s":[95,95]},{"t":35,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"1","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":-35,"op":47,"st":-38,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/submodules/TelegramUI/Resources/Animations/anim_moretosearch.json b/submodules/TelegramUI/Resources/Animations/anim_moretosearch.json new file mode 100644 index 0000000000..6ef1380743 --- /dev/null +++ b/submodules/TelegramUI/Resources/Animations/anim_moretosearch.json @@ -0,0 +1 @@ +{"v":"5.8.1","fr":60,"ip":0,"op":90,"w":300,"h":300,"nm":"more_to_search","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"more and search","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2,"l":2},"a":{"a":0,"k":[150,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[28.25,28.25],[-28.25,-28.25]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[25]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":89,"s":[0]},{"t":102,"s":[25]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[25]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":89,"s":[100]},{"t":102,"s":[25]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22.5,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[211.75,211.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":30,"s":[100]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":97,"s":[100]},{"t":102,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"h","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[-55.228,0],[0,55.229],[55.229,0],[0,-55.228]],"o":[[55.229,0],[0,-55.228],[-55.228,0],[0,55.229]],"v":[[23.5,123.5],[123.5,23.5],[23.5,-76.5],[-76.5,23.5]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.6,"y":0},"t":89,"s":[{"i":[[-42.25,0],[0,42.25],[42.25,0],[0,-42.25]],"o":[[42.25,0],[0,-42.25],[-42.25,0],[0,42.25]],"v":[[0,76.5],[76.5,0],[0,-76.5],[-76.5,0]],"c":true}]},{"t":102,"s":[{"i":[[-55.228,0],[0,55.229],[55.229,0],[0,-55.228]],"o":[[55.229,0],[0,-55.228],[-55.228,0],[0,55.229]],"v":[[23.5,123.5],[123.5,23.5],[23.5,-76.5],[-76.5,23.5]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[126.5,126.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"c","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[-8.284,0],[0,8.284],[8.284,0],[0,-8.284]],"o":[[8.284,0],[0,-8.284],[-8.284,0],[0,8.284]],"v":[[50,15],[65,0],[50,-15],[35,0]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.6,"y":0},"t":89,"s":[{"i":[[-6.337,0],[0,6.337],[6.337,0],[0,-6.337]],"o":[[6.337,0],[0,-6.337],[-6.337,0],[0,6.337]],"v":[[14.75,-12.025],[26.225,-23.5],[14.75,-34.975],[3.275,-23.5]],"c":true}]},{"t":102,"s":[{"i":[[-8.284,0],[0,8.284],[8.284,0],[0,-8.284]],"o":[[8.284,0],[0,-8.284],[-8.284,0],[0,8.284]],"v":[[50,15],[65,0],[50,-15],[35,0]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[-8.284,0],[0,8.284],[8.284,0],[0,-8.284]],"o":[[8.284,0],[0,-8.284],[-8.284,0],[0,8.284]],"v":[[0.5,15],[15.5,0],[0.5,-15],[-14.5,0]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.6,"y":0},"t":89,"s":[{"i":[[-6.337,0],[0,6.337],[6.338,0],[0,-6.337]],"o":[[6.338,0],[0,-6.337],[-6.337,0],[0,6.337]],"v":[[-23.118,-12.025],[-11.643,-23.5],[-23.118,-34.975],[-34.592,-23.5]],"c":true}]},{"t":102,"s":[{"i":[[-8.284,0],[0,8.284],[8.284,0],[0,-8.284]],"o":[[8.284,0],[0,-8.284],[-8.284,0],[0,8.284]],"v":[[0.5,15],[15.5,0],[0.5,-15],[-14.5,0]],"c":true}]}],"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[-8.284,0],[0,8.284],[8.284,0],[0,-8.284]],"o":[[8.284,0],[0,-8.284],[-8.284,0],[0,8.284]],"v":[[-49,15],[-34,0],[-49,-15],[-64,0]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.6,"y":0},"t":89,"s":[{"i":[[-6.337,0],[0,6.337],[6.337,0],[0,-6.337]],"o":[[6.337,0],[0,-6.337],[-6.337,0],[0,6.337]],"v":[[-60.985,-12.025],[-49.51,-23.5],[-60.985,-34.975],[-72.46,-23.5]],"c":true}]},{"t":102,"s":[{"i":[[-8.284,0],[0,8.284],[8.284,0],[0,-8.284]],"o":[[8.284,0],[0,-8.284],[-8.284,0],[0,8.284]],"v":[[-49,15],[-34,0],[-49,-15],[-64,0]],"c":true}]}],"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[150,150],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[100]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":89,"s":[0]},{"t":102,"s":[100]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"e","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":-10,"op":92,"st":-10,"bm":0}],"markers":[]} diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 3113bc823c..4ca957222e 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -7573,7 +7573,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G |> switchToLatest |> deliverOnMainQueue).start(next: { [weak self] added in if let strongSelf = self { - strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: stickerFile, text: added ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites), elevatedLayout: false, action: { _ in return false }), in: .current) + strongSelf.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: stickerFile, text: added ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites), elevatedLayout: true, action: { _ in return false }), with: nil) } }) } diff --git a/submodules/TelegramUI/Sources/ForwardAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ForwardAccessoryPanelNode.swift index 003a728897..11c2158590 100644 --- a/submodules/TelegramUI/Sources/ForwardAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ForwardAccessoryPanelNode.swift @@ -21,6 +21,9 @@ func textStringForForwardedMessage(_ message: Message, strings: PresentationStri case _ as TelegramMediaImage: return (strings.Message_Photo, true) case let file as TelegramMediaFile: + if file.isVideoSticker || file.isAnimatedSticker { + return (strings.Message_Sticker, true) + } var fileName: String = strings.Message_File for attribute in file.attributes { switch attribute { @@ -52,9 +55,6 @@ func textStringForForwardedMessage(_ message: Message, strings: PresentationStri break } } - if file.isAnimatedSticker { - return (strings.Message_Sticker, true) - } return (fileName, true) case _ as TelegramMediaContact: return (strings.Message_Contact, true) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index 4c3f25c74d..8520b31a72 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -21,6 +21,7 @@ import TelegramUIPreferences import PeerInfoAvatarListNode import AnimationUI import ContextUI +import ManagedAnimationNode enum PeerInfoHeaderButtonKey: Hashable { case message @@ -920,13 +921,99 @@ final class PeerInfoAvatarListNode: ASDisplayNode { } } +private enum MoreIconNodeState: Equatable { + case more + case search + case moreToSearch(Float) +} + +private final class MoreIconNode: ManagedAnimationNode { + private let duration: Double = 0.21 + private var iconState: MoreIconNodeState = .more + + init() { + super.init(size: CGSize(width: 30.0, height: 30.0)) + + self.trackTo(item: ManagedAnimationItem(source: .local("anim_moretosearch"), frames: .range(startFrame: 0, endFrame: 0), duration: 0.0)) + } + + func play() { + if case .more = self.iconState { + self.trackTo(item: ManagedAnimationItem(source: .local("anim_moredots"), frames: .range(startFrame: 0, endFrame: 46), duration: 0.76)) + } + } + + func enqueueState(_ state: MoreIconNodeState, animated: Bool) { + guard self.iconState != state else { + return + } + + let previousState = self.iconState + self.iconState = state + + let source = ManagedAnimationSource.local("anim_moretosearch") + + let totalLength: Int = 90 + if animated { + switch previousState { + case .more: + switch state { + case .more: + break + case .search: + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: 0, endFrame: totalLength), duration: self.duration)) + case let .moreToSearch(progress): + let frame = Int(progress * Float(totalLength)) + let duration = self.duration * Double(progress) + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: 0, endFrame: frame), duration: duration)) + } + case .search: + switch state { + case .more: + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: totalLength, endFrame: 0), duration: self.duration)) + case .search: + break + case let .moreToSearch(progress): + let frame = Int(progress * Float(totalLength)) + let duration = self.duration * Double((1.0 - progress)) + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: totalLength, endFrame: frame), duration: duration)) + } + case let .moreToSearch(currentProgress): + let currentFrame = Int(currentProgress * Float(totalLength)) + switch state { + case .more: + let duration = self.duration * Double(currentProgress) + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: currentFrame, endFrame: 0), duration: duration)) + case .search: + let duration = self.duration * (1.0 - Double(currentProgress)) + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: currentFrame, endFrame: totalLength), duration: duration)) + case let .moreToSearch(progress): + let frame = Int(progress * Float(totalLength)) + let duration = self.duration * Double(abs(currentProgress - progress)) + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: currentFrame, endFrame: frame), duration: duration)) + } + } + } else { + switch state { + case .more: + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: 0, endFrame: 0), duration: 0.0)) + case .search: + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: totalLength, endFrame: totalLength), duration: 0.0)) + case let .moreToSearch(progress): + let frame = Int(progress * Float(totalLength)) + self.trackTo(item: ManagedAnimationItem(source: source, frames: .range(startFrame: frame, endFrame: frame), duration: 0.0)) + } + } + } +} + final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { let containerNode: ContextControllerSourceNode let contextSourceNode: ContextReferenceContentNode private let regularTextNode: ImmediateTextNode private let whiteTextNode: ImmediateTextNode private let iconNode: ASImageNode - private var animationNode: AnimationNode? + private var animationNode: MoreIconNode? private var key: PeerInfoHeaderNavigationButtonKey? private var theme: PresentationTheme? @@ -982,11 +1069,13 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { } @objc private func pressed() { + self.animationNode?.play() self.action?(self.contextSourceNode, nil) } func update(key: PeerInfoHeaderNavigationButtonKey, presentationData: PresentationData, height: CGFloat) -> CGSize { let textSize: CGSize + let isFirstTime = self.key == nil if self.key != key || self.theme !== presentationData.theme { self.key = key self.theme = presentationData.theme @@ -995,6 +1084,8 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { var icon: UIImage? var isBold = false var isGestureEnabled = false + var isAnimation = false + var animationState: MoreIconNodeState = .more switch key { case .edit: text = presentationData.strings.Common_Edit @@ -1005,18 +1096,24 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { text = presentationData.strings.Common_Select case .search: text = "" - icon = PresentationResourcesRootController.navigationCompactSearchIcon(presentationData.theme) + icon = nil// PresentationResourcesRootController.navigationCompactSearchIcon(presentationData.theme) + isAnimation = true + animationState = .search case .editPhoto: text = presentationData.strings.Settings_EditPhoto case .editVideo: text = presentationData.strings.Settings_EditVideo case .more: text = "" - icon = PresentationResourcesRootController.navigationMoreCircledIcon(presentationData.theme) + icon = nil// PresentationResourcesRootController.navigationMoreCircledIcon(presentationData.theme) isGestureEnabled = true + isAnimation = true + animationState = .more case .qrCode: text = "" icon = PresentationResourcesRootController.navigationQrCodeIcon(presentationData.theme) + case .moreToSearch: + text = "" } self.accessibilityLabel = text self.containerNode.isGestureEnabled = isGestureEnabled @@ -1027,6 +1124,26 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { self.whiteTextNode.attributedText = NSAttributedString(string: text, font: font, textColor: .white) self.iconNode.image = icon + if isAnimation { + self.iconNode.isHidden = true + let animationNode: MoreIconNode + if let current = self.animationNode { + animationNode = current + } else { + animationNode = MoreIconNode() + self.animationNode = animationNode + self.contextSourceNode.addSubnode(animationNode) + } + animationNode.customColor = presentationData.theme.rootController.navigationBar.accentTextColor + animationNode.enqueueState(animationState, animated: !isFirstTime) + } else { + self.iconNode.isHidden = false + if let current = self.animationNode { + self.animationNode = nil + current.removeFromSupernode() + } + } + textSize = self.regularTextNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude)) let _ = self.whiteTextNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude)) } else { @@ -1039,7 +1156,16 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { self.regularTextNode.frame = textFrame self.whiteTextNode.frame = textFrame - if let image = self.iconNode.image { + if let animationNode = self.animationNode { + let animationSize = CGSize(width: 30.0, height: 30.0) + + animationNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((height - animationSize.height) / 2.0)), size: animationSize) + + let size = CGSize(width: animationSize.width + inset * 2.0, height: height) + self.containerNode.frame = CGRect(origin: CGPoint(), size: size) + self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size) + return size + } else if let image = self.iconNode.image { self.iconNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((height - image.size.height) / 2.0)), size: image.size) let size = CGSize(width: image.size.width + inset * 2.0, height: height) @@ -1066,6 +1192,7 @@ enum PeerInfoHeaderNavigationButtonKey { case editVideo case more case qrCode + case moreToSearch } struct PeerInfoHeaderNavigationButtonSpec: Equatable { @@ -1187,20 +1314,26 @@ final class PeerInfoHeaderNavigationButtonContainerNode: ASDisplayNode { for spec in rightButtons.reversed() { let buttonNode: PeerInfoHeaderNavigationButton var wasAdded = false - if let current = self.rightButtonNodes[spec.key] { + + var key = spec.key + if key == .more || key == .search { + key = .moreToSearch + } + + if let current = self.rightButtonNodes[key] { buttonNode = current } else { wasAdded = true buttonNode = PeerInfoHeaderNavigationButton() - self.rightButtonNodes[spec.key] = buttonNode + self.rightButtonNodes[key] = buttonNode self.addSubnode(buttonNode) buttonNode.isWhite = self.isWhite - buttonNode.action = { [weak self] _, gesture in - guard let strongSelf = self, let buttonNode = strongSelf.rightButtonNodes[spec.key] else { - return - } - strongSelf.performAction?(spec.key, buttonNode.contextSourceNode, gesture) + } + buttonNode.action = { [weak self] _, gesture in + guard let strongSelf = self, let buttonNode = strongSelf.rightButtonNodes[key] else { + return } + strongSelf.performAction?(spec.key, buttonNode.contextSourceNode, gesture) } let buttonSize = buttonNode.update(key: spec.key, presentationData: presentationData, height: size.height) var nextButtonOrigin = spec.isForExpandedView ? nextExpandedButtonOrigin : nextRegularButtonOrigin @@ -1213,6 +1346,10 @@ final class PeerInfoHeaderNavigationButtonContainerNode: ASDisplayNode { } let alphaFactor: CGFloat = spec.isForExpandedView ? expandFraction : (1.0 - expandFraction) if wasAdded { + if key == .moreToSearch { + buttonNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2) + } + buttonNode.frame = buttonFrame buttonNode.alpha = 0.0 transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) @@ -1223,20 +1360,37 @@ final class PeerInfoHeaderNavigationButtonContainerNode: ASDisplayNode { } var removeKeys: [PeerInfoHeaderNavigationButtonKey] = [] for (key, _) in self.rightButtonNodes { - if !rightButtons.contains(where: { $0.key == key }) { + if key == .moreToSearch { + if !rightButtons.contains(where: { $0.key == .more || $0.key == .search }) { + removeKeys.append(key) + } + } else if !rightButtons.contains(where: { $0.key == key }) { removeKeys.append(key) } } for key in removeKeys { if let buttonNode = self.rightButtonNodes.removeValue(forKey: key) { - buttonNode.removeFromSupernode() + if key == .moreToSearch { + buttonNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak buttonNode] _ in + buttonNode?.removeFromSupernode() + }) + buttonNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false) + } else { + buttonNode.removeFromSupernode() + } } } } else { var nextRegularButtonOrigin = size.width - 16.0 var nextExpandedButtonOrigin = size.width - 16.0 + for spec in rightButtons.reversed() { - if let buttonNode = self.rightButtonNodes[spec.key] { + var key = spec.key + if key == .more || key == .search { + key = .moreToSearch + } + + if let buttonNode = self.rightButtonNodes[key] { let buttonSize = buttonNode.bounds.size var nextButtonOrigin = spec.isForExpandedView ? nextExpandedButtonOrigin : nextRegularButtonOrigin let buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin - buttonSize.width, y: expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0)), size: buttonSize) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 174b712727..f014ab9f31 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -2835,7 +2835,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } case .qrCode: strongSelf.openQrCode() - case .editPhoto, .editVideo: + case .editPhoto, .editVideo, .moreToSearch: break } } diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift index 55f8a42358..53743c1330 100644 --- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift +++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift @@ -424,7 +424,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { case let .animated(resource, isVideo): let animatedStickerNode = AnimatedStickerNode() self.animatedStickerNode = animatedStickerNode - animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: resource._asResource(), isVideo: isVideo), width: 80, height: 80, mode: .cached) + animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: resource._asResource(), isVideo: isVideo), width: 80, height: 80, mode: .direct(cachePathPrefix: nil)) } } case let .dice(dice, context, text, action):