Various improvements

This commit is contained in:
Isaac 2024-08-16 21:41:36 +08:00
parent d40cf1023e
commit 4ceb3ac58b
13 changed files with 351 additions and 201 deletions

View File

@ -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

View File

@ -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)
} }
} }

View File

@ -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)

View File

@ -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))

View File

@ -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))
} }

View File

@ -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([])
} }

View File

@ -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,

View File

@ -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)

View File

@ -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)
} }
} }
)), )),

View File

@ -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

View File

@ -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 {

View File

@ -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)

View File

@ -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))
}))
})
}) })
} }