[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 typealias Result = Bool

View File

@ -1282,6 +1282,13 @@ final class PeerInfoHeaderNode: ASDisplayNode {
transition.updateFrame(node: self.avatarListNode.listContainerNode.bottomShadowNode, frame: bottomShadowFrame, beginWithCurrentState: true)
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 {
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)
@ -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)
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)
usernameFrame = CGRect(origin: CGPoint(x: width - usernameSize.width - 16.0, y: minTitleFrame.midY - usernameSize.height / 2.0), size: usernameSize)
} 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)
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
if let subtitleBadgeSize {
effectiveSubtitleWidth += subtitleBadgeSize.width + 7.0
effectiveSubtitleWidth += (subtitleBadgeSize.width + 7.0) * (1.0 - titleCollapseFraction)
}
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 subtitleMinScale: CGFloat = 0.8
let avatarMinScale: CGFloat = 0.55
@ -1686,17 +1697,26 @@ final class PeerInfoHeaderNode: ASDisplayNode {
transition.updateSublayerTransformScale(node: self.titleNodeContainer, scale: titleScale)
transition.updateSublayerTransformScale(node: self.subtitleNodeContainer, 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 {
let titleScale: CGFloat
let subtitleScale: CGFloat
var subtitleOffset: CGFloat = 0.0
let subtitleBadgeFraction: CGFloat
if self.isAvatarExpanded {
titleScale = expandedTitleScale
subtitleScale = 1.0
subtitleBadgeFraction = 1.0
} else {
titleScale = (1.0 - titleCollapseFraction) * 1.0 + titleCollapseFraction * titleMinScale
subtitleScale = (1.0 - titleCollapseFraction) * 1.0 + titleCollapseFraction * subtitleMinScale
subtitleOffset = titleCollapseFraction * -1.0
subtitleBadgeFraction = (1.0 - titleCollapseFraction)
}
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.subtitleNodeContainer, scale: subtitleScale)
transition.updateSublayerTransformScaleAdditive(node: self.usernameNodeContainer, scale: subtitleScale)
}
}
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)
transition.updateFrameAdditive(view: subtitleBadgeView, frame: subtitleBadgeFrame)
transition.updateAlpha(layer: subtitleBadgeView.layer, alpha: 1.0 - transitionFraction)
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) * subtitleBadgeFraction)
}
}
}
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 {
return false
}
case _ as TelegramUser:
break
case let user as TelegramUser:
if user.botInfo != nil {
return false
}
if user.flags.contains(.isSupport) {
return false
}
default:
return false
}
@ -719,18 +724,28 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
var linkedDiscusionPeerId: EnginePeerCachedInfoItem<EnginePeer.Id?>
var canViewStats: Bool
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(
TelegramEngine.EngineData.Item.Peer.LinkedDiscussionPeerId(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(
linkedDiscusionPeerId: linkedDiscusionPeerId,
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
for reaction in mergedMessageReactionsAndPeers(accountPeerId: context.account.peerId, accountPeer: nil, message: message).reactions {
reactionCount += Int(reaction.count)
@ -2392,6 +2413,8 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus
private let highlightedBackgroundNode: ASDisplayNode
private let placeholderCalculationTextNode: ImmediateTextNode
private let textNode: ImmediateTextNode
private var badgeBackground: UIImageView?
private var badgeText: ImmediateTextNode?
private let shimmerNode: ShimmerEffectNode
private let iconNode: ASImageNode
@ -2449,7 +2472,9 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus
self.buttonNode.accessibilityLabel = presentationData.strings.VoiceChat_StopRecording
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)
} else {
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) {
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 rightTextInset: CGFloat = sideInset + 36.0
let calculatedWidth = min(constrainedWidth, 250.0)
let textFont = Font.regular(self.presentationData.listsFontSize.baseDisplaySize)
@ -2622,13 +2654,21 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus
reactionCount += Int(reaction.count)
}
var showReadBadge = false
var animatePositions = true
if let currentStats = self.currentStats {
reactionCount = currentStats.reactionCount
if currentStats.peers.isEmpty {
if self.item.message.id.peerId.namespace == Namespaces.Peer.CloudUser {
//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 {
if reactionCount != 0 {
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))
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
return (CGSize(width: calculatedWidth, height: verticalInset * 2.0 + combinedTextHeight), { size, transition in
self.validLayout = (calculatedWidth: calculatedWidth, size: size)
let positionTransition: ContainedViewLayoutTransition = animatePositions ? transition : .immediate
let verticalOrigin = floor((size.height - combinedTextHeight) / 2.0)
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)
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
self.shimmerNode.frame = CGRect(origin: CGPoint(x: textFrame.minX, y: floor((size.height - shimmerHeight) / 2.0)), size: CGSize(width: placeholderTextSize.width, height: shimmerHeight))