diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index aa5535ff96..70d855bea1 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7539,6 +7539,8 @@ Sorry for the inconvenience."; "Premium.Stickers.Description" = "Unlock this sticker and more by subscribing to Telegram Premium."; "Premium.Stickers.Proceed" = "Unlock Premium Stickers"; +"Premium.Reactions.Proceed" = "Unlock Premium Reactions"; + "AccessDenied.LocationPreciseDenied" = "To share your specific location in this chat, please go to Settings > Privacy > Location Services > Telegram and set Precise Location to On."; "Chat.MultipleTypingPair" = "%@ and %@"; @@ -7626,8 +7628,6 @@ Sorry for the inconvenience."; "Premium.ChargeInfo" = "Next charge: %1$@ on %2$@. [Cancel](cancel)."; -"Premium.MoreAboutPremium" = "More About Premium"; - "Conversation.CopyProtectionSavingDisabledSecret" = "Saving is restricted"; "Conversation.CopyProtectionForwardingDisabledSecret" = "Forwards are restricted"; @@ -7651,6 +7651,7 @@ Sorry for the inconvenience."; "Premium.Limits.PublicLinks" = "Public Links"; "Premium.Limits.SavedGifs" = "Saved GIFs"; "Premium.Limits.FavedStickers" = "Favorite Stickers"; +"Premium.Limits.Bio" = "Bio"; "Premium.Limits.Captions" = "Captions"; "Premium.Limits.Folders" = "Folders"; "Premium.Limits.ChatsPerFolder" = "Chats per Folder"; @@ -7661,6 +7662,7 @@ Sorry for the inconvenience."; "Premium.Limits.PublicLinksInfo" = "Reserve up to 20 [t.me/name]() links"; "Premium.Limits.SavedGifsInfo" = "Save up to 400 GIFs in your Favorite GIFs"; "Premium.Limits.FavedStickersInfo" = "Save up to 10 stickers in your Favorite stickers"; +"Premium.Limits.BioInfo" = "Add more symbols and use links in your bio"; "Premium.Limits.CaptionsInfo" = "Use longer descriptions for your photos and videos"; "Premium.Limits.FoldersInfo" = "Organize your chats into 20 folders"; "Premium.Limits.ChatsPerFolderInfo" = "Add up to 200 chats into each of your folders"; diff --git a/submodules/Components/SolidRoundedButtonComponent/Sources/SolidRoundedButtonComponent.swift b/submodules/Components/SolidRoundedButtonComponent/Sources/SolidRoundedButtonComponent.swift index 2021776113..1064e74520 100644 --- a/submodules/Components/SolidRoundedButtonComponent/Sources/SolidRoundedButtonComponent.swift +++ b/submodules/Components/SolidRoundedButtonComponent/Sources/SolidRoundedButtonComponent.swift @@ -17,7 +17,9 @@ public final class SolidRoundedButtonComponent: Component { public let cornerRadius: CGFloat public let gloss: Bool public let iconName: String? + public let animationName: String? public let iconPosition: SolidRoundedButtonIconPosition + public let iconSpacing: CGFloat public let isLoading: Bool public let action: () -> Void @@ -31,7 +33,9 @@ public final class SolidRoundedButtonComponent: Component { cornerRadius: CGFloat = 24.0, gloss: Bool = false, iconName: String? = nil, + animationName: String? = nil, iconPosition: SolidRoundedButtonIconPosition = .left, + iconSpacing: CGFloat = 8.0, isLoading: Bool = false, action: @escaping () -> Void ) { @@ -44,7 +48,9 @@ public final class SolidRoundedButtonComponent: Component { self.cornerRadius = cornerRadius self.gloss = gloss self.iconName = iconName + self.animationName = animationName self.iconPosition = iconPosition + self.iconSpacing = iconSpacing self.isLoading = isLoading self.action = action } @@ -77,9 +83,15 @@ public final class SolidRoundedButtonComponent: Component { if lhs.iconName != rhs.iconName { return false } + if lhs.animationName != rhs.animationName { + return false + } if lhs.iconPosition != rhs.iconPosition { return false } + if lhs.iconSpacing != rhs.iconSpacing { + return false + } if lhs.isLoading != rhs.isLoading { return false } @@ -116,7 +128,9 @@ public final class SolidRoundedButtonComponent: Component { if let button = self.button { button.title = component.title button.iconPosition = component.iconPosition + button.iconSpacing = component.iconSpacing button.icon = component.iconName.flatMap { UIImage(bundleImageName: $0) } + button.animation = component.animationName button.gloss = component.gloss button.updateTheme(component.theme) diff --git a/submodules/PremiumUI/Resources/premium_addone.json b/submodules/PremiumUI/Resources/premium_addone.json new file mode 100644 index 0000000000..2c0230da5e --- /dev/null +++ b/submodules/PremiumUI/Resources/premium_addone.json @@ -0,0 +1 @@ +{"v":"5.8.1","fr":60,"ip":0,"op":120,"w":30,"h":30,"nm":"addone","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"+1","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-24,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,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":[[3,0],[-3,0]],"c":false},"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":1.66,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"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":"Vector 76","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":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[3,0],[-3,0]],"c":false},"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":1.66,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[0]},{"t":16,"s":[100]}],"ix":1},"e":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":-90,"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":"Vector 80","np":3,"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":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[1,4],[1,-4],[-1,-2]],"c":false},"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":1.66,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":16,"s":[100]},{"t":24,"s":[0]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[48,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"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":"Vector 78","np":3,"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":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[2,0],[-2,0]],"c":false},"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":1.66,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":24,"s":[100]},{"t":30,"s":[0]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[54,24],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"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":"Vector 79","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":124,"st":4,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"block","tt":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3,15,0],"ix":2,"l":2},"a":{"a":0,"k":[-72,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[0,0,100]},{"t":30,"s":[16.667,16.667,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-2.209],[0,0],[2.209,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[0,2.209],[0,0],[-2.209,0],[0,0],[0,-2.209],[0,0],[2.209,0]],"v":[[12,-5],[12,5],[8,9],[-8,9],[-12,5],[-12,-5],[-8,-9],[8,-9]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,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":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"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":"Rectangle 111","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"+2","parent":4,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[42,0,0],"ix":2,"l":2},"a":{"a":0,"k":[66,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":0,"s":[100,100,100]},{"t":20,"s":[0,0,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":[[3,0],[-3,0]],"c":false},"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":1.66,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"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":"Vector 76","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":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[3,0],[-3,0]],"c":false},"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":1.66,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":100,"ix":1},"e":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":-90,"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":"Vector 80","np":3,"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":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[1,4],[1,-4],[-1,-2]],"c":false},"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":1.66,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[48,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"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":"Vector 78","np":3,"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":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[2,0],[-2,0]],"c":false},"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":1.66,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[54,24],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"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":"Vector 79","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":124,"st":4,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"block 2","tt":2,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":12,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[27,15,0],"ix":2,"l":2},"a":{"a":0,"k":[72,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,17.667]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,-16.667]},"t":0,"s":[16.667,16.667,100]},{"t":12,"s":[0,0,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-2.209],[0,0],[2.209,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[0,2.209],[0,0],[-2.209,0],[0,0],[0,-2.209],[0,0],[2.209,0]],"v":[[12,-5],[12,5],[8,9],[-8,9],[-12,5],[-12,-5],[-8,-9],[8,-9]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,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":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"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":"Rectangle 111","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/submodules/PremiumUI/Resources/premium_unlock.json b/submodules/PremiumUI/Resources/premium_unlock.json new file mode 100644 index 0000000000..66733bb8fc --- /dev/null +++ b/submodules/PremiumUI/Resources/premium_unlock.json @@ -0,0 +1 @@ +{"v":"5.8.1","fr":60,"ip":0,"op":120,"w":30,"h":30,"nm":"unlock","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"lock2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[0.805]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[15]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[-0.195]},"t":20,"s":[7.407]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":30,"s":[15]},{"t":40,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[19,14,0],"to":[0,-0.139,0],"ti":[0,0,0]},{"i":{"x":0.903,"y":0},"o":{"x":0.333,"y":0},"t":10,"s":[19,13.167,0],"to":[0,0,0],"ti":[0,-0.124,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.152,"y":1},"t":20,"s":[19,13.383,0],"to":[0,0.216,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":30,"s":[19,13.167,0],"to":[0,0,0],"ti":[0,-0.088,0]},{"t":40,"s":[19,14,0]}],"ix":2,"l":2},"a":{"a":0,"k":[24,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,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],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-4,-1.5],[-4,-8],[4,-8],[4,8]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":4,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"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":"lock2","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"lock1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[0.805]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[-15]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[-0.195]},"t":20,"s":[-7.407]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":30,"s":[-15]},{"t":40,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[19,16,0],"to":[0,0.139,0],"ti":[0,0,0]},{"i":{"x":0.903,"y":0},"o":{"x":0.333,"y":0},"t":10,"s":[19,16.833,0],"to":[0,0,0],"ti":[0,0.124,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.152,"y":1},"t":20,"s":[19,16.617,0],"to":[0,-0.216,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":30,"s":[19,16.833,0],"to":[0,0,0],"ti":[0,0.088,0]},{"t":40,"s":[19,16,0]}],"ix":2,"l":2},"a":{"a":0,"k":[24,-12,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[14,12],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":4,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,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":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"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":"lock1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/submodules/PremiumUI/Resources/premium_x2.json b/submodules/PremiumUI/Resources/premium_x2.json new file mode 100644 index 0000000000..55fd5e2ed4 --- /dev/null +++ b/submodules/PremiumUI/Resources/premium_x2.json @@ -0,0 +1 @@ +{"v":"5.8.1","fr":60,"ip":0,"op":120,"w":30,"h":30,"nm":"double","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"x2move","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.593},"o":{"x":1,"y":0},"t":0,"s":[15,15,0],"to":[2.667,0,0],"ti":[2.667,0,0]},{"i":{"x":0.833,"y":0.963},"o":{"x":0.167,"y":0.042},"t":5,"s":[31,15,0],"to":[-2.667,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.704},"o":{"x":0.167,"y":0.296},"t":6,"s":[-1,15,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.957},"o":{"x":0.167,"y":0.037},"t":14,"s":[31,15,0],"to":[0,0,0],"ti":[2.528,0,0]},{"i":{"x":0.833,"y":0.935},"o":{"x":0.167,"y":0.395},"t":15,"s":[-1,15,0],"to":[-2.528,0,0],"ti":[-2.667,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.78},"t":20,"s":[15.833,15,0],"to":[2.667,0,0],"ti":[0.139,0,0]},{"t":30,"s":[15,15,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[1,1,0.333],"y":[0,0,0]},"t":0,"s":[16.667,16.667,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":5,"s":[8.333,8.333,100]},{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":15,"s":[8.333,8.333,100]},{"t":20,"s":[16.667,16.667,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"x2","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-24,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,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":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[3,4],[-3,-4]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":25,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[3,4],[-1.333,-4]],"c":false}]},{"t":30,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[3,4],[-3,-4]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.909803921569,0.470588235294,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.66,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"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":"Vector 73","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":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3,4],[3,-4]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":25,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3,4],[4.667,-4]],"c":false}]},{"t":30,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3,4],[3,-4]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.909803921569,0.470588235294,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.66,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"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":"Vector 74","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.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[{"i":[[0,0],[0,0],[-0.828,0],[0,0],[0,-1.195],[0.733,-0.366],[0,0],[0,-0.758],[0,0],[0,0]],"o":[[0,0],[0,-0.828],[0,0],[1.195,0],[0,0.819],[0,0],[-0.678,0.339],[0,0],[0,0],[0,0]],"v":[[-2.5,-2],[-2.5,-2.5],[-1,-4],[0.337,-4],[2.5,-1.837],[1.304,0.098],[-1.394,1.447],[-2.5,3.236],[-2.5,4],[2.5,4]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":25,"s":[{"i":[[0,0],[0,0],[-0.828,0],[0,0],[0,-1.195],[0.733,-0.366],[0,0],[0,-0.758],[0,0],[0,0]],"o":[[0,0],[0,-0.828],[0,0],[1.195,0],[0,0.819],[0,0],[-0.678,0.339],[0,0],[0,0],[0,0]],"v":[[-0.833,-2],[-0.833,-2.5],[0.667,-4],[2.004,-4],[4.167,-1.837],[1.304,0.098],[-1.394,1.447],[-2.5,3.236],[-2.5,4],[2.5,4]],"c":false}]},{"t":30,"s":[{"i":[[0,0],[0,0],[-0.828,0],[0,0],[0,-1.195],[0.733,-0.366],[0,0],[0,-0.758],[0,0],[0,0]],"o":[[0,0],[0,-0.828],[0,0],[1.195,0],[0,0.819],[0,0],[-0.678,0.339],[0,0],[0,0],[0,0]],"v":[[-2.5,-2],[-2.5,-2.5],[-1,-4],[0.337,-4],[2.5,-1.837],[1.304,0.098],[-1.394,1.447],[-2.5,3.236],[-2.5,4],[2.5,4]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.909803921569,0.470588235294,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.66,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[51,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"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":"Vector 75","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"block","tt":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[15,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":15,"s":[{"i":[[0,-2.209],[0,0],[2.209,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[0,2.209],[0,0],[-2.209,0],[0,0],[0,-2.209],[0,0],[2.209,0]],"v":[[12,-5],[12,5],[8,9],[-8,9],[-12,5],[-12,-5],[-8,-9],[8,-9]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[{"i":[[0,-2.209],[0,0],[2.209,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[0,2.209],[0,0],[-2.209,0],[0,0],[0,-2.209],[0,0],[2.209,0]],"v":[[14.792,-5],[13.125,5],[9.125,9],[-8,9],[-12,5],[-10.333,-5],[-6.333,-9],[10.792,-9]],"c":true}]},{"t":30,"s":[{"i":[[0,-2.209],[0,0],[2.209,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[0,2.209],[0,0],[-2.209,0],[0,0],[0,-2.209],[0,0],[2.209,0]],"v":[[12,-5],[12,5],[8,9],[-8,9],[-12,5],[-12,-5],[-8,-9],[8,-9]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,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":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"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":"Rectangle 111","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift index 26dabed1e6..613d2b8c00 100644 --- a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift @@ -827,7 +827,7 @@ private final class DemoSheetContent: CombinedComponent { case let .intro(price): buttonText = strings.Premium_SubscribeFor(price ?? "–").string case .other: - buttonText = strings.Premium_MoreAboutPremium + buttonText = strings.Premium_Reactions_Proceed } } @@ -849,7 +849,9 @@ private final class DemoSheetContent: CombinedComponent { height: 50.0, cornerRadius: 10.0, gloss: state.isPremium != true, + animationName: isStandalone && component.subject == .uniqueReactions ? "premium_unlock" : nil, iconPosition: .right, + iconSpacing: 6.0, action: { [weak component] in guard let component = component else { return diff --git a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift index 365424302e..258025b7eb 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift @@ -680,7 +680,7 @@ private final class LimitSheetContent: CombinedComponent { ) var titleText = strings.Premium_LimitReached - var buttonIconName = "Premium/X2" + var buttonAnimationName = "premium_x2" let iconName: String let badgeText: String let string: String @@ -738,7 +738,7 @@ private final class LimitSheetContent: CombinedComponent { } else { badgePosition = CGFloat(component.count) / CGFloat(premiumLimit) } - buttonIconName = "Premium/PlusOne" + buttonAnimationName = "premium_addone" } var reachedMaximumLimit = badgePosition >= 1.0 if case .folders = subject, !state.isPremium { @@ -809,6 +809,7 @@ private final class LimitSheetContent: CombinedComponent { let button = button.update( component: SolidRoundedButtonComponent( title: !reachedMaximumLimit ? strings.Premium_IncreaseLimit : strings.Common_OK, + theme: SolidRoundedButtonComponent.Theme( backgroundColor: .black, backgroundColors: [ @@ -824,7 +825,7 @@ private final class LimitSheetContent: CombinedComponent { height: 50.0, cornerRadius: 10.0, gloss: !reachedMaximumLimit, - iconName: !reachedMaximumLimit ? buttonIconName : nil, + animationName: !reachedMaximumLimit ? buttonAnimationName : nil, iconPosition: .right, action: { [weak component] in guard let component = component else { diff --git a/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift index 1fc010dbea..073e49b464 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift @@ -191,6 +191,7 @@ private enum Limit: CaseIterable { case publicLinks case savedGifs case favedStickers + case about case captions case folders case chatsPerFolder @@ -208,6 +209,8 @@ private enum Limit: CaseIterable { return strings.Premium_Limits_SavedGifs case .favedStickers: return strings.Premium_Limits_FavedStickers + case .about: + return strings.Premium_Limits_Bio case .captions: return strings.Premium_Limits_Captions case .folders: @@ -231,6 +234,8 @@ private enum Limit: CaseIterable { return strings.Premium_Limits_SavedGifsInfo case .favedStickers: return strings.Premium_Limits_FavedStickersInfo + case .about: + return strings.Premium_Limits_BioInfo case .captions: return strings.Premium_Limits_CaptionsInfo case .folders: @@ -255,6 +260,8 @@ private enum Limit: CaseIterable { value = configuration.maxSavedGifCount case .favedStickers: value = configuration.maxFavedStickerCount + case .about: + value = configuration.maxAboutLength case .captions: value = configuration.maxCaptionLength case .folders: @@ -337,6 +344,7 @@ private final class PremimLimitsListScreenComponent: CombinedComponent { UIColor(rgb: 0x9377ff), UIColor(rgb: 0xac64f3), UIColor(rgb: 0xc456ae), + UIColor(rgb: 0xcf579a), UIColor(rgb: 0xdb5887), UIColor(rgb: 0xdb496f), UIColor(rgb: 0xe95d44), @@ -375,7 +383,7 @@ private final class PremimLimitsListScreenComponent: CombinedComponent { .position(CGPoint(x: context.availableSize.width / 2.0, y: environment.navigationHeight + list.size.height / 2.0)) ) - return CGSize(width: context.availableSize.width, height: environment.navigationHeight + list.size.height + environment.safeInsets.bottom) + return CGSize(width: context.availableSize.width, height: environment.navigationHeight + list.size.height + environment.safeInsets.bottom - 16.0) } } } @@ -474,6 +482,11 @@ public class PremimLimitsListScreen: ViewController { func scrollViewDidScroll(_ scrollView: UIScrollView) { let contentOffset = self.scrollView.contentOffset.y self.controller?.navigationBar?.updateBackgroundAlpha(min(30.0, contentOffset) / 30.0, transition: .immediate) + + let bottomOffsetY = max(0.0, self.scrollView.contentSize.height + 20.0 - contentOffset - self.scrollView.frame.height) + let backgroundAlpha: CGFloat = min(30.0, bottomOffsetY) / 30.0 + + self.footerNode.updateBackgroundAlpha(backgroundAlpha, transition: .immediate) } func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { diff --git a/submodules/ShimmerEffect/Sources/ShimmerEffect.swift b/submodules/ShimmerEffect/Sources/ShimmerEffect.swift index f2b4f99da9..e2827e4ea2 100644 --- a/submodules/ShimmerEffect/Sources/ShimmerEffect.swift +++ b/submodules/ShimmerEffect/Sources/ShimmerEffect.swift @@ -15,6 +15,7 @@ public final class ShimmerEffectForegroundView: UIView { private var absoluteLocation: (CGRect, CGSize)? private var isCurrentlyInHierarchy = false private var shouldBeAnimating = false + private var globalTimeOffset = true private let trackingLayer: HierarchyTrackingLayer @@ -57,7 +58,7 @@ public final class ShimmerEffectForegroundView: UIView { fatalError("init(coder:) has not been implemented") } - public func update(backgroundColor: UIColor, foregroundColor: UIColor, gradientSize: CGFloat?, duration: Double?, horizontal: Bool = false) { + public func update(backgroundColor: UIColor, foregroundColor: UIColor, gradientSize: CGFloat?, globalTimeOffset: Bool, duration: Double?, horizontal: Bool = false) { if let currentBackgroundColor = self.currentBackgroundColor, currentBackgroundColor.isEqual(backgroundColor), let currentForegroundColor = self.currentForegroundColor, currentForegroundColor.isEqual(foregroundColor), self.currentHorizontal == horizontal, self.currentGradientSize == gradientSize { return } @@ -65,6 +66,7 @@ public final class ShimmerEffectForegroundView: UIView { self.currentForegroundColor = foregroundColor self.currentHorizontal = horizontal self.currentGradientSize = gradientSize + self.globalTimeOffset = globalTimeOffset self.currentDuration = duration let image: UIImage? @@ -155,14 +157,18 @@ public final class ShimmerEffectForegroundView: UIView { self.image.frame = CGRect(origin: CGPoint(x: -gradientSize, y: 0.0), size: CGSize(width: gradientSize, height: containerSize.height)) let animation = self.image.makeAnimation(from: 0.0 as NSNumber, to: (containerSize.width + gradientSize) as NSNumber, keyPath: "position.x", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: self.currentDuration ?? 1.3, delay: 0.0, mediaTimingFunction: nil, removeOnCompletion: true, additive: true) animation.repeatCount = Float.infinity - animation.beginTime = 1.0 + if self.globalTimeOffset { + animation.beginTime = 1.0 + } self.image.add(animation, forKey: "shimmer") } else { let gradientSize = self.currentGradientSize ?? 250.0 self.image.frame = CGRect(origin: CGPoint(x: 0.0, y: -gradientSize), size: CGSize(width: containerSize.width, height: gradientSize)) let animation = self.image.makeAnimation(from: 0.0 as NSNumber, to: (containerSize.height + gradientSize) as NSNumber, keyPath: "position.y", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: self.currentDuration ?? 1.3, delay: 0.0, mediaTimingFunction: nil, removeOnCompletion: true, additive: true) animation.repeatCount = Float.infinity - animation.beginTime = 1.0 + if self.globalTimeOffset { + animation.beginTime = 1.0 + } self.image.add(animation, forKey: "shimmer") } } diff --git a/submodules/SolidRoundedButtonNode/BUILD b/submodules/SolidRoundedButtonNode/BUILD index fde9a24551..4166c5fe10 100644 --- a/submodules/SolidRoundedButtonNode/BUILD +++ b/submodules/SolidRoundedButtonNode/BUILD @@ -10,10 +10,12 @@ swift_library( "-warnings-as-errors", ], deps = [ + "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", "//submodules/ShimmerEffect:ShimmerEffect", "//submodules/Components/HierarchyTrackingLayer:HierarchyTrackingLayer", + "//submodules/ManagedAnimationNode:ManagedAnimationNode", ], visibility = [ "//visibility:public", diff --git a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift index e2e0befb1e..2d6ee14a8c 100644 --- a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift +++ b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift @@ -2,8 +2,10 @@ import Foundation import UIKit import AsyncDisplayKit import Display +import SwiftSignalKit import HierarchyTrackingLayer import ShimmerEffect +import ManagedAnimationNode private func generateIndefiniteActivityIndicatorImage(color: UIColor, diameter: CGFloat = 22.0, lineWidth: CGFloat = 2.0) -> UIImage? { return generateImage(CGSize(width: diameter, height: diameter), rotatedContext: { size, context in @@ -74,6 +76,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode { private let titleNode: ImmediateTextNode private let subtitleNode: ImmediateTextNode private let iconNode: ASImageNode + private var animationNode: SimpleAnimationNode? private var progressNode: ASImageNode? private let buttonHeight: CGFloat @@ -104,6 +107,44 @@ public final class SolidRoundedButtonNode: ASDisplayNode { } } + private var animationTimer: SwiftSignalKit.Timer? + public var animation: String? { + didSet { + if let animation = self.animation { + if animation != oldValue { + self.animationNode?.removeFromSupernode() + self.animationNode = nil + + let animationNode = SimpleAnimationNode(animationName: animation, size: CGSize(width: 30.0, height: 30.0)) + animationNode.customColor = self.theme.foregroundColor + self.addSubnode(animationNode) + self.animationNode = animationNode + + if let width = self.validLayout { + _ = self.updateLayout(width: width, transition: .immediate) + } + + if self.gloss { + self.animationTimer?.invalidate() + + Queue.mainQueue().after(0.75) { + self.animationNode?.play() + + let timer = SwiftSignalKit.Timer(timeout: 3.0, repeat: true, completion: { [weak self] in + self?.animationNode?.play() + }, queue: Queue.mainQueue()) + self.animationTimer = timer + timer.start() + } + } + } + } else if let animationNode = self.animationNode { + animationNode.removeFromSupernode() + self.animationNode = nil + } + } + } + public var iconSpacing: CGFloat = 8.0 { didSet { if let width = self.validLayout { @@ -253,8 +294,8 @@ public final class SolidRoundedButtonNode: ASDisplayNode { compositingFilter = nil } - shimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(alpha), gradientSize: 70.0, duration: 3.0, horizontal: true) - borderShimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(borderAlpha), gradientSize: 70.0, duration: 3.0, horizontal: true) + shimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(alpha), gradientSize: 70.0, globalTimeOffset: false, duration: 3.0, horizontal: true) + borderShimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(borderAlpha), gradientSize: 70.0, globalTimeOffset: false, duration: 3.0, horizontal: true) shimmerView.layer.compositingFilter = compositingFilter borderShimmerView.layer.compositingFilter = compositingFilter @@ -300,6 +341,18 @@ public final class SolidRoundedButtonNode: ASDisplayNode { self.iconNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -15.0), to: CGPoint(), duration: 0.2, additive: true) self.iconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } + + if let animationNode = self.animationNode, let snapshotView = animationNode.view.snapshotView(afterScreenUpdates: false) { + snapshotView.frame = animationNode.frame + + self.view.insertSubview(snapshotView, aboveSubview: animationNode.view) + snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: 15.0), duration: 0.2, removeOnCompletion: false, additive: true) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + animationNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -15.0), to: CGPoint(), duration: 0.2, additive: true) + animationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } } if theme.backgroundColors.count > 1 { self.buttonBackgroundNode.backgroundColor = nil @@ -319,6 +372,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode { self.subtitleNode.attributedText = NSAttributedString(string: self.subtitle ?? "", font: Font.regular(14.0), textColor: theme.foregroundColor) self.iconNode.image = generateTintedImage(image: self.iconNode.image, color: theme.foregroundColor) + self.animationNode?.customColor = theme.foregroundColor if let width = self.validLayout { _ = self.updateLayout(width: width, transition: .immediate) @@ -359,7 +413,12 @@ public final class SolidRoundedButtonNode: ASDisplayNode { self.titleNode.attributedText = NSAttributedString(string: self.title ?? "", font: self.font == .bold ? Font.semibold(self.fontSize) : Font.regular(self.fontSize), textColor: self.theme.foregroundColor) } - let iconSize = self.iconNode.image?.size ?? CGSize() + let iconSize: CGSize + if let _ = self.animationNode { + iconSize = CGSize(width: 30.0, height: 30.0) + } else { + iconSize = self.iconNode.image?.size ?? CGSize() + } let titleSize = self.titleNode.updateLayout(buttonSize) let spacingOffset: CGFloat = 9.0 @@ -391,6 +450,9 @@ public final class SolidRoundedButtonNode: ASDisplayNode { } transition.updateFrame(node: self.iconNode, frame: iconFrame) + if let animationNode = self.animationNode { + transition.updateFrame(node: animationNode, frame: iconFrame) + } transition.updateFrame(node: self.titleNode, frame: titleFrame) if self.subtitle != self.subtitleNode.attributedText?.string { @@ -477,6 +539,7 @@ public final class SolidRoundedButtonView: UIView { private let titleNode: ImmediateTextView private let subtitleNode: ImmediateTextView private let iconNode: UIImageView + private var animationNode: SimpleAnimationNode? private var progressNode: UIImageView? private let buttonHeight: CGFloat @@ -507,6 +570,47 @@ public final class SolidRoundedButtonView: UIView { } } + private var animationTimer: SwiftSignalKit.Timer? + public var animation: String? { + didSet { + if let animation = self.animation { + if animation != oldValue { + self.animationNode?.view.removeFromSuperview() + self.animationNode = nil + + let animationNode = SimpleAnimationNode(animationName: animation, size: CGSize(width: 30.0, height: 30.0)) + animationNode.customColor = self.theme.foregroundColor + self.addSubview(animationNode.view) + self.animationNode = animationNode + + if let width = self.validLayout { + _ = self.updateLayout(width: width, transition: .immediate) + } + + if self.gloss { + self.animationTimer?.invalidate() + + Queue.mainQueue().after(0.75) { + self.animationNode?.play() + + let timer = SwiftSignalKit.Timer(timeout: 3.0, repeat: true, completion: { [weak self] in + self?.animationNode?.play() + }, queue: Queue.mainQueue()) + self.animationTimer = timer + timer.start() + } + } + } + } else if let animationNode = self.animationNode { + animationNode.view.removeFromSuperview() + self.animationNode = nil + + self.animationTimer?.invalidate() + self.animationTimer = nil + } + } + } + public var iconSpacing: CGFloat = 8.0 { didSet { if let width = self.validLayout { @@ -815,14 +919,13 @@ public final class SolidRoundedButtonView: UIView { compositingFilter = nil } - shimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(alpha), gradientSize: 70.0, duration: 3.0, horizontal: true) - borderShimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(borderAlpha), gradientSize: 70.0, duration: 3.0, horizontal: true) + shimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(alpha), gradientSize: 70.0, globalTimeOffset: false, duration: 3.0, horizontal: true) + borderShimmerView.update(backgroundColor: .clear, foregroundColor: color.withAlphaComponent(borderAlpha), gradientSize: 70.0, globalTimeOffset: false, duration: 3.0, horizontal: true) shimmerView.layer.compositingFilter = compositingFilter borderShimmerView.layer.compositingFilter = compositingFilter } - public func updateTheme(_ theme: SolidRoundedButtonTheme) { guard theme !== self.theme else { return @@ -888,7 +991,12 @@ public final class SolidRoundedButtonView: UIView { self.titleNode.attributedText = NSAttributedString(string: self.title ?? "", font: self.font == .bold ? Font.semibold(self.fontSize) : Font.regular(self.fontSize), textColor: self.theme.foregroundColor) } - let iconSize = self.iconNode.image?.size ?? CGSize() + let iconSize: CGSize + if let _ = self.animationNode { + iconSize = CGSize(width: 30.0, height: 30.0) + } else { + iconSize = self.iconNode.image?.size ?? CGSize() + } let titleSize = self.titleNode.updateLayout(buttonSize) let spacingOffset: CGFloat = 9.0 @@ -905,7 +1013,7 @@ public final class SolidRoundedButtonView: UIView { let titleFrame: CGRect switch self.iconPosition { case .left: - iconFrame = CGRect(origin: CGPoint(x: buttonFrame.minX + nextContentOrigin, y: floor((buttonFrame.height - iconSize.height) / 2.0)), size: iconSize) + iconFrame = CGRect(origin: CGPoint(x: buttonFrame.minX + nextContentOrigin, y: floor((buttonFrame.height - iconSize.height) / 2.0)), size: iconSize) if !iconSize.width.isZero { nextContentOrigin += iconSize.width + iconSpacing } @@ -915,10 +1023,13 @@ public final class SolidRoundedButtonView: UIView { if !iconSize.width.isZero { nextContentOrigin += titleFrame.width + iconSpacing } - iconFrame = CGRect(origin: CGPoint(x: buttonFrame.minX + nextContentOrigin, y: floor((buttonFrame.height - iconSize.height) / 2.0)), size: iconSize) + iconFrame = CGRect(origin: CGPoint(x: buttonFrame.minX + nextContentOrigin, y: floor((buttonFrame.height - iconSize.height) / 2.0)), size: iconSize) } transition.updateFrame(view: self.iconNode, frame: iconFrame) + if let animationNode = self.animationNode { + transition.updateFrame(view: animationNode.view, frame: iconFrame) + } transition.updateFrame(view: self.titleNode, frame: titleFrame) if self.subtitle != self.subtitleNode.attributedText?.string { diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index 951edf304d..6d0f4f662e 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift @@ -14,6 +14,7 @@ import ContextUI import MoreButtonNode import UndoUI import ShareController +import TextFormat import PremiumUI private enum StickerPackPreviewGridEntry: Comparable, Identifiable { @@ -126,7 +127,7 @@ private final class StickerPackContainer: ASDisplayNode { private weak var peekController: PeekController? - init(index: Int, context: AccountContext, presentationData: PresentationData, stickerPack: StickerPackReference, decideNextAction: @escaping (StickerPackContainer, StickerPackAction) -> StickerPackNextAction, requestDismiss: @escaping () -> Void, expandProgressUpdated: @escaping (StickerPackContainer, ContainedViewLayoutTransition, ContainedViewLayoutTransition) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, controller: StickerPackScreenImpl?) { + init(index: Int, context: AccountContext, presentationData: PresentationData, stickerPack: StickerPackReference, decideNextAction: @escaping (StickerPackContainer, StickerPackAction) -> StickerPackNextAction, requestDismiss: @escaping () -> Void, expandProgressUpdated: @escaping (StickerPackContainer, ContainedViewLayoutTransition, ContainedViewLayoutTransition) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, openMention: @escaping (String) -> Void, controller: StickerPackScreenImpl?) { self.index = index self.context = context self.controller = controller @@ -156,6 +157,20 @@ private final class StickerPackContainer: ASDisplayNode { self.buttonNode = HighlightableButtonNode() self.titleNode = ImmediateTextNode() + self.titleNode.maximumNumberOfLines = 2 + self.titleNode.highlightAttributeAction = { attributes in + if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] { + return NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention) + } else { + return nil + } + } + self.titleNode.tapAttributeAction = { attributes, _ in + if let mention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String, mention.count > 1 { + openMention(String(mention[mention.index(after: mention.startIndex)...])) + } + } + self.titleContainer = ASDisplayNode() self.titleSeparatorNode = ASDisplayNode() self.titleSeparatorNode.backgroundColor = self.presentationData.theme.rootController.navigationBar.separatorColor @@ -175,7 +190,7 @@ private final class StickerPackContainer: ASDisplayNode { self.addSubnode(self.actionAreaSeparatorNode) self.addSubnode(self.buttonNode) - self.addSubnode(self.titleBackgroundnode) +// self.addSubnode(self.titleBackgroundnode) self.titleContainer.addSubnode(self.titleNode) self.addSubnode(self.titleContainer) self.addSubnode(self.titleSeparatorNode) @@ -203,12 +218,12 @@ private final class StickerPackContainer: ASDisplayNode { } } -// self.gridNode.visibleContentOffsetChanged = { [weak self] offset in -// guard let strongSelf = self else { -// return -// } -// -// } + self.gridNode.visibleContentOffsetChanged = { [weak self] _ in + guard let strongSelf = self else { + return + } + strongSelf.updateButtonBackgroundAlpha() + } self.gridNode.interactiveScrollingWillBeEnded = { [weak self] contentOffset, velocity, targetOffset -> CGPoint in guard let strongSelf = self, !strongSelf.isDismissed else { @@ -315,6 +330,8 @@ private final class StickerPackContainer: ASDisplayNode { strongSelf.morePressed(node: strongSelf.moreButtonNode.contextSourceNode, gesture: gesture) } } + + self.titleNode.linkHighlightColor = self.presentationData.theme.actionSheet.controlAccentColor.withAlphaComponent(0.5) } deinit { @@ -420,17 +437,23 @@ private final class StickerPackContainer: ASDisplayNode { self.cancelButtonNode.setTitle(self.presentationData.strings.Common_Cancel, with: Font.regular(17.0), with: self.presentationData.theme.actionSheet.controlAccentColor, for: .normal) self.moreButtonNode.theme = self.presentationData.theme + self.titleNode.linkHighlightColor = self.presentationData.theme.actionSheet.controlAccentColor.withAlphaComponent(0.5) + if let currentContents = self.currentContents { let buttonColor: UIColor + var buttonFont: UIFont = Font.semibold(17.0) switch currentContents { case .fetching: - buttonColor = self.presentationData.theme.list.itemDisabledTextColor + buttonColor = .clear case .none: buttonColor = self.presentationData.theme.list.itemAccentColor case let .result(_, _, installed): - buttonColor = installed ? self.presentationData.theme.list.itemDestructiveColor : self.presentationData.theme.list.itemAccentColor + buttonColor = installed ? self.presentationData.theme.list.itemDestructiveColor : self.presentationData.theme.list.itemCheckColors.foregroundColor + if installed { + buttonFont = Font.regular(17.0) + } } - self.buttonNode.setTitle(self.buttonNode.attributedTitle(for: .normal)?.string ?? "", with: Font.semibold(17.0), with: buttonColor, for: .normal) + self.buttonNode.setTitle(self.buttonNode.attributedTitle(for: .normal)?.string ?? "", with: buttonFont, with: buttonColor, for: .normal) } if !self.currentEntries.isEmpty { @@ -438,9 +461,13 @@ private final class StickerPackContainer: ASDisplayNode { self.enqueueTransaction(transaction) } - self.titleNode.attributedText = NSAttributedString(string: self.titleNode.attributedText?.string ?? "", font: Font.semibold(17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor) + let titleFont = Font.semibold(17.0) + let title = self.titleNode.attributedText?.string ?? "" + let entities = generateTextEntities(title, enabledTypes: [.mention]) + self.titleNode.attributedText = stringWithAppliedEntities(title, entities: entities, baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: titleFont, linkFont: titleFont, boldFont: titleFont, italicFont: titleFont, boldItalicFont: titleFont, fixedFont: titleFont, blockQuoteFont: titleFont) + if let (layout, _, _, _) = self.validLayout { - let _ = self.titleNode.updateLayout(CGSize(width: layout.size.width - 12.0 * 2.0, height: .greatestFiniteMagnitude)) + let _ = self.titleNode.updateLayout(CGSize(width: layout.size.width - max(12.0, self.cancelButtonNode.frame.width) * 2.0 - 40.0, height: .greatestFiniteMagnitude)) self.updateLayout(layout: layout, transition: .immediate) } } @@ -534,6 +561,21 @@ private final class StickerPackContainer: ASDisplayNode { }) } + private func updateButtonBackgroundAlpha() { + let offset = self.gridNode.visibleContentOffset() + + let backgroundAlpha: CGFloat + switch offset { + case let .known(value): + let bottomOffsetY = max(0.0, self.gridNode.scrollView.contentSize.height + self.gridNode.scrollView.contentInset.top + self.gridNode.scrollView.contentInset.bottom - value - self.gridNode.scrollView.frame.height - 10.0) + backgroundAlpha = min(10.0, bottomOffsetY) / 10.0 + case .unknown, .none: + backgroundAlpha = 1.0 + } + self.actionAreaBackgroundNode.alpha = backgroundAlpha + self.actionAreaSeparatorNode.alpha = backgroundAlpha + } + private var currentContents: LoadedStickerPack? private func updateStickerPackContents(_ contents: LoadedStickerPack, hasPremium: Bool) { self.currentContents = contents @@ -631,7 +673,10 @@ private final class StickerPackContainer: ASDisplayNode { } } - self.titleNode.attributedText = NSAttributedString(string: info.title, font: Font.semibold(17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor) + let titleFont = Font.semibold(17.0) + let entities = generateTextEntities(info.title, enabledTypes: [.mention]) + self.titleNode.attributedText = stringWithAppliedEntities(info.title, entities: entities, baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: titleFont, linkFont: titleFont, boldFont: titleFont, italicFont: titleFont, boldItalicFont: titleFont, fixedFont: titleFont, blockQuoteFont: titleFont) + updateLayout = true var generalItems: [StickerPackItem] = [] @@ -686,12 +731,12 @@ private final class StickerPackContainer: ASDisplayNode { } if updateLayout, let (layout, _, _, _) = self.validLayout { - let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - 12.0 * 2.0, height: .greatestFiniteMagnitude)) - self.titleNode.frame = CGRect(origin: CGPoint(x: floor((-titleSize.width) / 2.0), y: floor((-titleSize.height) / 2.0)), size: titleSize) - let cancelSize = self.cancelButtonNode.measure(CGSize(width: layout.size.width, height: .greatestFiniteMagnitude)) self.cancelButtonNode.frame = CGRect(origin: CGPoint(x: layout.safeInsets.left + 16.0, y: 18.0), size: cancelSize) + let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - cancelSize.width * 2.0 - 40.0, height: .greatestFiniteMagnitude)) + self.titleNode.frame = CGRect(origin: CGPoint(x: floor((-titleSize.width) / 2.0), y: floor((-titleSize.height) / 2.0)), size: titleSize) + self.moreButtonNode.frame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.right - 46.0, y: 5.0), size: CGSize(width: 44.0, height: 44.0)) self.updateLayout(layout: layout, transition: .immediate) @@ -851,7 +896,9 @@ private final class StickerPackContainer: ASDisplayNode { transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) transition.updateFrame(node: self.titleContainer, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + floor((backgroundFrame.width) / 2.0), y: backgroundFrame.minY + floor((56.0) / 2.0)), size: CGSize())) transition.updateFrame(node: self.titleSeparatorNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY + 56.0 - UIScreenPixel), size: CGSize(width: backgroundFrame.width, height: UIScreenPixel))) - + transition.updateFrame(node: self.titleBackgroundnode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.width, height: 56.0))) + self.titleBackgroundnode.update(size: CGSize(width: layout.size.width, height: 56.0), transition: .immediate) + transition.updateFrame(node: self.topContainerNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.width, height: 56.0))) let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut) @@ -880,6 +927,11 @@ private final class StickerPackContainer: ASDisplayNode { if !self.backgroundNode.bounds.contains(self.convert(point, to: self.backgroundNode)) { return nil } + + let titlePoint = self.view.convert(point, to: self.titleNode.view) + if self.titleNode.bounds.contains(titlePoint) { + return self.titleNode.view + } } let result = super.hitTest(point, with: event) @@ -908,6 +960,7 @@ private final class StickerPackScreenNode: ViewControllerTracingNode { private let dismissed: () -> Void private let presentInGlobalOverlay: (ViewController, Any?) -> Void private let sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)? + private let openMention: (String) -> Void private let dimNode: ASDisplayNode private let containerContainingNode: ASDisplayNode @@ -924,7 +977,7 @@ private final class StickerPackScreenNode: ViewControllerTracingNode { return self._ready } - init(context: AccountContext, controller: StickerPackScreenImpl, stickerPacks: [StickerPackReference], initialSelectedStickerPackIndex: Int, modalProgressUpdated: @escaping (CGFloat, ContainedViewLayoutTransition) -> Void, dismissed: @escaping () -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?) { + init(context: AccountContext, controller: StickerPackScreenImpl, stickerPacks: [StickerPackReference], initialSelectedStickerPackIndex: Int, modalProgressUpdated: @escaping (CGFloat, ContainedViewLayoutTransition) -> Void, dismissed: @escaping () -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, openMention: @escaping (String) -> Void) { self.context = context self.controller = controller self.presentationData = controller.presentationData @@ -934,6 +987,7 @@ private final class StickerPackScreenNode: ViewControllerTracingNode { self.dismissed = dismissed self.presentInGlobalOverlay = presentInGlobalOverlay self.sendSticker = sendSticker + self.openMention = openMention self.dimNode = ASDisplayNode() self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.25) @@ -1048,8 +1102,7 @@ private final class StickerPackScreenNode: ViewControllerTracingNode { } } } - }, presentInGlobalOverlay: presentInGlobalOverlay, - sendSticker: sendSticker, controller: self.controller) + }, presentInGlobalOverlay: presentInGlobalOverlay, sendSticker: sendSticker, openMention: openMention, controller: self.controller) self.containerContainingNode.addSubnode(container) self.containers[i] = container } @@ -1245,6 +1298,8 @@ public final class StickerPackScreenImpl: ViewController { return self._ready } + private let openMentionDisposable = MetaDisposable() + private var alreadyDidAppear: Bool = false public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, stickerPacks: [StickerPackReference], selectedStickerPackIndex: Int = 0, parentNavigationController: NavigationController? = nil, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)? = nil, actionPerformed: ((StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction) -> Void)? = nil) { @@ -1275,6 +1330,7 @@ public final class StickerPackScreenImpl: ViewController { deinit { self.presentationDataDisposable?.dispose() + self.openMentionDisposable.dispose() } override public func loadDisplayNode() { @@ -1299,6 +1355,28 @@ public final class StickerPackScreenImpl: ViewController { return false } } + }, openMention: { [weak self] mention in + guard let strongSelf = self else { + return + } + + strongSelf.openMentionDisposable.set((strongSelf.context.engine.peers.resolvePeerByName(name: mention) + |> mapToSignal { peer -> Signal in + if let peer = peer { + return .single(peer._asPeer()) + } else { + return .single(nil) + } + } + |> deliverOnMainQueue).start(next: { peer in + guard let strongSelf = self else { + return + } + if let peer = peer, let parentNavigationController = strongSelf.parentNavigationController { + strongSelf.dismiss() + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: parentNavigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), animated: true)) + } + })) }) self._ready.set(self.controllerNode.ready.get()) diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift b/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift index d47915abeb..728912f9b7 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift @@ -238,7 +238,7 @@ final class PremiumStickerPackAccessoryNode: SparseNode, PeekControllerAccessory self.textNode.attributedText = NSAttributedString(string: strings.Premium_Stickers_Description, font: Font.regular(17.0), textColor: theme.actionSheet.secondaryTextColor) self.textNode.lineSpacing = 0.1 - self.proceedButton = SolidRoundedButtonNode(title: strings.Premium_Stickers_Proceed, icon: UIImage(bundleImageName: "Premium/ButtonIcon"), theme: SolidRoundedButtonTheme( + self.proceedButton = SolidRoundedButtonNode(title: strings.Premium_Stickers_Proceed, theme: SolidRoundedButtonTheme( backgroundColor: .white, backgroundColors: [ UIColor(rgb: 0x0077ff), @@ -246,6 +246,9 @@ final class PremiumStickerPackAccessoryNode: SparseNode, PeekControllerAccessory UIColor(rgb: 0x8878ff), UIColor(rgb: 0xe46ace) ], foregroundColor: .white), height: 50.0, cornerRadius: 11.0, gloss: true) + self.proceedButton.iconPosition = .right + self.proceedButton.iconSpacing = 6.0 + self.proceedButton.animation = "premium_unlock" self.cancelButton = HighlightableButtonNode() self.cancelButton.setTitle(strings.Common_Cancel, with: Font.regular(17.0), with: theme.list.itemAccentColor, for: .normal) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift index d9ce03b4a3..f232feb612 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift @@ -124,6 +124,7 @@ final class TelegramGlobalSettings { let privacySettings: AccountPrivacySettings? let unreadTrendingStickerPacks: Int let archivedStickerPacks: [ArchivedStickerPackItem]? + let userLimits: EngineConfiguration.UserLimits let hasPassport: Bool let hasWatchApp: Bool let enableQRLogin: Bool @@ -143,6 +144,7 @@ final class TelegramGlobalSettings { privacySettings: AccountPrivacySettings?, unreadTrendingStickerPacks: Int, archivedStickerPacks: [ArchivedStickerPackItem]?, + userLimits: EngineConfiguration.UserLimits, hasPassport: Bool, hasWatchApp: Bool, enableQRLogin: Bool @@ -161,6 +163,7 @@ final class TelegramGlobalSettings { self.privacySettings = privacySettings self.unreadTrendingStickerPacks = unreadTrendingStickerPacks self.archivedStickerPacks = archivedStickerPacks + self.userLimits = userLimits self.hasPassport = hasPassport self.hasWatchApp = hasWatchApp self.enableQRLogin = enableQRLogin @@ -410,44 +413,67 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id, hasPassport, (context.watchManager?.watchAppInstalled ?? .single(false)), context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]), - getServerProvidedSuggestions(account: context.account) + getServerProvidedSuggestions(account: context.account), + context.engine.data.get( + TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false), + TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true) + ) + ) + |> map { peerView, accountsAndPeers, accountSessions, privacySettings, sharedPreferences, notifications, stickerPacks, hasPassport, hasWatchApp, accountPreferences, suggestions, limits -> PeerInfoScreenData in + let (notificationExceptions, notificationsAuthorizationStatus, notificationsWarningSuppressed) = notifications + let (featuredStickerPacks, archivedStickerPacks) = stickerPacks + + let proxySettings: ProxySettings = sharedPreferences.entries[SharedDataKeys.proxySettings]?.get(ProxySettings.self) ?? ProxySettings.defaultSettings + let inAppNotificationSettings: InAppNotificationSettings = sharedPreferences.entries[ApplicationSpecificSharedDataKeys.inAppNotificationSettings]?.get(InAppNotificationSettings.self) ?? InAppNotificationSettings.defaultSettings + + let unreadTrendingStickerPacks = featuredStickerPacks.reduce(0, { count, item -> Int in + return item.unread ? count + 1 : count + }) + + var enableQRLogin = false + if let appConfiguration = accountPreferences.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self), let data = appConfiguration.data, let enableQR = data["qr_login_camera"] as? Bool, enableQR { + enableQRLogin = true + } + + let peer = peerView.peers[peerId] + let globalSettings = TelegramGlobalSettings( + suggestPhoneNumberConfirmation: suggestions.contains(.validatePhoneNumber), + suggestPasswordConfirmation: suggestions.contains(.validatePassword), + accountsAndPeers: accountsAndPeers, + activeSessionsContext: accountSessions?.0, + webSessionsContext: accountSessions?.2, + otherSessionsCount: accountSessions?.1, + proxySettings: proxySettings, + notificationAuthorizationStatus: notificationsAuthorizationStatus, + notificationWarningSuppressed: notificationsWarningSuppressed, + notificationExceptions: notificationExceptions, + inAppNotificationSettings: inAppNotificationSettings, + privacySettings: privacySettings, + unreadTrendingStickerPacks: unreadTrendingStickerPacks, + archivedStickerPacks: archivedStickerPacks, + userLimits: peer?.isPremium == true ? limits.1 : limits.0, + hasPassport: hasPassport, + hasWatchApp: hasWatchApp, + enableQRLogin: enableQRLogin) + + return PeerInfoScreenData( + peer: peer, + chatPeer: peer, + cachedData: peerView.cachedData, + status: nil, + notificationSettings: nil, + globalNotificationSettings: nil, + isContact: false, + availablePanes: [], + groupsInCommon: nil, + linkedDiscussionPeer: nil, + members: nil, + encryptionKeyFingerprint: nil, + globalSettings: globalSettings, + invitations: nil, + requests: nil, + requestsContext: nil ) - |> map { peerView, accountsAndPeers, accountSessions, privacySettings, sharedPreferences, notifications, stickerPacks, hasPassport, hasWatchApp, accountPreferences, suggestions -> PeerInfoScreenData in - let (notificationExceptions, notificationsAuthorizationStatus, notificationsWarningSuppressed) = notifications - let (featuredStickerPacks, archivedStickerPacks) = stickerPacks - - let proxySettings: ProxySettings = sharedPreferences.entries[SharedDataKeys.proxySettings]?.get(ProxySettings.self) ?? ProxySettings.defaultSettings - let inAppNotificationSettings: InAppNotificationSettings = sharedPreferences.entries[ApplicationSpecificSharedDataKeys.inAppNotificationSettings]?.get(InAppNotificationSettings.self) ?? InAppNotificationSettings.defaultSettings - - let unreadTrendingStickerPacks = featuredStickerPacks.reduce(0, { count, item -> Int in - return item.unread ? count + 1 : count - }) - - var enableQRLogin = false - if let appConfiguration = accountPreferences.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self), let data = appConfiguration.data, let enableQR = data["qr_login_camera"] as? Bool, enableQR { - enableQRLogin = true - } - - let globalSettings = TelegramGlobalSettings(suggestPhoneNumberConfirmation: suggestions.contains(.validatePhoneNumber), suggestPasswordConfirmation: suggestions.contains(.validatePassword), accountsAndPeers: accountsAndPeers, activeSessionsContext: accountSessions?.0, webSessionsContext: accountSessions?.2, otherSessionsCount: accountSessions?.1, proxySettings: proxySettings, notificationAuthorizationStatus: notificationsAuthorizationStatus, notificationWarningSuppressed: notificationsWarningSuppressed, notificationExceptions: notificationExceptions, inAppNotificationSettings: inAppNotificationSettings, privacySettings: privacySettings, unreadTrendingStickerPacks: unreadTrendingStickerPacks, archivedStickerPacks: archivedStickerPacks, hasPassport: hasPassport, hasWatchApp: hasWatchApp, enableQRLogin: enableQRLogin) - - return PeerInfoScreenData( - peer: peerView.peers[peerId], - chatPeer: peerView.peers[peerId], - cachedData: peerView.cachedData, - status: nil, - notificationSettings: nil, - globalNotificationSettings: nil, - isContact: false, - availablePanes: [], - groupsInCommon: nil, - linkedDiscussionPeer: nil, - members: nil, - encryptionKeyFingerprint: nil, - globalSettings: globalSettings, - invitations: nil, - requests: nil, - requestsContext: nil - ) } } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 6b97eda603..76e030d998 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -806,7 +806,7 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat if let cachedData = data.cachedData as? CachedUserData { items[.bio]!.append(PeerInfoScreenMultilineInputItem(id: ItemBio, text: state.updatingBio ?? (cachedData.about ?? ""), placeholder: presentationData.strings.UserInfo_About_Placeholder, textUpdated: { updatedText in interaction.updateBio(updatedText) - }, maxLength: 70)) + }, maxLength: Int(data.globalSettings?.userLimits.maxAboutLength ?? 70))) items[.bio]!.append(PeerInfoScreenCommentItem(id: ItemBioHelp, text: presentationData.strings.Settings_About_Help)) } @@ -901,7 +901,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese interaction.requestLayout(false) })) } else if let about = cachedData.about, !about.isEmpty { - items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: { + items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.isPremium ? enabledPublicBioEntities : enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: { interaction.requestLayout(false) })) }