[WIP] Privacy

This commit is contained in:
Isaac 2024-01-11 23:16:58 +04:00
parent f001ee483e
commit b299cbb452
3 changed files with 182 additions and 30 deletions

View File

@ -844,6 +844,40 @@ public extension TelegramEngine.EngineData.Item {
} }
} }
public struct MessageReadStatsAreHidden: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
public typealias Result = Bool?
fileprivate var id: EnginePeer.Id
public var mapKey: EnginePeer.Id {
return self.id
}
public init(id: EnginePeer.Id) {
self.id = id
}
var key: PostboxViewKey {
return .cachedPeerData(peerId: self.id)
}
func extract(view: PostboxView) -> Result {
guard let view = view as? CachedPeerDataView else {
preconditionFailure()
}
if self.id.namespace == Namespaces.Peer.CloudUser {
if let cachedData = view.cachedPeerData as? CachedUserData, cachedData.flags.contains(.readDatesPrivate) {
return true
} else {
return false
}
} else {
return false
}
}
}
public struct CanDeleteHistory: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { public struct CanDeleteHistory: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
public typealias Result = Bool public typealias Result = Bool

View File

@ -1282,6 +1282,13 @@ final class PeerInfoHeaderNode: ASDisplayNode {
transition.updateFrame(node: self.avatarListNode.listContainerNode.bottomShadowNode, frame: bottomShadowFrame, beginWithCurrentState: true) transition.updateFrame(node: self.avatarListNode.listContainerNode.bottomShadowNode, frame: bottomShadowFrame, beginWithCurrentState: true)
self.avatarListNode.listContainerNode.bottomShadowNode.update(size: bottomShadowFrame.size, transition: transition) self.avatarListNode.listContainerNode.bottomShadowNode.update(size: bottomShadowFrame.size, transition: transition)
let singleTitleLockOffset: CGFloat = (peer?.id == self.context.account.peerId || subtitleSize.height.isZero) ? 8.0 : 0.0
let titleLockOffset: CGFloat = 7.0 + singleTitleLockOffset
let titleMaxLockOffset: CGFloat = 7.0
let titleOffset: CGFloat
let titleCollapseFraction: CGFloat
if self.isAvatarExpanded { if self.isAvatarExpanded {
let minTitleSize = CGSize(width: titleSize.width * expandedTitleScale, height: titleSize.height * expandedTitleScale) let minTitleSize = CGSize(width: titleSize.width * expandedTitleScale, height: titleSize.height * expandedTitleScale)
var minTitleFrame = CGRect(origin: CGPoint(x: 16.0, y: expandedAvatarHeight - 58.0 - UIScreenPixel + (subtitleSize.height.isZero ? 10.0 : 0.0)), size: minTitleSize) var minTitleFrame = CGRect(origin: CGPoint(x: 16.0, y: expandedAvatarHeight - 58.0 - UIScreenPixel + (subtitleSize.height.isZero ? 10.0 : 0.0)), size: minTitleSize)
@ -1290,14 +1297,29 @@ final class PeerInfoHeaderNode: ASDisplayNode {
} }
titleFrame = CGRect(origin: CGPoint(x: minTitleFrame.midX - titleSize.width / 2.0, y: minTitleFrame.midY - titleSize.height / 2.0), size: titleSize) titleFrame = CGRect(origin: CGPoint(x: minTitleFrame.midX - titleSize.width / 2.0, y: minTitleFrame.midY - titleSize.height / 2.0), size: titleSize)
var titleCollapseOffset = titleFrame.midY - statusBarHeight - titleLockOffset
if case .regular = metrics.widthClass, !isSettings {
titleCollapseOffset -= 7.0
}
titleOffset = -min(titleCollapseOffset, contentOffset)
titleCollapseFraction = max(0.0, min(1.0, contentOffset / titleCollapseOffset))
subtitleFrame = CGRect(origin: CGPoint(x: 16.0, y: minTitleFrame.maxY + 2.0), size: subtitleSize) subtitleFrame = CGRect(origin: CGPoint(x: 16.0, y: minTitleFrame.maxY + 2.0), size: subtitleSize)
usernameFrame = CGRect(origin: CGPoint(x: width - usernameSize.width - 16.0, y: minTitleFrame.midY - usernameSize.height / 2.0), size: usernameSize) usernameFrame = CGRect(origin: CGPoint(x: width - usernameSize.width - 16.0, y: minTitleFrame.midY - usernameSize.height / 2.0), size: usernameSize)
} else { } else {
titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 9.0 + (subtitleSize.height.isZero ? 11.0 : 0.0)), size: titleSize) titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 9.0 + (subtitleSize.height.isZero ? 11.0 : 0.0)), size: titleSize)
var titleCollapseOffset = titleFrame.midY - statusBarHeight - titleLockOffset
if case .regular = metrics.widthClass, !isSettings {
titleCollapseOffset -= 7.0
}
titleOffset = -min(titleCollapseOffset, contentOffset)
titleCollapseFraction = max(0.0, min(1.0, contentOffset / titleCollapseOffset))
var effectiveSubtitleWidth = subtitleSize.width var effectiveSubtitleWidth = subtitleSize.width
if let subtitleBadgeSize { if let subtitleBadgeSize {
effectiveSubtitleWidth += subtitleBadgeSize.width + 7.0 effectiveSubtitleWidth += (subtitleBadgeSize.width + 7.0) * (1.0 - titleCollapseFraction)
} }
let totalSubtitleWidth = effectiveSubtitleWidth + usernameSpacing + usernameSize.width let totalSubtitleWidth = effectiveSubtitleWidth + usernameSpacing + usernameSize.width
@ -1310,17 +1332,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
} }
} }
let singleTitleLockOffset: CGFloat = (peer?.id == self.context.account.peerId || subtitleSize.height.isZero) ? 8.0 : 0.0
let titleLockOffset: CGFloat = 7.0 + singleTitleLockOffset
let titleMaxLockOffset: CGFloat = 7.0
var titleCollapseOffset = titleFrame.midY - statusBarHeight - titleLockOffset
if case .regular = metrics.widthClass, !isSettings {
titleCollapseOffset -= 7.0
}
let titleOffset = -min(titleCollapseOffset, contentOffset)
let titleCollapseFraction = max(0.0, min(1.0, contentOffset / titleCollapseOffset))
let titleMinScale: CGFloat = 0.6 let titleMinScale: CGFloat = 0.6
let subtitleMinScale: CGFloat = 0.8 let subtitleMinScale: CGFloat = 0.8
let avatarMinScale: CGFloat = 0.55 let avatarMinScale: CGFloat = 0.55
@ -1686,17 +1697,26 @@ final class PeerInfoHeaderNode: ASDisplayNode {
transition.updateSublayerTransformScale(node: self.titleNodeContainer, scale: titleScale) transition.updateSublayerTransformScale(node: self.titleNodeContainer, scale: titleScale)
transition.updateSublayerTransformScale(node: self.subtitleNodeContainer, scale: subtitleScale) transition.updateSublayerTransformScale(node: self.subtitleNodeContainer, scale: subtitleScale)
transition.updateSublayerTransformScale(node: self.usernameNodeContainer, scale: subtitleScale) transition.updateSublayerTransformScale(node: self.usernameNodeContainer, scale: subtitleScale)
if let subtitleBadgeView = self.subtitleBadgeView, let subtitleBadgeSize {
let subtitleBadgeFrame = CGRect(origin: CGPoint(x: (subtitleSize.width + 8.0) * 0.5, y: floor((-subtitleBadgeSize.height) * 0.5)), size: subtitleBadgeSize)
transition.updateFrameAdditive(view: subtitleBadgeView, frame: subtitleBadgeFrame)
transition.updateAlpha(layer: subtitleBadgeView.layer, alpha: (1.0 - transitionFraction))
}
} else { } else {
let titleScale: CGFloat let titleScale: CGFloat
let subtitleScale: CGFloat let subtitleScale: CGFloat
var subtitleOffset: CGFloat = 0.0 var subtitleOffset: CGFloat = 0.0
let subtitleBadgeFraction: CGFloat
if self.isAvatarExpanded { if self.isAvatarExpanded {
titleScale = expandedTitleScale titleScale = expandedTitleScale
subtitleScale = 1.0 subtitleScale = 1.0
subtitleBadgeFraction = 1.0
} else { } else {
titleScale = (1.0 - titleCollapseFraction) * 1.0 + titleCollapseFraction * titleMinScale titleScale = (1.0 - titleCollapseFraction) * 1.0 + titleCollapseFraction * titleMinScale
subtitleScale = (1.0 - titleCollapseFraction) * 1.0 + titleCollapseFraction * subtitleMinScale subtitleScale = (1.0 - titleCollapseFraction) * 1.0 + titleCollapseFraction * subtitleMinScale
subtitleOffset = titleCollapseFraction * -1.0 subtitleOffset = titleCollapseFraction * -1.0
subtitleBadgeFraction = (1.0 - titleCollapseFraction)
} }
let rawTitleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0) let rawTitleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0)
@ -1728,13 +1748,13 @@ final class PeerInfoHeaderNode: ASDisplayNode {
transition.updateSublayerTransformScaleAdditive(node: self.titleNodeContainer, scale: titleScale) transition.updateSublayerTransformScaleAdditive(node: self.titleNodeContainer, scale: titleScale)
transition.updateSublayerTransformScaleAdditive(node: self.subtitleNodeContainer, scale: subtitleScale) transition.updateSublayerTransformScaleAdditive(node: self.subtitleNodeContainer, scale: subtitleScale)
transition.updateSublayerTransformScaleAdditive(node: self.usernameNodeContainer, scale: subtitleScale) transition.updateSublayerTransformScaleAdditive(node: self.usernameNodeContainer, scale: subtitleScale)
}
}
if let subtitleBadgeView = self.subtitleBadgeView, let subtitleBadgeSize { if let subtitleBadgeView = self.subtitleBadgeView, let subtitleBadgeSize {
let subtitleBadgeFrame = CGRect(origin: CGPoint(x: (subtitleSize.width + 7.0) * 0.5, y: floor((-subtitleBadgeSize.height) * 0.5)), size: subtitleBadgeSize) let subtitleBadgeFrame = CGRect(origin: CGPoint(x: (subtitleSize.width + 8.0) * 0.5, y: floor((-subtitleBadgeSize.height) * 0.5)), size: subtitleBadgeSize)
transition.updateFrameAdditive(view: subtitleBadgeView, frame: subtitleBadgeFrame) transition.updateFrameAdditive(view: subtitleBadgeView, frame: subtitleBadgeFrame)
transition.updateAlpha(layer: subtitleBadgeView.layer, alpha: 1.0 - transitionFraction) transition.updateAlpha(layer: subtitleBadgeView.layer, alpha: (1.0 - transitionFraction) * subtitleBadgeFraction)
}
}
} }
let buttonsTransitionDistance: CGFloat = -min(0.0, apparentBackgroundHeight - backgroundHeight) let buttonsTransitionDistance: CGFloat = -min(0.0, apparentBackgroundHeight - backgroundHeight)

