mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
Various improvements
This commit is contained in:
parent
d40cf1023e
commit
4ceb3ac58b
@ -22,6 +22,7 @@ public let repostStoryIcon = generateTintedImage(image: UIImage(bundleImageName:
|
|||||||
private let archivedChatsIcon = UIImage(bundleImageName: "Avatar/ArchiveAvatarIcon")?.precomposed()
|
private let archivedChatsIcon = UIImage(bundleImageName: "Avatar/ArchiveAvatarIcon")?.precomposed()
|
||||||
private let repliesIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/RepliesMessagesIcon"), color: .white)
|
private let repliesIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/RepliesMessagesIcon"), color: .white)
|
||||||
private let anonymousSavedMessagesIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/AnonymousSenderIcon"), color: .white)
|
private let anonymousSavedMessagesIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/AnonymousSenderIcon"), color: .white)
|
||||||
|
private let anonymousSavedMessagesDarkIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/AnonymousSenderIcon"), color: UIColor(white: 1.0, alpha: 0.4))
|
||||||
private let myNotesIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/MyNotesIcon"), color: .white)
|
private let myNotesIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/MyNotesIcon"), color: .white)
|
||||||
|
|
||||||
public func avatarPlaceholderFont(size: CGFloat) -> UIFont {
|
public func avatarPlaceholderFont(size: CGFloat) -> UIFont {
|
||||||
@ -98,7 +99,11 @@ private func calculateColors(context: AccountContext?, explicitColorIndex: Int?,
|
|||||||
if isColored {
|
if isColored {
|
||||||
colors = AvatarNode.savedMessagesColors
|
colors = AvatarNode.savedMessagesColors
|
||||||
} else {
|
} else {
|
||||||
colors = AvatarNode.grayscaleColors
|
if let theme, theme.overallDarkAppearance {
|
||||||
|
colors = AvatarNode.grayscaleDarkColors
|
||||||
|
} else {
|
||||||
|
colors = AvatarNode.grayscaleColors
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if case .myNotesIcon = icon {
|
} else if case .myNotesIcon = icon {
|
||||||
colors = AvatarNode.savedMessagesColors
|
colors = AvatarNode.savedMessagesColors
|
||||||
@ -269,6 +274,10 @@ public final class AvatarNode: ASDisplayNode {
|
|||||||
UIColor(rgb: 0xb1b1b1), UIColor(rgb: 0xcdcdcd)
|
UIColor(rgb: 0xb1b1b1), UIColor(rgb: 0xcdcdcd)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
static let grayscaleDarkColors: [UIColor] = [
|
||||||
|
UIColor(white: 1.0, alpha: 0.22), UIColor(white: 1.0, alpha: 0.18)
|
||||||
|
]
|
||||||
|
|
||||||
static let savedMessagesColors: [UIColor] = [
|
static let savedMessagesColors: [UIColor] = [
|
||||||
UIColor(rgb: 0x2a9ef1), UIColor(rgb: 0x72d5fd)
|
UIColor(rgb: 0x2a9ef1), UIColor(rgb: 0x72d5fd)
|
||||||
]
|
]
|
||||||
@ -928,8 +937,14 @@ public final class AvatarNode: ASDisplayNode {
|
|||||||
context.scaleBy(x: factor, y: -factor)
|
context.scaleBy(x: factor, y: -factor)
|
||||||
context.translateBy(x: -bounds.size.width / 2.0, y: -bounds.size.height / 2.0)
|
context.translateBy(x: -bounds.size.width / 2.0, y: -bounds.size.height / 2.0)
|
||||||
|
|
||||||
if let anonymousSavedMessagesIcon = anonymousSavedMessagesIcon {
|
if let theme = parameters.theme, theme.overallDarkAppearance {
|
||||||
context.draw(anonymousSavedMessagesIcon.cgImage!, in: CGRect(origin: CGPoint(x: floor((bounds.size.width - anonymousSavedMessagesIcon.size.width) / 2.0), y: floor((bounds.size.height - anonymousSavedMessagesIcon.size.height) / 2.0)), size: anonymousSavedMessagesIcon.size))
|
if let anonymousSavedMessagesDarkIcon = anonymousSavedMessagesDarkIcon {
|
||||||
|
context.draw(anonymousSavedMessagesDarkIcon.cgImage!, in: CGRect(origin: CGPoint(x: floor((bounds.size.width - anonymousSavedMessagesDarkIcon.size.width) / 2.0), y: floor((bounds.size.height - anonymousSavedMessagesDarkIcon.size.height) / 2.0)), size: anonymousSavedMessagesDarkIcon.size))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let anonymousSavedMessagesIcon = anonymousSavedMessagesIcon {
|
||||||
|
context.draw(anonymousSavedMessagesIcon.cgImage!, in: CGRect(origin: CGPoint(x: floor((bounds.size.width - anonymousSavedMessagesIcon.size.width) / 2.0), y: floor((bounds.size.height - anonymousSavedMessagesIcon.size.height) / 2.0)), size: anonymousSavedMessagesIcon.size))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if case .myNotesIcon = parameters.icon {
|
} else if case .myNotesIcon = parameters.icon {
|
||||||
let factor = bounds.size.width / 60.0
|
let factor = bounds.size.width / 60.0
|
||||||
|
|||||||
@ -24,10 +24,10 @@ public func isViewVisibleInHierarchy(_ view: UIView, _ initial: Bool = true) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class HierarchyTrackingNode: ASDisplayNode {
|
public final class HierarchyTrackingNode: ASDisplayNode {
|
||||||
private let f: (Bool) -> Void
|
public var updated: (Bool) -> Void
|
||||||
|
|
||||||
public init(_ f: @escaping (Bool) -> Void) {
|
public init(_ f: @escaping (Bool) -> Void = { _ in }) {
|
||||||
self.f = f
|
self.updated = f
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
@ -37,13 +37,13 @@ public final class HierarchyTrackingNode: ASDisplayNode {
|
|||||||
override public func didEnterHierarchy() {
|
override public func didEnterHierarchy() {
|
||||||
super.didEnterHierarchy()
|
super.didEnterHierarchy()
|
||||||
|
|
||||||
self.f(true)
|
self.updated(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func didExitHierarchy() {
|
override public func didExitHierarchy() {
|
||||||
super.didExitHierarchy()
|
super.didExitHierarchy()
|
||||||
|
|
||||||
self.f(false)
|
self.updated(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -57,11 +57,13 @@ private let lockedBackgroundImage: UIImage = generateFilledCircleImage(diameter:
|
|||||||
private let lockedBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/PanelBadgeLock"), color: .white)
|
private let lockedBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/PanelBadgeLock"), color: .white)
|
||||||
|
|
||||||
private final class StarsButtonEffectLayer: SimpleLayer {
|
private final class StarsButtonEffectLayer: SimpleLayer {
|
||||||
|
let gradientLayer = SimpleGradientLayer()
|
||||||
let emitterLayer = CAEmitterLayer()
|
let emitterLayer = CAEmitterLayer()
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
self.addSublayer(self.gradientLayer)
|
||||||
self.addSublayer(self.emitterLayer)
|
self.addSublayer(self.emitterLayer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,8 +75,8 @@ private final class StarsButtonEffectLayer: SimpleLayer {
|
|||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setup() {
|
private func setup(theme: PresentationTheme) {
|
||||||
let color = UIColor(rgb: 0xffbe27)
|
let color = UIColor(rgb: 0xffbe27, alpha: theme.overallDarkAppearance ? 0.2 : 1.0)
|
||||||
|
|
||||||
let emitter = CAEmitterCell()
|
let emitter = CAEmitterCell()
|
||||||
emitter.name = "emitter"
|
emitter.name = "emitter"
|
||||||
@ -101,17 +103,31 @@ private final class StarsButtonEffectLayer: SimpleLayer {
|
|||||||
emitter.setValue([staticColorBehavior], forKey: "emitterBehaviors")
|
emitter.setValue([staticColorBehavior], forKey: "emitterBehaviors")
|
||||||
|
|
||||||
self.emitterLayer.emitterCells = [emitter]
|
self.emitterLayer.emitterCells = [emitter]
|
||||||
|
|
||||||
|
let gradientColor = UIColor(rgb: 0xffbe27, alpha: theme.overallDarkAppearance ? 0.2 : 1.0)
|
||||||
|
|
||||||
|
self.gradientLayer.type = .radial
|
||||||
|
self.gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.5)
|
||||||
|
self.gradientLayer.endPoint = CGPoint(x: 0.0, y: 1.0)
|
||||||
|
self.gradientLayer.colors = [
|
||||||
|
gradientColor.withMultipliedAlpha(0.4).cgColor,
|
||||||
|
gradientColor.withMultipliedAlpha(0.4).cgColor,
|
||||||
|
gradientColor.withMultipliedAlpha(0.25).cgColor,
|
||||||
|
gradientColor.withMultipliedAlpha(0.0).cgColor
|
||||||
|
] as [CGColor]
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(size: CGSize) {
|
func update(theme: PresentationTheme, size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||||
if self.emitterLayer.emitterCells == nil {
|
if self.emitterLayer.emitterCells == nil {
|
||||||
self.setup()
|
self.setup(theme: theme)
|
||||||
}
|
}
|
||||||
self.emitterLayer.emitterShape = .circle
|
self.emitterLayer.emitterShape = .circle
|
||||||
self.emitterLayer.emitterSize = CGSize(width: size.width * 0.7, height: size.height * 0.7)
|
self.emitterLayer.emitterSize = CGSize(width: size.width * 0.7, height: size.height * 0.7)
|
||||||
self.emitterLayer.emitterMode = .surface
|
self.emitterLayer.emitterMode = .surface
|
||||||
self.emitterLayer.frame = CGRect(origin: .zero, size: size)
|
self.emitterLayer.frame = CGRect(origin: .zero, size: size)
|
||||||
self.emitterLayer.emitterPosition = CGPoint(x: size.width / 2.0, y: size.height / 2.0)
|
self.emitterLayer.emitterPosition = CGPoint(x: size.width / 2.0, y: size.height / 2.0)
|
||||||
|
|
||||||
|
transition.updateFrame(layer: self.gradientLayer, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: -6.0, dy: -6.0).offsetBy(dx: 0.0, dy: 2.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,7 +323,7 @@ public final class ReactionNode: ASDisplayNode, ReactionItemNode {
|
|||||||
|
|
||||||
if let starsEffectLayer = self.starsEffectLayer {
|
if let starsEffectLayer = self.starsEffectLayer {
|
||||||
transition.updateFrame(layer: starsEffectLayer, frame: CGRect(origin: CGPoint(), size: size))
|
transition.updateFrame(layer: starsEffectLayer, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
starsEffectLayer.update(size: size)
|
starsEffectLayer.update(theme: self.theme, size: size, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
let animationSize = self.item.stillAnimation.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0)
|
let animationSize = self.item.stillAnimation.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0)
|
||||||
|
|||||||
@ -842,7 +842,7 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
|
|||||||
}
|
}
|
||||||
|
|
||||||
if case .phoneNumber = kind, state.setting == .nobody {
|
if case .phoneNumber = kind, state.setting == .nobody {
|
||||||
if state.phoneDiscoveryEnabled == false {
|
if state.phoneDiscoveryEnabled == false || phoneNumber.hasPrefix("888") {
|
||||||
entries.append(.phoneDiscoveryHeader(presentationData.theme, presentationData.strings.PrivacyPhoneNumberSettings_DiscoveryHeader))
|
entries.append(.phoneDiscoveryHeader(presentationData.theme, presentationData.strings.PrivacyPhoneNumberSettings_DiscoveryHeader))
|
||||||
entries.append(.phoneDiscoveryEverybody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenEverybody, state.phoneDiscoveryEnabled != false))
|
entries.append(.phoneDiscoveryEverybody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenEverybody, state.phoneDiscoveryEnabled != false))
|
||||||
entries.append(.phoneDiscoveryMyContacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.phoneDiscoveryEnabled == false))
|
entries.append(.phoneDiscoveryMyContacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.phoneDiscoveryEnabled == false))
|
||||||
|
|||||||
@ -716,7 +716,11 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
|
|||||||
|
|
||||||
var authorId: PeerId?
|
var authorId: PeerId?
|
||||||
if let sendAsPeer = sendAsPeer {
|
if let sendAsPeer = sendAsPeer {
|
||||||
authorId = sendAsPeer.id
|
if let peer = peer as? TelegramChannel, case let .broadcast(info) = peer.info, info.flags.contains(.messagesShouldHaveProfiles) {
|
||||||
|
authorId = sendAsPeer.id
|
||||||
|
} else {
|
||||||
|
authorId = peer.id
|
||||||
|
}
|
||||||
} else if let peer = peer as? TelegramChannel {
|
} else if let peer = peer as? TelegramChannel {
|
||||||
if case .broadcast = peer.info {
|
if case .broadcast = peer.info {
|
||||||
authorId = peer.id
|
authorId = peer.id
|
||||||
@ -748,7 +752,11 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if info.flags.contains(.messagesShouldHaveSignatures) {
|
if info.flags.contains(.messagesShouldHaveSignatures) {
|
||||||
if let sendAsPeer, sendAsPeer.id == peerId {
|
if let sendAsPeer {
|
||||||
|
if sendAsPeer.id == peerId {
|
||||||
|
} else {
|
||||||
|
attributes.append(AuthorSignatureMessageAttribute(signature: sendAsPeer.debugDisplayTitle))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
attributes.append(AuthorSignatureMessageAttribute(signature: accountPeer.debugDisplayTitle))
|
attributes.append(AuthorSignatureMessageAttribute(signature: accountPeer.debugDisplayTitle))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -109,13 +109,7 @@ func _internal_peerSendAsAvailablePeers(accountPeerId: PeerId, network: Network,
|
|||||||
return .single([])
|
return .single([])
|
||||||
}
|
}
|
||||||
|
|
||||||
if let channel = peer as? TelegramChannel {
|
if let _ = peer as? TelegramChannel {
|
||||||
if case .group = channel.info {
|
|
||||||
} else if case let .broadcast(info) = channel.info {
|
|
||||||
if !info.flags.contains(.messagesShouldHaveProfiles) {
|
|
||||||
return .single([])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return .single([])
|
return .single([])
|
||||||
}
|
}
|
||||||
|
|||||||
@ -531,7 +531,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
|
|||||||
reactionInactiveForeground: UIColor(rgb: 0xffffff),
|
reactionInactiveForeground: UIColor(rgb: 0xffffff),
|
||||||
reactionActiveBackground: UIColor(rgb: 0xffffff, alpha: 1.0),
|
reactionActiveBackground: UIColor(rgb: 0xffffff, alpha: 1.0),
|
||||||
reactionActiveForeground: .clear,
|
reactionActiveForeground: .clear,
|
||||||
reactionStarsInactiveBackground: UIColor(rgb: 0xFEF1D4, alpha: 1.0),
|
reactionStarsInactiveBackground: UIColor(rgb: 0xD3720A, alpha: 0.2),
|
||||||
reactionStarsInactiveForeground: UIColor(rgb: 0xD3720A),
|
reactionStarsInactiveForeground: UIColor(rgb: 0xD3720A),
|
||||||
reactionStarsActiveBackground: UIColor(rgb: 0xD3720A, alpha: 1.0),
|
reactionStarsActiveBackground: UIColor(rgb: 0xD3720A, alpha: 1.0),
|
||||||
reactionStarsActiveForeground: .white,
|
reactionStarsActiveForeground: .white,
|
||||||
@ -547,7 +547,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
|
|||||||
reactionInactiveForeground: UIColor(rgb: 0xffffff),
|
reactionInactiveForeground: UIColor(rgb: 0xffffff),
|
||||||
reactionActiveBackground: UIColor(rgb: 0xffffff, alpha: 1.0),
|
reactionActiveBackground: UIColor(rgb: 0xffffff, alpha: 1.0),
|
||||||
reactionActiveForeground: .clear,
|
reactionActiveForeground: .clear,
|
||||||
reactionStarsInactiveBackground: UIColor(rgb: 0xFEF1D4, alpha: 1.0),
|
reactionStarsInactiveBackground: UIColor(rgb: 0xD3720A, alpha: 0.2),
|
||||||
reactionStarsInactiveForeground: UIColor(rgb: 0xD3720A),
|
reactionStarsInactiveForeground: UIColor(rgb: 0xD3720A),
|
||||||
reactionStarsActiveBackground: UIColor(rgb: 0xD3720A, alpha: 1.0),
|
reactionStarsActiveBackground: UIColor(rgb: 0xD3720A, alpha: 1.0),
|
||||||
reactionStarsActiveForeground: .white,
|
reactionStarsActiveForeground: .white,
|
||||||
@ -569,7 +569,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
|
|||||||
reactionInactiveForeground: UIColor(rgb: 0xffffff),
|
reactionInactiveForeground: UIColor(rgb: 0xffffff),
|
||||||
reactionActiveBackground: UIColor(rgb: 0xffffff, alpha: 1.0),
|
reactionActiveBackground: UIColor(rgb: 0xffffff, alpha: 1.0),
|
||||||
reactionActiveForeground: .clear,
|
reactionActiveForeground: .clear,
|
||||||
reactionStarsInactiveBackground: UIColor(rgb: 0xFEF1D4, alpha: 1.0),
|
reactionStarsInactiveBackground: UIColor(rgb: 0xD3720A, alpha: 0.2),
|
||||||
reactionStarsInactiveForeground: UIColor(rgb: 0xD3720A),
|
reactionStarsInactiveForeground: UIColor(rgb: 0xD3720A),
|
||||||
reactionStarsActiveBackground: UIColor(rgb: 0xD3720A, alpha: 1.0),
|
reactionStarsActiveBackground: UIColor(rgb: 0xD3720A, alpha: 1.0),
|
||||||
reactionStarsActiveForeground: .white,
|
reactionStarsActiveForeground: .white,
|
||||||
@ -585,7 +585,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
|
|||||||
reactionInactiveForeground: UIColor(rgb: 0xffffff),
|
reactionInactiveForeground: UIColor(rgb: 0xffffff),
|
||||||
reactionActiveBackground: UIColor(rgb: 0xffffff, alpha: 1.0),
|
reactionActiveBackground: UIColor(rgb: 0xffffff, alpha: 1.0),
|
||||||
reactionActiveForeground: .clear,
|
reactionActiveForeground: .clear,
|
||||||
reactionStarsInactiveBackground: UIColor(rgb: 0xFEF1D4, alpha: 1.0),
|
reactionStarsInactiveBackground: UIColor(rgb: 0xD3720A, alpha: 0.2),
|
||||||
reactionStarsInactiveForeground: UIColor(rgb: 0xD3720A),
|
reactionStarsInactiveForeground: UIColor(rgb: 0xD3720A),
|
||||||
reactionStarsActiveBackground: UIColor(rgb: 0xD3720A, alpha: 1.0),
|
reactionStarsActiveBackground: UIColor(rgb: 0xD3720A, alpha: 1.0),
|
||||||
reactionStarsActiveForeground: .white,
|
reactionStarsActiveForeground: .white,
|
||||||
@ -604,7 +604,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
|
|||||||
reactionInactiveForeground: UIColor(rgb: 0xffffff),
|
reactionInactiveForeground: UIColor(rgb: 0xffffff),
|
||||||
reactionActiveBackground: UIColor(rgb: 0xffffff, alpha: 1.0),
|
reactionActiveBackground: UIColor(rgb: 0xffffff, alpha: 1.0),
|
||||||
reactionActiveForeground: .clear,
|
reactionActiveForeground: .clear,
|
||||||
reactionStarsInactiveBackground: UIColor(rgb: 0xFEF1D4, alpha: 1.0),
|
reactionStarsInactiveBackground: UIColor(rgb: 0xD3720A, alpha: 0.2),
|
||||||
reactionStarsInactiveForeground: UIColor(rgb: 0xD3720A),
|
reactionStarsInactiveForeground: UIColor(rgb: 0xD3720A),
|
||||||
reactionStarsActiveBackground: UIColor(rgb: 0xD3720A, alpha: 1.0),
|
reactionStarsActiveBackground: UIColor(rgb: 0xD3720A, alpha: 1.0),
|
||||||
reactionStarsActiveForeground: .white,
|
reactionStarsActiveForeground: .white,
|
||||||
@ -620,7 +620,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
|
|||||||
reactionInactiveForeground: UIColor(rgb: 0xffffff),
|
reactionInactiveForeground: UIColor(rgb: 0xffffff),
|
||||||
reactionActiveBackground: UIColor(rgb: 0xffffff, alpha: 1.0),
|
reactionActiveBackground: UIColor(rgb: 0xffffff, alpha: 1.0),
|
||||||
reactionActiveForeground: .clear,
|
reactionActiveForeground: .clear,
|
||||||
reactionStarsInactiveBackground: UIColor(rgb: 0xFEF1D4, alpha: 1.0),
|
reactionStarsInactiveBackground: UIColor(rgb: 0xD3720A, alpha: 0.2),
|
||||||
reactionStarsInactiveForeground: UIColor(rgb: 0xD3720A),
|
reactionStarsInactiveForeground: UIColor(rgb: 0xD3720A),
|
||||||
reactionStarsActiveBackground: UIColor(rgb: 0xD3720A, alpha: 1.0),
|
reactionStarsActiveBackground: UIColor(rgb: 0xD3720A, alpha: 1.0),
|
||||||
reactionStarsActiveForeground: .white,
|
reactionStarsActiveForeground: .white,
|
||||||
|
|||||||
@ -128,22 +128,15 @@ private final class BalanceComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class BadgeComponent: Component {
|
private final class BadgeComponent: Component {
|
||||||
enum Direction {
|
|
||||||
case left
|
|
||||||
case right
|
|
||||||
}
|
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let title: String
|
let title: String
|
||||||
let inertiaDirection: Direction?
|
|
||||||
|
|
||||||
init(
|
init(
|
||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
title: String,
|
title: String
|
||||||
inertiaDirection: Direction?
|
|
||||||
) {
|
) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.title = title
|
self.title = title
|
||||||
self.inertiaDirection = inertiaDirection
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: BadgeComponent, rhs: BadgeComponent) -> Bool {
|
static func ==(lhs: BadgeComponent, rhs: BadgeComponent) -> Bool {
|
||||||
@ -153,9 +146,6 @@ private final class BadgeComponent: Component {
|
|||||||
if lhs.title != rhs.title {
|
if lhs.title != rhs.title {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.inertiaDirection != rhs.inertiaDirection {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +165,6 @@ private final class BadgeComponent: Component {
|
|||||||
private var component: BadgeComponent?
|
private var component: BadgeComponent?
|
||||||
|
|
||||||
private var previousAvailableSize: CGSize?
|
private var previousAvailableSize: CGSize?
|
||||||
private var previousInertiaDirection: BadgeComponent.Direction?
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
self.badgeView = UIView()
|
self.badgeView = UIView()
|
||||||
@ -189,6 +178,7 @@ private final class BadgeComponent: Component {
|
|||||||
self.badgeView.mask = self.badgeMaskView
|
self.badgeView.mask = self.badgeMaskView
|
||||||
|
|
||||||
self.badgeForeground = SimpleLayer()
|
self.badgeForeground = SimpleLayer()
|
||||||
|
self.badgeForeground.anchorPoint = CGPoint()
|
||||||
|
|
||||||
self.badgeIcon = UIImageView()
|
self.badgeIcon = UIImageView()
|
||||||
self.badgeIcon.contentMode = .center
|
self.badgeIcon.contentMode = .center
|
||||||
@ -257,31 +247,7 @@ private final class BadgeComponent: Component {
|
|||||||
|
|
||||||
self.badgeView.bounds = CGRect(origin: .zero, size: badgeFullSize)
|
self.badgeView.bounds = CGRect(origin: .zero, size: badgeFullSize)
|
||||||
|
|
||||||
transition.setAnchorPoint(layer: self.badgeView.layer, anchorPoint: CGPoint(x: 0.5, y: 1.0))
|
self.badgeForeground.bounds = CGRect(origin: CGPoint(), size: CGSize(width: 600.0, height: badgeFullSize.height))
|
||||||
|
|
||||||
if component.inertiaDirection != self.previousInertiaDirection {
|
|
||||||
self.previousInertiaDirection = component.inertiaDirection
|
|
||||||
|
|
||||||
var angle: CGFloat = 0.0
|
|
||||||
let transition: ContainedViewLayoutTransition
|
|
||||||
if let inertiaDirection = component.inertiaDirection {
|
|
||||||
switch inertiaDirection {
|
|
||||||
case .left:
|
|
||||||
angle = 0.22
|
|
||||||
case .right:
|
|
||||||
angle = -0.22
|
|
||||||
}
|
|
||||||
transition = .animated(duration: 0.45, curve: .spring)
|
|
||||||
} else {
|
|
||||||
transition = .animated(duration: 0.45, curve: .customSpring(damping: 65.0, initialVelocity: 0.0))
|
|
||||||
}
|
|
||||||
transition.updateTransformRotation(view: self.badgeView, angle: angle)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.badgeForeground.bounds = CGRect(origin: CGPoint(), size: CGSize(width: badgeFullSize.width * 3.0, height: badgeFullSize.height))
|
|
||||||
if self.badgeForeground.animation(forKey: "movement") == nil {
|
|
||||||
self.badgeForeground.position = CGPoint(x: badgeSize.width * 3.0 / 2.0 - self.badgeForeground.frame.width * 0.35, y: badgeFullSize.height / 2.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.badgeIcon.frame = CGRect(x: 10.0, y: 9.0, width: 30.0, height: 30.0)
|
self.badgeIcon.frame = CGRect(x: 10.0, y: 9.0, width: 30.0, height: 30.0)
|
||||||
self.badgeLabelMaskView.frame = CGRect(x: 0.0, y: 0.0, width: 100.0, height: 36.0)
|
self.badgeLabelMaskView.frame = CGRect(x: 0.0, y: 0.0, width: 100.0, height: 36.0)
|
||||||
@ -320,7 +286,17 @@ private final class BadgeComponent: Component {
|
|||||||
tailPosition += overflowWidth
|
tailPosition += overflowWidth
|
||||||
tailPosition = max(0.0, min(size.width, tailPosition))
|
tailPosition = max(0.0, min(size.width, tailPosition))
|
||||||
|
|
||||||
self.badgeShapeLayer.path = generateRoundedRectWithTailPath(rectSize: size, tailPosition: tailPosition / size.width).cgPath
|
let tailPositionFraction = tailPosition / size.width
|
||||||
|
self.badgeShapeLayer.path = generateRoundedRectWithTailPath(rectSize: size, tailPosition: tailPositionFraction).cgPath
|
||||||
|
|
||||||
|
let transition: ContainedViewLayoutTransition = .immediate
|
||||||
|
transition.updateAnchorPoint(layer: self.badgeView.layer, anchorPoint: CGPoint(x: tailPositionFraction, y: 1.0))
|
||||||
|
transition.updatePosition(layer: self.badgeView.layer, position: CGPoint(x: (tailPositionFraction - 0.5) * size.width, y: 0.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateBadgeAngle(angle: CGFloat) {
|
||||||
|
let transition: ContainedViewLayoutTransition = .immediate
|
||||||
|
transition.updateTransformRotation(view: self.badgeView, angle: angle)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupGradientAnimations() {
|
private func setupGradientAnimations() {
|
||||||
@ -331,13 +307,14 @@ private final class BadgeComponent: Component {
|
|||||||
} else {
|
} else {
|
||||||
CATransaction.begin()
|
CATransaction.begin()
|
||||||
|
|
||||||
let badgeOffset = (self.badgeForeground.frame.width - self.badgeView.bounds.width) / 2.0
|
|
||||||
let badgePreviousValue = self.badgeForeground.position.x
|
let badgePreviousValue = self.badgeForeground.position.x
|
||||||
var badgeNewValue: CGFloat = badgeOffset
|
let badgeNewValue: CGFloat
|
||||||
if badgeOffset - badgePreviousValue < self.badgeForeground.frame.width * 0.25 {
|
if self.badgeForeground.position.x == -300.0 {
|
||||||
badgeNewValue -= self.badgeForeground.frame.width * 0.35
|
badgeNewValue = 0.0
|
||||||
|
} else {
|
||||||
|
badgeNewValue = -300.0
|
||||||
}
|
}
|
||||||
self.badgeForeground.position = CGPoint(x: badgeNewValue, y: self.badgeForeground.bounds.size.height / 2.0)
|
self.badgeForeground.position = CGPoint(x: badgeNewValue, y: self.badgeForeground.bounds.size.height)
|
||||||
|
|
||||||
let badgeAnimation = CABasicAnimation(keyPath: "position.x")
|
let badgeAnimation = CABasicAnimation(keyPath: "position.x")
|
||||||
badgeAnimation.duration = 4.5
|
badgeAnimation.duration = 4.5
|
||||||
@ -1007,6 +984,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
private let scrollView: ScrollView
|
private let scrollView: ScrollView
|
||||||
private let scrollContentClippingView: SparseContainerView
|
private let scrollContentClippingView: SparseContainerView
|
||||||
private let scrollContentView: UIView
|
private let scrollContentView: UIView
|
||||||
|
private let hierarchyTrackingNode: HierarchyTrackingNode
|
||||||
|
|
||||||
private let leftButton = ComponentView<Empty>()
|
private let leftButton = ComponentView<Empty>()
|
||||||
private let closeButton = ComponentView<Empty>()
|
private let closeButton = ComponentView<Empty>()
|
||||||
@ -1056,6 +1034,8 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
|
|
||||||
private var balanceDisposable: Disposable?
|
private var balanceDisposable: Disposable?
|
||||||
|
|
||||||
|
private var badgePhysicsLink: SharedDisplayLinkDriver.Link?
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
self.bottomOverscrollLimit = 200.0
|
self.bottomOverscrollLimit = 200.0
|
||||||
|
|
||||||
@ -1074,6 +1054,8 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
|
|
||||||
self.scrollContentView = UIView()
|
self.scrollContentView = UIView()
|
||||||
|
|
||||||
|
self.hierarchyTrackingNode = HierarchyTrackingNode()
|
||||||
|
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
self.addSubview(self.dimView)
|
self.addSubview(self.dimView)
|
||||||
@ -1104,6 +1086,30 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
self.addSubview(self.navigationBarContainer)
|
self.addSubview(self.navigationBarContainer)
|
||||||
|
|
||||||
self.dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
|
self.dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
|
||||||
|
|
||||||
|
self.addSubnode(self.hierarchyTrackingNode)
|
||||||
|
|
||||||
|
self.hierarchyTrackingNode.updated = { [weak self] value in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if value {
|
||||||
|
if self.badgePhysicsLink == nil {
|
||||||
|
let badgePhysicsLink = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] _ in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.updateBadgePhysics()
|
||||||
|
})
|
||||||
|
self.badgePhysicsLink = badgePhysicsLink
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let badgePhysicsLink = self.badgePhysicsLink {
|
||||||
|
self.badgePhysicsLink = nil
|
||||||
|
badgePhysicsLink.invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
@ -1175,7 +1181,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
|
|
||||||
transition.setPosition(view: self.navigationBarContainer, position: CGPoint(x: 0.0, y: topOffset + itemLayout.containerInset))
|
transition.setPosition(view: self.navigationBarContainer, position: CGPoint(x: 0.0, y: topOffset + itemLayout.containerInset))
|
||||||
|
|
||||||
let topOffsetDistance: CGFloat = min(200.0, floor(itemLayout.containerSize.height * 0.25))
|
let topOffsetDistance: CGFloat = min(60.0, floor(itemLayout.containerSize.height * 0.25))
|
||||||
self.topOffsetDistance = topOffsetDistance
|
self.topOffsetDistance = topOffsetDistance
|
||||||
var topOffsetFraction = topOffset / topOffsetDistance
|
var topOffsetFraction = topOffset / topOffsetDistance
|
||||||
topOffsetFraction = max(0.0, min(1.0, topOffsetFraction))
|
topOffsetFraction = max(0.0, min(1.0, topOffsetFraction))
|
||||||
@ -1217,7 +1223,78 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
|
|
||||||
private var previousSliderValue: Float = 0.0
|
private var previousSliderValue: Float = 0.0
|
||||||
private var previousTimestamp: Double?
|
private var previousTimestamp: Double?
|
||||||
private var inertiaDirection: BadgeComponent.Direction?
|
|
||||||
|
private var badgeAngularSpeed: CGFloat = 0.0
|
||||||
|
private var badgeAngle: CGFloat = 0.0
|
||||||
|
private var previousBadgeX: CGFloat?
|
||||||
|
private var previousPhysicsTimestamp: Double?
|
||||||
|
|
||||||
|
private func updateBadgePhysics() {
|
||||||
|
let timestamp = CACurrentMediaTime()
|
||||||
|
|
||||||
|
let deltaTime: CGFloat
|
||||||
|
if let previousPhysicsTimestamp = self.previousPhysicsTimestamp {
|
||||||
|
deltaTime = CGFloat(min(1.0 / 60.0, timestamp - previousPhysicsTimestamp))
|
||||||
|
} else {
|
||||||
|
deltaTime = CGFloat(1.0 / 60.0)
|
||||||
|
}
|
||||||
|
self.previousPhysicsTimestamp = timestamp
|
||||||
|
|
||||||
|
guard let badgeView = self.badge.view as? BadgeComponent.View else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let badgeX = badgeView.center.x
|
||||||
|
|
||||||
|
let horizontalVelocity: CGFloat
|
||||||
|
if let previousBadgeX = self.previousBadgeX {
|
||||||
|
horizontalVelocity = (badgeX - previousBadgeX) / deltaTime
|
||||||
|
} else {
|
||||||
|
horizontalVelocity = 0.0
|
||||||
|
}
|
||||||
|
self.previousBadgeX = badgeX
|
||||||
|
|
||||||
|
let testSpringFriction: CGFloat = 9.0
|
||||||
|
let testSpringConstant: CGFloat = 243.0
|
||||||
|
|
||||||
|
let frictionConstant: CGFloat = testSpringFriction
|
||||||
|
let springConstant: CGFloat = testSpringConstant
|
||||||
|
let time: CGFloat = deltaTime
|
||||||
|
|
||||||
|
var badgeAngle = self.badgeAngle
|
||||||
|
|
||||||
|
badgeAngle -= horizontalVelocity * 0.0001
|
||||||
|
if abs(badgeAngle) > 0.22 {
|
||||||
|
badgeAngle = badgeAngle < 0.0 ? -0.22 : 0.22
|
||||||
|
}
|
||||||
|
|
||||||
|
// friction force = velocity * friction constant
|
||||||
|
let frictionForce = self.badgeAngularSpeed * frictionConstant
|
||||||
|
// spring force = (target point - current position) * spring constant
|
||||||
|
let springForce = -badgeAngle * springConstant
|
||||||
|
// force = spring force - friction force
|
||||||
|
let force = springForce - frictionForce
|
||||||
|
|
||||||
|
// velocity = current velocity + force * time / mass
|
||||||
|
self.badgeAngularSpeed = self.badgeAngularSpeed + force * time
|
||||||
|
// position = current position + velocity * time
|
||||||
|
badgeAngle = badgeAngle + self.badgeAngularSpeed * time
|
||||||
|
badgeAngle = badgeAngle.isNaN ? 0.0 : badgeAngle
|
||||||
|
|
||||||
|
let epsilon: CGFloat = 0.01
|
||||||
|
if abs(badgeAngle) < epsilon && abs(self.badgeAngularSpeed) < epsilon {
|
||||||
|
badgeAngle = 0.0
|
||||||
|
self.badgeAngularSpeed = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
if abs(badgeAngle) > 0.22 {
|
||||||
|
badgeAngle = badgeAngle < 0.0 ? -0.22 : 0.22
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.badgeAngle != badgeAngle {
|
||||||
|
self.badgeAngle = badgeAngle
|
||||||
|
badgeView.updateBadgeAngle(angle: self.badgeAngle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func update(component: ChatSendStarsScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<ViewControllerComponentContainer.Environment>, transition: ComponentTransition) -> CGSize {
|
func update(component: ChatSendStarsScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<ViewControllerComponentContainer.Environment>, transition: ComponentTransition) -> CGSize {
|
||||||
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
||||||
@ -1225,7 +1302,13 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
|
|
||||||
let resetScrolling = self.scrollView.bounds.width != availableSize.width
|
let resetScrolling = self.scrollView.bounds.width != availableSize.width
|
||||||
|
|
||||||
let sideInset: CGFloat = 16.0
|
let fillingSize: CGFloat
|
||||||
|
if case .regular = environment.metrics.widthClass {
|
||||||
|
fillingSize = min(availableSize.width, 414.0) - environment.safeInsets.left * 2.0
|
||||||
|
} else {
|
||||||
|
fillingSize = min(availableSize.width, 428.0) - environment.safeInsets.left * 2.0
|
||||||
|
}
|
||||||
|
let sideInset: CGFloat = floor((availableSize.width - fillingSize) * 0.5) + 16.0
|
||||||
|
|
||||||
if self.component == nil {
|
if self.component == nil {
|
||||||
self.balance = component.balance
|
self.balance = component.balance
|
||||||
@ -1307,21 +1390,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
let speed = deltaValue / Float(deltaTime)
|
let speed = deltaValue / Float(deltaTime)
|
||||||
let newSpeed = max(0, min(65.0, speed * 70.0))
|
let newSpeed = max(0, min(65.0, speed * 70.0))
|
||||||
|
|
||||||
var inertiaDirection: BadgeComponent.Direction?
|
|
||||||
if newSpeed >= 1.0 {
|
|
||||||
if delta > 0.0 {
|
|
||||||
inertiaDirection = .right
|
|
||||||
} else {
|
|
||||||
inertiaDirection = .left
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if inertiaDirection != self.inertiaDirection {
|
|
||||||
self.inertiaDirection = inertiaDirection
|
|
||||||
self.state?.updated(transition: .immediate)
|
|
||||||
}
|
|
||||||
|
|
||||||
if newSpeed < 0.01 && deltaValue < 0.001 {
|
if newSpeed < 0.01 && deltaValue < 0.001 {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
self.badgeStars.update(speed: newSpeed, delta: delta)
|
self.badgeStars.update(speed: newSpeed, delta: delta)
|
||||||
}
|
}
|
||||||
@ -1338,10 +1407,6 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
self.previousTimestamp = nil
|
self.previousTimestamp = nil
|
||||||
self.badgeStars.update(speed: 0.0)
|
self.badgeStars.update(speed: 0.0)
|
||||||
}
|
}
|
||||||
if self.inertiaDirection != nil {
|
|
||||||
self.inertiaDirection = nil
|
|
||||||
self.state?.updated(transition: .immediate)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
@ -1401,17 +1466,11 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
|
|
||||||
transition.setFrame(view: sliderBackgroundView, frame: sliderBackgroundFrame)
|
transition.setFrame(view: sliderBackgroundView, frame: sliderBackgroundFrame)
|
||||||
|
|
||||||
var effectiveInertiaDirection = self.inertiaDirection
|
|
||||||
if progressFraction <= 0.03 || progressFraction >= 0.97 {
|
|
||||||
effectiveInertiaDirection = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let badgeSize = self.badge.update(
|
let badgeSize = self.badge.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
component: AnyComponent(BadgeComponent(
|
component: AnyComponent(BadgeComponent(
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
title: "\(self.amount.realValue)",
|
title: "\(self.amount.realValue)"
|
||||||
inertiaDirection: effectiveInertiaDirection
|
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: 200.0, height: 200.0)
|
containerSize: CGSize(width: 200.0, height: 200.0)
|
||||||
@ -1463,7 +1522,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: 120.0, height: 100.0)
|
containerSize: CGSize(width: 120.0, height: 100.0)
|
||||||
)
|
)
|
||||||
let leftButtonFrame = CGRect(origin: CGPoint(x: 16.0, y: floor((56.0 - leftButtonSize.height) * 0.5)), size: leftButtonSize)
|
let leftButtonFrame = CGRect(origin: CGPoint(x: sideInset, y: floor((56.0 - leftButtonSize.height) * 0.5)), size: leftButtonSize)
|
||||||
if let leftButtonView = self.leftButton.view {
|
if let leftButtonView = self.leftButton.view {
|
||||||
if leftButtonView.superview == nil {
|
if leftButtonView.superview == nil {
|
||||||
self.navigationBarContainer.addSubview(leftButtonView)
|
self.navigationBarContainer.addSubview(leftButtonView)
|
||||||
@ -2005,7 +2064,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
transition.setFrame(view: self.scrollContentView, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset + containerInset), size: CGSize(width: availableSize.width, height: contentHeight)))
|
transition.setFrame(view: self.scrollContentView, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset + containerInset), size: CGSize(width: availableSize.width, height: contentHeight)))
|
||||||
|
|
||||||
transition.setPosition(layer: self.backgroundLayer, position: CGPoint(x: availableSize.width / 2.0, y: availableSize.height / 2.0))
|
transition.setPosition(layer: self.backgroundLayer, position: CGPoint(x: availableSize.width / 2.0, y: availableSize.height / 2.0))
|
||||||
transition.setBounds(layer: self.backgroundLayer, bounds: CGRect(origin: CGPoint(), size: availableSize))
|
transition.setBounds(layer: self.backgroundLayer, bounds: CGRect(origin: CGPoint(), size: CGSize(width: fillingSize, height: availableSize.height)))
|
||||||
|
|
||||||
let scrollClippingFrame = CGRect(origin: CGPoint(x: 0.0, y: containerInset), size: CGSize(width: availableSize.width, height: clippingY - containerInset))
|
let scrollClippingFrame = CGRect(origin: CGPoint(x: 0.0, y: containerInset), size: CGSize(width: availableSize.width, height: clippingY - containerInset))
|
||||||
transition.setPosition(view: self.scrollContentClippingView, position: scrollClippingFrame.center)
|
transition.setPosition(view: self.scrollContentClippingView, position: scrollClippingFrame.center)
|
||||||
|
|||||||
@ -154,13 +154,17 @@ final class PeerAllowedReactionsScreenComponent: Component {
|
|||||||
if Set(availableReactions.reactions.filter({ $0.isEnabled }).map(\.value)) == Set(enabledReactions.map(\.reaction)) {
|
if Set(availableReactions.reactions.filter({ $0.isEnabled }).map(\.value)) == Set(enabledReactions.map(\.reaction)) {
|
||||||
allowedReactions = .all
|
allowedReactions = .all
|
||||||
} else {
|
} else {
|
||||||
allowedReactions = .limited(enabledReactions.map(\.reaction))
|
if enabledReactions.isEmpty {
|
||||||
|
allowedReactions = .empty
|
||||||
|
} else {
|
||||||
|
allowedReactions = .limited(enabledReactions.map(\.reaction))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
allowedReactions = .empty
|
allowedReactions = .empty
|
||||||
}
|
}
|
||||||
|
|
||||||
let reactionSettings = PeerReactionSettings(allowedReactions: allowedReactions, maxReactionCount: self.allowedReactionCount >= 11 ? nil : Int32(self.allowedReactionCount), starsAllowed: self.areStarsReactionsEnabled)
|
let reactionSettings = PeerReactionSettings(allowedReactions: allowedReactions, maxReactionCount: self.allowedReactionCount >= 11 ? nil : Int32(self.allowedReactionCount), starsAllowed: self.isEnabled && self.areStarsReactionsEnabled)
|
||||||
|
|
||||||
if self.appliedReactionSettings != reactionSettings {
|
if self.appliedReactionSettings != reactionSettings {
|
||||||
if case .empty = allowedReactions {
|
if case .empty = allowedReactions {
|
||||||
@ -255,7 +259,7 @@ final class PeerAllowedReactionsScreenComponent: Component {
|
|||||||
} else {
|
} else {
|
||||||
allowedReactions = .empty
|
allowedReactions = .empty
|
||||||
}
|
}
|
||||||
let reactionSettings = PeerReactionSettings(allowedReactions: allowedReactions, maxReactionCount: self.allowedReactionCount == 11 ? nil : Int32(self.allowedReactionCount), starsAllowed: self.areStarsReactionsEnabled)
|
let reactionSettings = PeerReactionSettings(allowedReactions: allowedReactions, maxReactionCount: self.allowedReactionCount == 11 ? nil : Int32(self.allowedReactionCount), starsAllowed: self.isEnabled && self.areStarsReactionsEnabled)
|
||||||
|
|
||||||
let applyDisposable = (component.context.engine.peers.updatePeerReactionSettings(peerId: component.peerId, reactionSettings: reactionSettings)
|
let applyDisposable = (component.context.engine.peers.updatePeerReactionSettings(peerId: component.peerId, reactionSettings: reactionSettings)
|
||||||
|> deliverOnMainQueue).start(error: { [weak self] error in
|
|> deliverOnMainQueue).start(error: { [weak self] error in
|
||||||
@ -603,7 +607,7 @@ final class PeerAllowedReactionsScreenComponent: Component {
|
|||||||
self.displayInput = false
|
self.displayInput = false
|
||||||
}
|
}
|
||||||
|
|
||||||
self.state?.updated(transition: .easeInOut(duration: 0.25))
|
self.state?.updated(transition: .immediate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)),
|
)),
|
||||||
|
|||||||
@ -294,7 +294,7 @@ extension ChatControllerImpl {
|
|||||||
if !hasAnonymousPeer {
|
if !hasAnonymousPeer {
|
||||||
allPeers?.insert(SendAsPeer(peer: channel, subscribers: 0, isPremiumRequired: false), at: 0)
|
allPeers?.insert(SendAsPeer(peer: channel, subscribers: 0, isPremiumRequired: false), at: 0)
|
||||||
}
|
}
|
||||||
} else if let channel = peerViewMainPeer(peerView) as? TelegramChannel, case let .broadcast(info) = channel.info, info.flags.contains(.messagesShouldHaveProfiles) {
|
} else if let channel = peerViewMainPeer(peerView) as? TelegramChannel, case .broadcast = channel.info {
|
||||||
allPeers = peers
|
allPeers = peers
|
||||||
|
|
||||||
var hasAnonymousPeer = false
|
var hasAnonymousPeer = false
|
||||||
|
|||||||
@ -375,7 +375,9 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
if let itemNode = itemNode, let targetView = itemNode.targetReactionView(value: chosenReaction) {
|
if let itemNode = itemNode, let targetView = itemNode.targetReactionView(value: chosenReaction) {
|
||||||
if !"".isEmpty {
|
if !"".isEmpty {
|
||||||
self.chatDisplayNode.wrappingNode.triggerRipple(at: targetView.convert(targetView.bounds.center, to: self.chatDisplayNode.view))
|
if self.context.sharedContext.energyUsageSettings.fullTranslucency {
|
||||||
|
self.chatDisplayNode.wrappingNode.triggerRipple(at: targetView.convert(targetView.bounds.center, to: self.chatDisplayNode.view))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, completion: {})
|
}, completion: {})
|
||||||
@ -390,13 +392,29 @@ extension ChatControllerImpl {
|
|||||||
guard let starsContext = self.context.starsContext else {
|
guard let starsContext = self.context.starsContext else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = (starsContext.state
|
guard let peerId = self.chatLocation.peerId else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let _ = (combineLatest(
|
||||||
|
starsContext.state,
|
||||||
|
self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ReactionSettings(id: peerId))
|
||||||
|
)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
|> deliverOnMainQueue).start(next: { [weak self] state, reactionSettings in
|
||||||
guard let strongSelf = self, let balance = state?.balance else {
|
guard let strongSelf = self, let balance = state?.balance else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if case let .known(reactionSettings) = reactionSettings, let starsAllowed = reactionSettings.starsAllowed, !starsAllowed {
|
||||||
|
if let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer {
|
||||||
|
//TODO:localize
|
||||||
|
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: "Star Reactions were disabled in \(peer.debugDisplayTitle).", actions: [
|
||||||
|
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {})
|
||||||
|
]), in: .window(.root))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if balance < 1 {
|
if balance < 1 {
|
||||||
controller?.dismiss(completion: {
|
controller?.dismiss(completion: {
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -406,7 +424,7 @@ extension ChatControllerImpl {
|
|||||||
let _ = (strongSelf.context.engine.payments.starsTopUpOptions()
|
let _ = (strongSelf.context.engine.payments.starsTopUpOptions()
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).startStandalone(next: { [weak strongSelf] options in
|
|> deliverOnMainQueue).startStandalone(next: { [weak strongSelf] options in
|
||||||
guard let strongSelf, let peerId = strongSelf.chatLocation.peerId else {
|
guard let strongSelf else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let starsContext = strongSelf.context.starsContext else {
|
guard let starsContext = strongSelf.context.starsContext else {
|
||||||
|
|||||||
@ -1686,7 +1686,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let itemNode = itemNode, let targetView = itemNode.targetReactionView(value: chosenReaction) {
|
if let itemNode = itemNode, let targetView = itemNode.targetReactionView(value: chosenReaction), strongSelf.context.sharedContext.energyUsageSettings.fullTranslucency {
|
||||||
strongSelf.chatDisplayNode.wrappingNode.triggerRipple(at: targetView.convert(targetView.bounds.center, to: strongSelf.chatDisplayNode.view))
|
strongSelf.chatDisplayNode.wrappingNode.triggerRipple(at: targetView.convert(targetView.bounds.center, to: strongSelf.chatDisplayNode.view))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1701,13 +1701,29 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
guard let starsContext = strongSelf.context.starsContext else {
|
guard let starsContext = strongSelf.context.starsContext else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = (starsContext.state
|
guard let peerId = strongSelf.chatLocation.peerId else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let _ = (combineLatest(
|
||||||
|
starsContext.state,
|
||||||
|
strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ReactionSettings(id: peerId))
|
||||||
|
)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { [weak strongSelf] state in
|
|> deliverOnMainQueue).start(next: { [weak strongSelf] state, reactionSettings in
|
||||||
guard let strongSelf, let balance = state?.balance else {
|
guard let strongSelf, let balance = state?.balance else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if case let .known(reactionSettings) = reactionSettings, let starsAllowed = reactionSettings.starsAllowed, !starsAllowed {
|
||||||
|
if let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer {
|
||||||
|
//TODO:localize
|
||||||
|
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: "Star Reactions were disabled in \(peer.debugDisplayTitle).", actions: [
|
||||||
|
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {})
|
||||||
|
]), in: .window(.root))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if balance < 1 {
|
if balance < 1 {
|
||||||
let _ = (strongSelf.context.engine.payments.starsTopUpOptions()
|
let _ = (strongSelf.context.engine.payments.starsTopUpOptions()
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|
|||||||
@ -375,101 +375,121 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
self.context.engine.messages.forceSendPendingSendStarsReaction(id: message.id)
|
self.context.engine.messages.forceSendPendingSendStarsReaction(id: message.id)
|
||||||
|
|
||||||
let reactionsAttribute = mergedMessageReactions(attributes: message.attributes, isTags: false)
|
guard let peerId = self.chatLocation.peerId else {
|
||||||
let _ = (ChatSendStarsScreen.initialData(context: self.context, peerId: message.id.peerId, messageId: message.id, topPeers: reactionsAttribute?.topPeers ?? [])
|
return
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] initialData in
|
}
|
||||||
guard let self, let initialData else {
|
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ReactionSettings(id: peerId))
|
||||||
|
|> deliverOnMainQueue).startStandalone(next: { [weak self] reactionSettings in
|
||||||
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
HapticFeedback().tap()
|
|
||||||
self.push(ChatSendStarsScreen(context: self.context, initialData: initialData, completion: { [weak self] amount, isAnonymous, isBecomingTop, transitionOut in
|
let reactionsAttribute = mergedMessageReactions(attributes: message.attributes, isTags: false)
|
||||||
guard let self, amount > 0 else {
|
let _ = (ChatSendStarsScreen.initialData(context: self.context, peerId: message.id.peerId, messageId: message.id, topPeers: reactionsAttribute?.topPeers ?? [])
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] initialData in
|
||||||
|
guard let self, let initialData else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
HapticFeedback().tap()
|
||||||
var sourceItemNode: ChatMessageItemView?
|
self.push(ChatSendStarsScreen(context: self.context, initialData: initialData, completion: { [weak self] amount, isAnonymous, isBecomingTop, transitionOut in
|
||||||
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
guard let self, amount > 0 else {
|
||||||
if let itemNode = itemNode as? ChatMessageItemView {
|
return
|
||||||
if itemNode.item?.message.id == message.id {
|
|
||||||
sourceItemNode = itemNode
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let itemNode = sourceItemNode, let item = itemNode.item, let availableReactions = item.associatedData.availableReactions, let targetView = itemNode.targetReactionView(value: .stars) {
|
|
||||||
var reactionItem: ReactionItem?
|
|
||||||
|
|
||||||
for reaction in availableReactions.reactions {
|
|
||||||
guard let centerAnimation = reaction.centerAnimation else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
guard let aroundAnimation = reaction.aroundAnimation else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if reaction.value == .stars {
|
|
||||||
reactionItem = ReactionItem(
|
|
||||||
reaction: ReactionItem.Reaction(rawValue: reaction.value),
|
|
||||||
appearAnimation: reaction.appearAnimation,
|
|
||||||
stillAnimation: reaction.selectAnimation,
|
|
||||||
listAnimation: centerAnimation,
|
|
||||||
largeListAnimation: reaction.activateAnimation,
|
|
||||||
applicationAnimation: aroundAnimation,
|
|
||||||
largeApplicationAnimation: reaction.effectAnimation,
|
|
||||||
isCustom: false
|
|
||||||
)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let reactionItem {
|
if case let .known(reactionSettings) = reactionSettings, let starsAllowed = reactionSettings.starsAllowed, !starsAllowed {
|
||||||
let standaloneReactionAnimation = StandaloneReactionAnimation(genericReactionEffect: self.chatDisplayNode.historyNode.takeGenericReactionEffect())
|
if let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer {
|
||||||
|
//TODO:localize
|
||||||
|
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: "Star Reactions were disabled in \(peer.debugDisplayTitle).", actions: [
|
||||||
|
TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_OK, action: {})
|
||||||
|
]), in: .window(.root))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
self.chatDisplayNode.messageTransitionNode.addMessageStandaloneReactionAnimation(messageId: item.message.id, standaloneReactionAnimation: standaloneReactionAnimation)
|
var sourceItemNode: ChatMessageItemView?
|
||||||
|
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||||
self.view.window?.addSubview(standaloneReactionAnimation.view)
|
if let itemNode = itemNode as? ChatMessageItemView {
|
||||||
standaloneReactionAnimation.frame = self.chatDisplayNode.bounds
|
if itemNode.item?.message.id == message.id {
|
||||||
standaloneReactionAnimation.animateOutToReaction(
|
sourceItemNode = itemNode
|
||||||
context: self.context,
|
return
|
||||||
theme: self.presentationData.theme,
|
|
||||||
item: reactionItem,
|
|
||||||
value: .stars,
|
|
||||||
sourceView: transitionOut.sourceView,
|
|
||||||
targetView: targetView,
|
|
||||||
hideNode: false,
|
|
||||||
forceSwitchToInlineImmediately: false,
|
|
||||||
animateTargetContainer: nil,
|
|
||||||
addStandaloneReactionAnimation: { [weak self] standaloneReactionAnimation in
|
|
||||||
guard let self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.chatDisplayNode.messageTransitionNode.addMessageStandaloneReactionAnimation(messageId: item.message.id, standaloneReactionAnimation: standaloneReactionAnimation)
|
|
||||||
standaloneReactionAnimation.frame = self.chatDisplayNode.bounds
|
|
||||||
self.chatDisplayNode.addSubnode(standaloneReactionAnimation)
|
|
||||||
},
|
|
||||||
onHit: { [weak self, weak itemNode] in
|
|
||||||
guard let self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if isBecomingTop {
|
|
||||||
self.chatDisplayNode.animateQuizCorrectOptionSelected()
|
|
||||||
}
|
|
||||||
|
|
||||||
if let itemNode, let targetView = itemNode.targetReactionView(value: .stars) {
|
|
||||||
self.chatDisplayNode.wrappingNode.triggerRipple(at: targetView.convert(targetView.bounds.center, to: self.chatDisplayNode.view))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
completion: { [weak standaloneReactionAnimation] in
|
|
||||||
standaloneReactionAnimation?.view.removeFromSuperview()
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let _ = self.context.engine.messages.sendStarsReaction(id: message.id, count: Int(amount), isAnonymous: isAnonymous)
|
if let itemNode = sourceItemNode, let item = itemNode.item, let availableReactions = item.associatedData.availableReactions, let targetView = itemNode.targetReactionView(value: .stars) {
|
||||||
self.displayOrUpdateSendStarsUndo(messageId: message.id, count: Int(amount))
|
var reactionItem: ReactionItem?
|
||||||
}))
|
|
||||||
|
for reaction in availableReactions.reactions {
|
||||||
|
guard let centerAnimation = reaction.centerAnimation else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
guard let aroundAnimation = reaction.aroundAnimation else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if reaction.value == .stars {
|
||||||
|
reactionItem = ReactionItem(
|
||||||
|
reaction: ReactionItem.Reaction(rawValue: reaction.value),
|
||||||
|
appearAnimation: reaction.appearAnimation,
|
||||||
|
stillAnimation: reaction.selectAnimation,
|
||||||
|
listAnimation: centerAnimation,
|
||||||
|
largeListAnimation: reaction.activateAnimation,
|
||||||
|
applicationAnimation: aroundAnimation,
|
||||||
|
largeApplicationAnimation: reaction.effectAnimation,
|
||||||
|
isCustom: false
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let reactionItem {
|
||||||
|
let standaloneReactionAnimation = StandaloneReactionAnimation(genericReactionEffect: self.chatDisplayNode.historyNode.takeGenericReactionEffect())
|
||||||
|
|
||||||
|
self.chatDisplayNode.messageTransitionNode.addMessageStandaloneReactionAnimation(messageId: item.message.id, standaloneReactionAnimation: standaloneReactionAnimation)
|
||||||
|
|
||||||
|
self.view.window?.addSubview(standaloneReactionAnimation.view)
|
||||||
|
standaloneReactionAnimation.frame = self.chatDisplayNode.bounds
|
||||||
|
standaloneReactionAnimation.animateOutToReaction(
|
||||||
|
context: self.context,
|
||||||
|
theme: self.presentationData.theme,
|
||||||
|
item: reactionItem,
|
||||||
|
value: .stars,
|
||||||
|
sourceView: transitionOut.sourceView,
|
||||||
|
targetView: targetView,
|
||||||
|
hideNode: false,
|
||||||
|
forceSwitchToInlineImmediately: false,
|
||||||
|
animateTargetContainer: nil,
|
||||||
|
addStandaloneReactionAnimation: { [weak self] standaloneReactionAnimation in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.chatDisplayNode.messageTransitionNode.addMessageStandaloneReactionAnimation(messageId: item.message.id, standaloneReactionAnimation: standaloneReactionAnimation)
|
||||||
|
standaloneReactionAnimation.frame = self.chatDisplayNode.bounds
|
||||||
|
self.chatDisplayNode.addSubnode(standaloneReactionAnimation)
|
||||||
|
},
|
||||||
|
onHit: { [weak self, weak itemNode] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if isBecomingTop {
|
||||||
|
self.chatDisplayNode.animateQuizCorrectOptionSelected()
|
||||||
|
}
|
||||||
|
|
||||||
|
if let itemNode, let targetView = itemNode.targetReactionView(value: .stars), self.context.sharedContext.energyUsageSettings.fullTranslucency {
|
||||||
|
self.chatDisplayNode.wrappingNode.triggerRipple(at: targetView.convert(targetView.bounds.center, to: self.chatDisplayNode.view))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
completion: { [weak standaloneReactionAnimation] in
|
||||||
|
standaloneReactionAnimation?.view.removeFromSuperview()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = self.context.engine.messages.sendStarsReaction(id: message.id, count: Int(amount), isAnonymous: isAnonymous)
|
||||||
|
self.displayOrUpdateSendStarsUndo(messageId: message.id, count: Int(amount))
|
||||||
|
}))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user