mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-03 21:16:35 +00:00
[WIP] Privacy
This commit is contained in:
parent
f001ee483e
commit
b299cbb452
@ -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
|
||||
|
||||
|
||||
@ -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)
|
||||
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)
|
||||
transition.updateAlpha(layer: subtitleBadgeView.layer, alpha: (1.0 - transitionFraction) * subtitleBadgeFraction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let buttonsTransitionDistance: CGFloat = -min(0.0, apparentBackgroundHeight - backgroundHeight)
|
||||
|
||||
@ -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))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user