mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Various improvements
This commit is contained in:
parent
03a13c0305
commit
a4895a903c
@ -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";
|
||||
|
@ -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)
|
||||
|
1
submodules/PremiumUI/Resources/premium_addone.json
Normal file
1
submodules/PremiumUI/Resources/premium_addone.json
Normal file
File diff suppressed because one or more lines are too long
1
submodules/PremiumUI/Resources/premium_unlock.json
Normal file
1
submodules/PremiumUI/Resources/premium_unlock.json
Normal file
@ -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":[]}
|
1
submodules/PremiumUI/Resources/premium_x2.json
Normal file
1
submodules/PremiumUI/Resources/premium_x2.json
Normal file
File diff suppressed because one or more lines are too long
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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 {
|
||||
|
@ -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<PresentationData, NoError>)? = 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<Peer?, NoError> 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())
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user