View File

@ -244,8 +244,13 @@ private func canViewReadStats(message: Message, participantCount: Int?, isMessag
if group.participantCount > maxParticipantCount { if group.participantCount > maxParticipantCount {
return false return false
} }
case _ as TelegramUser: case let user as TelegramUser:
break if user.botInfo != nil {
return false
}
if user.flags.contains(.isSupport) {
return false
}
default: default:
return false return false
} }
@ -719,18 +724,28 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
var linkedDiscusionPeerId: EnginePeerCachedInfoItem<EnginePeer.Id?> var linkedDiscusionPeerId: EnginePeerCachedInfoItem<EnginePeer.Id?>
var canViewStats: Bool var canViewStats: Bool
var participantCount: Int? var participantCount: Int?
var messageReadStatsAreHidden: Bool?
init(linkedDiscusionPeerId: EnginePeerCachedInfoItem<EnginePeer.Id?>, canViewStats: Bool, participantCount: Int?, messageReadStatsAreHidden: Bool?) {
self.linkedDiscusionPeerId = linkedDiscusionPeerId
self.canViewStats = canViewStats
self.participantCount = participantCount
self.messageReadStatsAreHidden = messageReadStatsAreHidden
}
} }
let infoSummaryData = context.engine.data.get( let infoSummaryData = context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.LinkedDiscussionPeerId(id: messages[0].id.peerId), TelegramEngine.EngineData.Item.Peer.LinkedDiscussionPeerId(id: messages[0].id.peerId),
TelegramEngine.EngineData.Item.Peer.CanViewStats(id: messages[0].id.peerId), TelegramEngine.EngineData.Item.Peer.CanViewStats(id: messages[0].id.peerId),
TelegramEngine.EngineData.Item.Peer.ParticipantCount(id: messages[0].id.peerId) TelegramEngine.EngineData.Item.Peer.ParticipantCount(id: messages[0].id.peerId),
TelegramEngine.EngineData.Item.Peer.MessageReadStatsAreHidden(id: messages[0].id.peerId)
) )
|> map { linkedDiscusionPeerId, canViewStats, participantCount -> InfoSummaryData in |> map { linkedDiscusionPeerId, canViewStats, participantCount, messageReadStatsAreHidden -> InfoSummaryData in
return InfoSummaryData( return InfoSummaryData(
linkedDiscusionPeerId: linkedDiscusionPeerId, linkedDiscusionPeerId: linkedDiscusionPeerId,
canViewStats: canViewStats, canViewStats: canViewStats,
participantCount: participantCount participantCount: participantCount,
messageReadStatsAreHidden: messageReadStatsAreHidden
) )
} }
@ -1696,7 +1711,13 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
} }
} }
let canViewStats = canViewReadStats(message: message, participantCount: infoSummaryData.participantCount, isMessageRead: isMessageRead, appConfig: appConfig) let canViewStats: Bool
if let messageReadStatsAreHidden = infoSummaryData.messageReadStatsAreHidden, !messageReadStatsAreHidden {
canViewStats = canViewReadStats(message: message, participantCount: infoSummaryData.participantCount, isMessageRead: isMessageRead, appConfig: appConfig)
} else {
canViewStats = false
}
var reactionCount = 0 var reactionCount = 0
for reaction in mergedMessageReactionsAndPeers(accountPeerId: context.account.peerId, accountPeer: nil, message: message).reactions { for reaction in mergedMessageReactionsAndPeers(accountPeerId: context.account.peerId, accountPeer: nil, message: message).reactions {
reactionCount += Int(reaction.count) reactionCount += Int(reaction.count)
@ -2392,6 +2413,8 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus
private let highlightedBackgroundNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode
private let placeholderCalculationTextNode: ImmediateTextNode private let placeholderCalculationTextNode: ImmediateTextNode
private let textNode: ImmediateTextNode private let textNode: ImmediateTextNode
private var badgeBackground: UIImageView?
private var badgeText: ImmediateTextNode?
private let shimmerNode: ShimmerEffectNode private let shimmerNode: ShimmerEffectNode
private let iconNode: ASImageNode private let iconNode: ASImageNode
@ -2449,7 +2472,9 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus
self.buttonNode.accessibilityLabel = presentationData.strings.VoiceChat_StopRecording self.buttonNode.accessibilityLabel = presentationData.strings.VoiceChat_StopRecording
self.iconNode = ASImageNode() self.iconNode = ASImageNode()
if let reactionsAttribute = item.message.reactionsAttribute, !reactionsAttribute.reactions.isEmpty { if self.item.message.id.peerId.namespace == Namespaces.Peer.CloudUser {
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/MenuReadIcon"), color: presentationData.theme.actionSheet.primaryTextColor)
} else if let reactionsAttribute = item.message.reactionsAttribute, !reactionsAttribute.reactions.isEmpty {
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reactions"), color: presentationData.theme.actionSheet.primaryTextColor) self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reactions"), color: presentationData.theme.actionSheet.primaryTextColor)
} else { } else {
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Read"), color: presentationData.theme.actionSheet.primaryTextColor) self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Read"), color: presentationData.theme.actionSheet.primaryTextColor)
@ -2607,12 +2632,19 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus
func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) { func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) {
let sideInset: CGFloat = 14.0 let sideInset: CGFloat = 14.0
let verticalInset: CGFloat = 12.0 let verticalInset: CGFloat
let rightTextInset: CGFloat
if self.item.message.id.peerId.namespace == Namespaces.Peer.CloudUser {
verticalInset = 7.0
rightTextInset = 8.0
} else {
verticalInset = 12.0
rightTextInset = sideInset + 36.0
}
let iconSize: CGSize = self.iconNode.image?.size ?? CGSize(width: 10.0, height: 10.0) let iconSize: CGSize = self.iconNode.image?.size ?? CGSize(width: 10.0, height: 10.0)
let rightTextInset: CGFloat = sideInset + 36.0
let calculatedWidth = min(constrainedWidth, 250.0) let calculatedWidth = min(constrainedWidth, 250.0)
let textFont = Font.regular(self.presentationData.listsFontSize.baseDisplaySize) let textFont = Font.regular(self.presentationData.listsFontSize.baseDisplaySize)
@ -2622,13 +2654,21 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus
reactionCount += Int(reaction.count) reactionCount += Int(reaction.count)
} }
var showReadBadge = false
var animatePositions = true
if let currentStats = self.currentStats { if let currentStats = self.currentStats {
reactionCount = currentStats.reactionCount reactionCount = currentStats.reactionCount
if currentStats.peers.isEmpty { if currentStats.peers.isEmpty {
if self.item.message.id.peerId.namespace == Namespaces.Peer.CloudUser { if self.item.message.id.peerId.namespace == Namespaces.Peer.CloudUser {
//TODO:localize //TODO:localize
self.textNode.attributedText = NSAttributedString(string: "Show Read Time", font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor) let text = NSAttributedString(string: "read", font: Font.regular(floor(self.presentationData.listsFontSize.baseDisplaySize * 0.8)), textColor: self.presentationData.theme.contextMenu.primaryColor)
if self.textNode.attributedText != text {
animatePositions = false
}
self.textNode.attributedText = text
showReadBadge = true
} else { } else {
if reactionCount != 0 { if reactionCount != 0 {
let text: String = self.presentationData.strings.Chat_ContextReactionCount(Int32(reactionCount)) let text: String = self.presentationData.strings.Chat_ContextReactionCount(Int32(reactionCount))
@ -2698,14 +2738,72 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus
let placeholderTextSize = self.placeholderCalculationTextNode.updateLayout(CGSize(width: calculatedWidth - sideInset - rightTextInset - iconSize.width - 4.0, height: .greatestFiniteMagnitude)) let placeholderTextSize = self.placeholderCalculationTextNode.updateLayout(CGSize(width: calculatedWidth - sideInset - rightTextInset - iconSize.width - 4.0, height: .greatestFiniteMagnitude))
var badgeTextSize: CGSize?
if showReadBadge {
let badgeBackground: UIImageView
if let current = self.badgeBackground {
badgeBackground = current
} else {
badgeBackground = UIImageView()
badgeBackground.alpha = 0.0
self.badgeBackground = badgeBackground
self.view.addSubview(badgeBackground)
}
let badgeText: ImmediateTextNode
if let current = self.badgeText {
badgeText = current
} else {
badgeText = ImmediateTextNode()
badgeText.alpha = 0.0
self.badgeText = badgeText
self.addSubnode(badgeText)
}
//TODO:localize
badgeText.attributedText = NSAttributedString(string: "show when", font: Font.regular(self.presentationData.listsFontSize.baseDisplaySize * 11.0 / 17.0), textColor: self.presentationData.theme.contextMenu.primaryColor)
badgeTextSize = badgeText.updateLayout(CGSize(width: calculatedWidth - sideInset - rightTextInset - iconSize.width - 4.0 - textSize.width - 12.0, height: 100.0))
} else {
if let badgeBackground = self.badgeBackground {
badgeBackground.removeFromSuperview()
self.badgeBackground = nil
}
if let badgeText = self.badgeText {
badgeText.removeFromSupernode()
self.badgeText = nil
}
}
let combinedTextHeight = textSize.height let combinedTextHeight = textSize.height
return (CGSize(width: calculatedWidth, height: verticalInset * 2.0 + combinedTextHeight), { size, transition in return (CGSize(width: calculatedWidth, height: verticalInset * 2.0 + combinedTextHeight), { size, transition in
self.validLayout = (calculatedWidth: calculatedWidth, size: size) self.validLayout = (calculatedWidth: calculatedWidth, size: size)
let positionTransition: ContainedViewLayoutTransition = animatePositions ? transition : .immediate
let verticalOrigin = floor((size.height - combinedTextHeight) / 2.0) let verticalOrigin = floor((size.height - combinedTextHeight) / 2.0)
let textFrame = CGRect(origin: CGPoint(x: sideInset + iconSize.width + 4.0, y: verticalOrigin), size: textSize) let textFrame = CGRect(origin: CGPoint(x: sideInset + iconSize.width + 4.0, y: verticalOrigin), size: textSize)
transition.updateFrameAdditive(node: self.textNode, frame: textFrame) positionTransition.updateFrameAdditive(node: self.textNode, frame: textFrame)
transition.updateAlpha(node: self.textNode, alpha: self.currentStats == nil ? 0.0 : 1.0) transition.updateAlpha(node: self.textNode, alpha: self.currentStats == nil ? 0.0 : 1.0)
if let badgeTextSize, let badgeText = self.badgeText, let badgeBackground = self.badgeBackground {
let backgroundSideInset: CGFloat = 5.0
let backgroundVerticalInset: CGFloat = 3.0
let badgeTextFrame = CGRect(origin: CGPoint(x: textFrame.maxX + 5.0 + backgroundSideInset, y: textFrame.minY + floor((textFrame.height - badgeTextSize.height) * 0.5)), size: badgeTextSize)
positionTransition.updateFrameAdditive(node: badgeText, frame: badgeTextFrame)
transition.updateAlpha(node: badgeText, alpha: self.currentStats == nil ? 0.0 : 1.0)
let badgeBackgroundFrame = badgeTextFrame.insetBy(dx: -backgroundSideInset, dy: -backgroundVerticalInset).offsetBy(dx: 0.0, dy: 1.0)
if badgeBackground.image?.size.height != ceil(badgeBackgroundFrame.height) {
badgeBackground.image = generateStretchableFilledCircleImage(diameter: ceil(badgeBackgroundFrame.height), color: .white, strokeColor: nil, strokeWidth: nil, backgroundColor: nil)?.withRenderingMode(.alwaysTemplate)
}
badgeBackground.tintColor = self.presentationData.theme.contextMenu.primaryColor.withMultipliedAlpha(0.05)
positionTransition.updateFrame(view: badgeBackground, frame: badgeBackgroundFrame)
transition.updateAlpha(layer: badgeBackground.layer, alpha: self.currentStats == nil ? 0.0 : 1.0)
}
let shimmerHeight: CGFloat = 8.0 let shimmerHeight: CGFloat = 8.0
self.shimmerNode.frame = CGRect(origin: CGPoint(x: textFrame.minX, y: floor((size.height - shimmerHeight) / 2.0)), size: CGSize(width: placeholderTextSize.width, height: shimmerHeight)) self.shimmerNode.frame = CGRect(origin: CGPoint(x: textFrame.minX, y: floor((size.height - shimmerHeight) / 2.0)), size: CGSize(width: placeholderTextSize.width, height: shimmerHeight))