Autoremove improvements

This commit is contained in:
Ali 2022-11-27 03:06:24 +04:00
parent 1656ecff49
commit fea0e8b23e
15 changed files with 616 additions and 169 deletions

View File

@ -6249,6 +6249,10 @@ Sorry for the inconvenience.";
"Conversation.AutoremoveTimerSetUserYou" = "You set messages to automatically delete after %1$@"; "Conversation.AutoremoveTimerSetUserYou" = "You set messages to automatically delete after %1$@";
"Conversation.AutoremoveTimerSetUser" = "%1$@ set messages to automatically delete after %2$@"; "Conversation.AutoremoveTimerSetUser" = "%1$@ set messages to automatically delete after %2$@";
"Conversation.AutoremoveTimerSetUserGlobalYou" = "You set a self-destruct timer for all chats.\nAll new messages in this chat will be automatically deleted after %1$@ they're sent.";
"Conversation.AutoremoveTimerSetUserGlobal" = "%1$@ set a self-destruct timer for all chats.\nAll new messages in this chat will be automatically deleted after %2$@ they're sent.";
"Conversation.AutoremoveTimerRemovedUserYou" = "You disabled the auto-delete timer"; "Conversation.AutoremoveTimerRemovedUserYou" = "You disabled the auto-delete timer";
"Conversation.AutoremoveTimerRemovedUser" = "%1$@ disabled the auto-delete timer"; "Conversation.AutoremoveTimerRemovedUser" = "%1$@ disabled the auto-delete timer";
"Conversation.AutoremoveTimerSetGroup" = "A group admin set messages to automatically delete after %1$@"; "Conversation.AutoremoveTimerSetGroup" = "A group admin set messages to automatically delete after %1$@";

View File

@ -9,6 +9,7 @@ import AnimationUI
import AppBundle import AppBundle
import AccountContext import AccountContext
import Emoji import Emoji
import Accelerate
private let deletedIcon = UIImage(bundleImageName: "Avatar/DeletedIcon")?.precomposed() private let deletedIcon = UIImage(bundleImageName: "Avatar/DeletedIcon")?.precomposed()
private let phoneIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/PhoneIcon"), color: .white) private let phoneIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/PhoneIcon"), color: .white)
@ -30,6 +31,7 @@ private class AvatarNodeParameters: NSObject {
let theme: PresentationTheme? let theme: PresentationTheme?
let accountPeerId: EnginePeer.Id? let accountPeerId: EnginePeer.Id?
let peerId: EnginePeer.Id? let peerId: EnginePeer.Id?
let colors: [UIColor]
let letters: [String] let letters: [String]
let font: UIFont let font: UIFont
let icon: AvatarNodeIcon let icon: AvatarNodeIcon
@ -37,10 +39,11 @@ private class AvatarNodeParameters: NSObject {
let hasImage: Bool let hasImage: Bool
let clipStyle: AvatarNodeClipStyle let clipStyle: AvatarNodeClipStyle
init(theme: PresentationTheme?, accountPeerId: EnginePeer.Id?, peerId: EnginePeer.Id?, letters: [String], font: UIFont, icon: AvatarNodeIcon, explicitColorIndex: Int?, hasImage: Bool, clipStyle: AvatarNodeClipStyle) { init(theme: PresentationTheme?, accountPeerId: EnginePeer.Id?, peerId: EnginePeer.Id?, colors: [UIColor], letters: [String], font: UIFont, icon: AvatarNodeIcon, explicitColorIndex: Int?, hasImage: Bool, clipStyle: AvatarNodeClipStyle) {
self.theme = theme self.theme = theme
self.accountPeerId = accountPeerId self.accountPeerId = accountPeerId
self.peerId = peerId self.peerId = peerId
self.colors = colors
self.letters = letters self.letters = letters
self.font = font self.font = font
self.icon = icon self.icon = icon
@ -52,18 +55,71 @@ private class AvatarNodeParameters: NSObject {
} }
func withUpdatedHasImage(_ hasImage: Bool) -> AvatarNodeParameters { func withUpdatedHasImage(_ hasImage: Bool) -> AvatarNodeParameters {
return AvatarNodeParameters(theme: self.theme, accountPeerId: self.accountPeerId, peerId: self.peerId, letters: self.letters, font: self.font, icon: self.icon, explicitColorIndex: self.explicitColorIndex, hasImage: hasImage, clipStyle: self.clipStyle) return AvatarNodeParameters(theme: self.theme, accountPeerId: self.accountPeerId, peerId: self.peerId, colors: self.colors, letters: self.letters, font: self.font, icon: self.icon, explicitColorIndex: self.explicitColorIndex, hasImage: hasImage, clipStyle: self.clipStyle)
} }
} }
private let grayscaleColors: NSArray = [ private let grayscaleColors: [UIColor] = [
UIColor(rgb: 0xb1b1b1).cgColor, UIColor(rgb: 0xcdcdcd).cgColor UIColor(rgb: 0xb1b1b1), UIColor(rgb: 0xcdcdcd)
] ]
private let savedMessagesColors: NSArray = [ private let savedMessagesColors: [UIColor] = [
UIColor(rgb: 0x2a9ef1).cgColor, UIColor(rgb: 0x72d5fd).cgColor UIColor(rgb: 0x2a9ef1), UIColor(rgb: 0x72d5fd)
] ]
private func calculateColors(explicitColorIndex: Int?, peerId: EnginePeer.Id?, icon: AvatarNodeIcon, theme: PresentationTheme?) -> [UIColor] {
let colorIndex: Int
if let explicitColorIndex = explicitColorIndex {
colorIndex = explicitColorIndex
} else {
if let peerId {
if peerId.namespace == .max {
colorIndex = -1
} else {
colorIndex = abs(Int(clamping: peerId.id._internalGetInt64Value()))
}
} else {
colorIndex = -1
}
}
let colors: [UIColor]
if icon != .none {
if case .deletedIcon = icon {
colors = grayscaleColors
} else if case .phoneIcon = icon {
colors = grayscaleColors
} else if case .savedMessagesIcon = icon {
colors = savedMessagesColors
} else if case .repliesIcon = icon {
colors = savedMessagesColors
} else if case .editAvatarIcon = icon, let theme {
colors = [theme.list.itemAccentColor.withAlphaComponent(0.1), theme.list.itemAccentColor.withAlphaComponent(0.1)]
} else if case let .archivedChatsIcon(hiddenByDefault) = icon, let theme = theme {
let backgroundColors: (UIColor, UIColor)
if hiddenByDefault {
backgroundColors = theme.chatList.unpinnedArchiveAvatarColor.backgroundColors.colors
} else {
backgroundColors = theme.chatList.pinnedArchiveAvatarColor.backgroundColors.colors
}
colors = [backgroundColors.1, backgroundColors.0]
} else {
colors = grayscaleColors
}
} else if colorIndex == -1 {
if let theme {
let backgroundColors = theme.chatList.unpinnedArchiveAvatarColor.backgroundColors.colors
colors = [backgroundColors.1, backgroundColors.0]
} else {
colors = grayscaleColors
}
} else {
colors = AvatarNode.gradientColors[colorIndex % AvatarNode.gradientColors.count]
}
return colors
}
public enum AvatarNodeExplicitIcon { public enum AvatarNodeExplicitIcon {
case phone case phone
} }
@ -158,21 +214,21 @@ public final class AvatarEditOverlayNode: ASDisplayNode {
} }
public final class AvatarNode: ASDisplayNode { public final class AvatarNode: ASDisplayNode {
public static let gradientColors: [NSArray] = [ public static let gradientColors: [[UIColor]] = [
[UIColor(rgb: 0xff516a).cgColor, UIColor(rgb: 0xff885e).cgColor], [UIColor(rgb: 0xff516a), UIColor(rgb: 0xff885e)],
[UIColor(rgb: 0xffa85c).cgColor, UIColor(rgb: 0xffcd6a).cgColor], [UIColor(rgb: 0xffa85c), UIColor(rgb: 0xffcd6a)],
[UIColor(rgb: 0x665fff).cgColor, UIColor(rgb: 0x82b1ff).cgColor], [UIColor(rgb: 0x665fff), UIColor(rgb: 0x82b1ff)],
[UIColor(rgb: 0x54cb68).cgColor, UIColor(rgb: 0xa0de7e).cgColor], [UIColor(rgb: 0x54cb68), UIColor(rgb: 0xa0de7e)],
[UIColor(rgb: 0x4acccd).cgColor, UIColor(rgb: 0x00fcfd).cgColor], [UIColor(rgb: 0x4acccd), UIColor(rgb: 0x00fcfd)],
[UIColor(rgb: 0x2a9ef1).cgColor, UIColor(rgb: 0x72d5fd).cgColor], [UIColor(rgb: 0x2a9ef1), UIColor(rgb: 0x72d5fd)],
[UIColor(rgb: 0xd669ed).cgColor, UIColor(rgb: 0xe0a2f3).cgColor], [UIColor(rgb: 0xd669ed), UIColor(rgb: 0xe0a2f3)],
] ]
public var font: UIFont { public var font: UIFont {
didSet { didSet {
if oldValue.pointSize != font.pointSize { if oldValue.pointSize != font.pointSize {
if let parameters = self.parameters { if let parameters = self.parameters {
self.parameters = AvatarNodeParameters(theme: parameters.theme, accountPeerId: parameters.accountPeerId, peerId: parameters.peerId, letters: parameters.letters, font: self.font, icon: parameters.icon, explicitColorIndex: parameters.explicitColorIndex, hasImage: parameters.hasImage, clipStyle: parameters.clipStyle) self.parameters = AvatarNodeParameters(theme: parameters.theme, accountPeerId: parameters.accountPeerId, peerId: parameters.peerId, colors: parameters.colors, letters: parameters.letters, font: self.font, icon: parameters.icon, explicitColorIndex: parameters.explicitColorIndex, hasImage: parameters.hasImage, clipStyle: parameters.clipStyle)
} }
if !self.displaySuspended { if !self.displaySuspended {
@ -193,6 +249,29 @@ public final class AvatarNode: ASDisplayNode {
private var state: AvatarNodeState = .empty private var state: AvatarNodeState = .empty
public var unroundedImage: UIImage? public var unroundedImage: UIImage?
private var currentImage: UIImage?
public var badgeView: AvatarBadgeView? {
didSet {
if self.badgeView !== oldValue {
if let badgeView = self.badgeView, let parameters = self.parameters {
if parameters.hasImage {
if let currentImage = self.currentImage {
badgeView.update(content: .image(currentImage))
}
} else {
let badgeColor: UIColor
if parameters.colors.isEmpty {
badgeColor = .white
} else {
badgeColor = parameters.colors[parameters.colors.count - 1]
}
badgeView.update(content: .color(badgeColor))
}
}
}
}
}
private let imageReady = Promise<Bool>(false) private let imageReady = Promise<Bool>(false)
public var ready: Signal<Void, NoError> { public var ready: Signal<Void, NoError> {
@ -217,6 +296,22 @@ public final class AvatarNode: ASDisplayNode {
self.imageNode.isLayerBacked = true self.imageNode.isLayerBacked = true
self.addSubnode(self.imageNode) self.addSubnode(self.imageNode)
self.imageNode.contentUpdated = { [weak self] image in
guard let self else {
return
}
self.currentImage = image
guard let badgeView = self.badgeView, let parameters = self.parameters else {
return
}
if parameters.hasImage, let image {
badgeView.update(content: .image(image))
}
}
} }
override public func didLoad() { override public func didLoad() {
@ -365,7 +460,7 @@ public final class AvatarNode: ASDisplayNode {
self.editOverlayNode?.isHidden = true self.editOverlayNode?.isHidden = true
} }
parameters = AvatarNodeParameters(theme: theme, accountPeerId: account.peerId, peerId: peer.id, letters: peer.displayLetters, font: self.font, icon: icon, explicitColorIndex: nil, hasImage: true, clipStyle: clipStyle) parameters = AvatarNodeParameters(theme: theme, accountPeerId: account.peerId, peerId: peer.id, colors: calculateColors(explicitColorIndex: nil, peerId: peer.id, icon: icon, theme: theme), letters: peer.displayLetters, font: self.font, icon: icon, explicitColorIndex: nil, hasImage: true, clipStyle: clipStyle)
} else { } else {
self.imageReady.set(.single(true)) self.imageReady.set(.single(true))
self.displaySuspended = false self.displaySuspended = false
@ -374,7 +469,18 @@ public final class AvatarNode: ASDisplayNode {
} }
self.editOverlayNode?.isHidden = true self.editOverlayNode?.isHidden = true
parameters = AvatarNodeParameters(theme: theme, accountPeerId: account.peerId, peerId: peer?.id ?? EnginePeer.Id(0), letters: peer?.displayLetters ?? [], font: self.font, icon: icon, explicitColorIndex: nil, hasImage: false, clipStyle: clipStyle) let colors = calculateColors(explicitColorIndex: nil, peerId: peer?.id ?? EnginePeer.Id(0), icon: icon, theme: theme)
parameters = AvatarNodeParameters(theme: theme, accountPeerId: account.peerId, peerId: peer?.id ?? EnginePeer.Id(0), colors: colors, letters: peer?.displayLetters ?? [], font: self.font, icon: icon, explicitColorIndex: nil, hasImage: false, clipStyle: clipStyle)
if let badgeView = self.badgeView {
let badgeColor: UIColor
if colors.isEmpty {
badgeColor = .white
} else {
badgeColor = colors[colors.count - 1]
}
badgeView.update(content: .color(badgeColor))
}
} }
if self.parameters == nil || self.parameters != parameters { if self.parameters == nil || self.parameters != parameters {
self.parameters = parameters self.parameters = parameters
@ -400,9 +506,9 @@ public final class AvatarNode: ASDisplayNode {
let parameters: AvatarNodeParameters let parameters: AvatarNodeParameters
if let icon = icon, case .phone = icon { if let icon = icon, case .phone = icon {
parameters = AvatarNodeParameters(theme: nil, accountPeerId: nil, peerId: nil, letters: [], font: self.font, icon: .phoneIcon, explicitColorIndex: explicitIndex, hasImage: false, clipStyle: .round) parameters = AvatarNodeParameters(theme: nil, accountPeerId: nil, peerId: nil, colors: calculateColors(explicitColorIndex: explicitIndex, peerId: nil, icon: .phoneIcon, theme: nil), letters: [], font: self.font, icon: .phoneIcon, explicitColorIndex: explicitIndex, hasImage: false, clipStyle: .round)
} else { } else {
parameters = AvatarNodeParameters(theme: nil, accountPeerId: nil, peerId: nil, letters: letters, font: self.font, icon: .none, explicitColorIndex: explicitIndex, hasImage: false, clipStyle: .round) parameters = AvatarNodeParameters(theme: nil, accountPeerId: nil, peerId: nil, colors: calculateColors(explicitColorIndex: explicitIndex, peerId: nil, icon: .none, theme: nil), letters: letters, font: self.font, icon: .none, explicitColorIndex: explicitIndex, hasImage: false, clipStyle: .round)
} }
self.displaySuspended = true self.displaySuspended = true
@ -433,8 +539,10 @@ public final class AvatarNode: ASDisplayNode {
context.fill(bounds) context.fill(bounds)
} }
let colorIndex: Int let colors: [UIColor]
if let parameters = parameters as? AvatarNodeParameters { if let parameters = parameters as? AvatarNodeParameters {
colors = parameters.colors
if case .round = parameters.clipStyle { if case .round = parameters.clipStyle {
context.beginPath() context.beginPath()
context.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: bounds.size.width, height: context.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: bounds.size.width, height:
@ -445,59 +553,21 @@ public final class AvatarNode: ASDisplayNode {
context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: bounds.size.width, height: bounds.size.height), cornerRadius: floor(bounds.size.width * 0.25)).cgPath) context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: bounds.size.width, height: bounds.size.height), cornerRadius: floor(bounds.size.width * 0.25)).cgPath)
context.clip() context.clip()
} }
if let explicitColorIndex = parameters.explicitColorIndex {
colorIndex = explicitColorIndex
} else {
if let peerId = parameters.peerId {
if peerId.namespace == .max {
colorIndex = -1
} else {
colorIndex = abs(Int(clamping: peerId.id._internalGetInt64Value()))
}
} else {
colorIndex = -1
}
}
} else { } else {
colorIndex = -1 colors = grayscaleColors
} }
let colorsArray: NSArray let colorsArray: NSArray = colors.map(\.cgColor) as NSArray
var iconColor = UIColor.white var iconColor = UIColor.white
if let parameters = parameters as? AvatarNodeParameters, parameters.icon != .none { if let parameters = parameters as? AvatarNodeParameters, parameters.icon != .none {
if case .deletedIcon = parameters.icon { if case let .archivedChatsIcon(hiddenByDefault) = parameters.icon, let theme = parameters.theme {
colorsArray = grayscaleColors
} else if case .phoneIcon = parameters.icon {
colorsArray = grayscaleColors
} else if case .savedMessagesIcon = parameters.icon {
colorsArray = savedMessagesColors
} else if case .repliesIcon = parameters.icon {
colorsArray = savedMessagesColors
} else if case .editAvatarIcon = parameters.icon, let theme = parameters.theme {
colorsArray = [theme.list.itemAccentColor.withAlphaComponent(0.1).cgColor, theme.list.itemAccentColor.withAlphaComponent(0.1).cgColor]
} else if case let .archivedChatsIcon(hiddenByDefault) = parameters.icon, let theme = parameters.theme {
let backgroundColors: (UIColor, UIColor)
if hiddenByDefault { if hiddenByDefault {
iconColor = theme.chatList.unpinnedArchiveAvatarColor.foregroundColor iconColor = theme.chatList.unpinnedArchiveAvatarColor.foregroundColor
backgroundColors = theme.chatList.unpinnedArchiveAvatarColor.backgroundColors.colors
} else { } else {
iconColor = theme.chatList.pinnedArchiveAvatarColor.foregroundColor iconColor = theme.chatList.pinnedArchiveAvatarColor.foregroundColor
backgroundColors = theme.chatList.pinnedArchiveAvatarColor.backgroundColors.colors
} }
colorsArray = [backgroundColors.1.cgColor, backgroundColors.0.cgColor]
} else {
colorsArray = grayscaleColors
} }
} else if colorIndex == -1 {
if let parameters = parameters as? AvatarNodeParameters, let theme = parameters.theme {
let colors = theme.chatList.unpinnedArchiveAvatarColor.backgroundColors.colors
colorsArray = [colors.1.cgColor, colors.0.cgColor]
} else {
colorsArray = grayscaleColors
}
} else {
colorsArray = AvatarNode.gradientColors[colorIndex % AvatarNode.gradientColors.count]
} }
var locations: [CGFloat] = [1.0, 0.0] var locations: [CGFloat] = [1.0, 0.0]
@ -636,9 +706,9 @@ public func drawPeerAvatarLetters(context: CGContext, size: CGSize, round: Bool
let colorsArray: NSArray let colorsArray: NSArray
if colorIndex == -1 { if colorIndex == -1 {
colorsArray = grayscaleColors colorsArray = grayscaleColors.map(\.cgColor) as NSArray
} else { } else {
colorsArray = AvatarNode.gradientColors[colorIndex % AvatarNode.gradientColors.count] colorsArray = AvatarNode.gradientColors[colorIndex % AvatarNode.gradientColors.count].map(\.cgColor) as NSArray
} }
var locations: [CGFloat] = [1.0, 0.0] var locations: [CGFloat] = [1.0, 0.0]
@ -707,9 +777,9 @@ public func generateAvatarImage(size: CGSize, icon: UIImage?, iconScale: CGFloat
let colorsArray: NSArray let colorsArray: NSArray
if colorIndex == -1 { if colorIndex == -1 {
colorsArray = grayscaleColors colorsArray = grayscaleColors.map(\.cgColor) as NSArray
} else { } else {
colorsArray = AvatarNode.gradientColors[colorIndex % AvatarNode.gradientColors.count] colorsArray = AvatarNode.gradientColors[colorIndex % AvatarNode.gradientColors.count].map(\.cgColor) as NSArray
} }
var locations: [CGFloat] = [1.0, 0.0] var locations: [CGFloat] = [1.0, 0.0]
@ -734,3 +804,190 @@ public func generateAvatarImage(size: CGSize, icon: UIImage?, iconScale: CGFloat
} }
}) })
} }
public final class AvatarBadgeView: UIImageView {
enum OriginalContent: Equatable {
case color(UIColor)
case image(UIImage)
static func ==(lhs: OriginalContent, rhs: OriginalContent) -> Bool {
switch lhs {
case let .color(color):
if case .color(color) = rhs {
return true
} else {
return false
}
case let .image(lhsImage):
if case let .image(rhsImage) = rhs {
return lhsImage === rhsImage
} else {
return false
}
}
}
}
private struct Parameters: Equatable {
var size: CGSize
var text: String
}
private var originalContent: OriginalContent?
private var parameters: Parameters?
override public init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(content: OriginalContent) {
if self.originalContent != content {
self.originalContent = content
self.update()
}
}
public func update(size: CGSize, text: String) {
let parameters = Parameters(size: size, text: text)
if self.parameters != parameters {
self.parameters = parameters
self.update()
}
}
private func update() {
guard let originalContent = self.originalContent, let parameters = self.parameters else {
return
}
let blurredWidth = 16
let blurredHeight = 16
guard let blurredContext = DrawingContext(size: CGSize(width: CGFloat(blurredWidth), height: CGFloat(blurredHeight)), scale: 1.0, opaque: true) else {
return
}
let blurredSize = CGSize(width: CGFloat(blurredWidth), height: CGFloat(blurredHeight))
blurredContext.withContext { c in
switch originalContent {
case let .color(color):
c.setFillColor(color.cgColor)
c.fill(CGRect(origin: CGPoint(), size: blurredSize))
case let .image(image):
c.scaleBy(x: blurredSize.width / parameters.size.width, y: blurredSize.height / parameters.size.height)
let offsetFactor: CGFloat = 1.0 - 0.7071
let imageFrame = CGRect(origin: CGPoint(x: parameters.size.width - image.size.width + offsetFactor * parameters.size.width, y: parameters.size.height - image.size.height + offsetFactor * parameters.size.height), size: image.size)
UIGraphicsPushContext(c)
image.draw(in: imageFrame)
UIGraphicsPopContext()
}
}
var destinationBuffer = vImage_Buffer()
destinationBuffer.width = UInt(blurredWidth)
destinationBuffer.height = UInt(blurredHeight)
destinationBuffer.data = blurredContext.bytes
destinationBuffer.rowBytes = blurredContext.bytesPerRow
vImageBoxConvolve_ARGB8888(
&destinationBuffer,
&destinationBuffer,
nil,
0, 0,
UInt32(15),
UInt32(15),
nil,
vImage_Flags(kvImageEdgeExtend)
)
let divisor: Int32 = 0x1000
let rwgt: CGFloat = 0.3086
let gwgt: CGFloat = 0.6094
let bwgt: CGFloat = 0.0820
let adjustSaturation: CGFloat = 1.9
let a = (1.0 - adjustSaturation) * rwgt + adjustSaturation
let b = (1.0 - adjustSaturation) * rwgt
let c = (1.0 - adjustSaturation) * rwgt
let d = (1.0 - adjustSaturation) * gwgt
let e = (1.0 - adjustSaturation) * gwgt + adjustSaturation
let f = (1.0 - adjustSaturation) * gwgt
let g = (1.0 - adjustSaturation) * bwgt
let h = (1.0 - adjustSaturation) * bwgt
let i = (1.0 - adjustSaturation) * bwgt + adjustSaturation
let satMatrix: [CGFloat] = [
a, b, c, 0,
d, e, f, 0,
g, h, i, 0,
0, 0, 0, 1
]
var matrix: [Int16] = satMatrix.map { value in
return Int16(value * CGFloat(divisor))
}
vImageMatrixMultiply_ARGB8888(&destinationBuffer, &destinationBuffer, &matrix, divisor, nil, nil, vImage_Flags(kvImageDoNotTile))
guard let blurredImage = blurredContext.generateImage() else {
return
}
self.image = generateImage(parameters.size, rotatedContext: { size, context in
UIGraphicsPushContext(context)
context.clear(CGRect(origin: CGPoint(), size: size))
context.setBlendMode(.copy)
context.setFillColor(UIColor.black.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
context.setBlendMode(.sourceIn)
blurredImage.draw(in: CGRect(origin: CGPoint(), size: size), blendMode: .sourceIn, alpha: 1.0)
context.setBlendMode(.normal)
context.setFillColor(UIColor(white: 0.0, alpha: 0.05).cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
let string = NSAttributedString(string: parameters.text, font: Font.bold(floor(parameters.size.height * 0.48)), textColor: .white)
let stringBounds = string.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil)
string.draw(at: CGPoint(x: stringBounds.minX + floorToScreenPixels((size.width - stringBounds.width) / 2.0), y: stringBounds.minY + floorToScreenPixels((size.height - stringBounds.height) / 2.0)))
let lineWidth: CGFloat = 1.5
let lineInset: CGFloat = 2.0
let lineRadius: CGFloat = size.width * 0.5 - lineInset - lineWidth * 0.5
context.setLineWidth(lineWidth)
context.setStrokeColor(UIColor.white.cgColor)
context.setLineCap(.round)
context.addArc(center: CGPoint(x: size.width * 0.5, y: size.height * 0.5), radius: lineRadius, startAngle: CGFloat.pi * 0.5, endAngle: -CGFloat.pi * 0.5, clockwise: false)
context.strokePath()
let sectionAngle: CGFloat = CGFloat.pi / 11.0
for i in 0 ..< 10 {
if i % 2 == 0 {
continue
}
let startAngle = CGFloat.pi * 0.5 - CGFloat(i) * sectionAngle - sectionAngle * 0.1
let endAngle = startAngle - sectionAngle * 0.8
context.addArc(center: CGPoint(x: size.width * 0.5, y: size.height * 0.5), radius: lineRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
context.strokePath()
}
//context.strokeEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: 2.0 + lineWidth * 0.5, dy: 2.0 + lineWidth * 0.5))
UIGraphicsPopContext()
})
}
}

View File

@ -1228,7 +1228,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
} }
var joined = false var joined = false
if case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = item.content, let message = messages.first { if case let .peer(peerData) = item.content, let message = peerData.messages.first {
for media in message.media { for media in message.media {
if let action = media as? TelegramMediaAction, action.action == .peerJoined { if let action = media as? TelegramMediaAction, action.action == .peerJoined {
joined = true joined = true
@ -1242,7 +1242,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
chatListController.navigationPresentation = .master chatListController.navigationPresentation = .master
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupId._asGroup(), chatListController: strongSelf) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupId._asGroup(), chatListController: strongSelf) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController) strongSelf.presentInGlobalOverlay(contextController)
case let .peer(_, peer, threadInfo, _, _, _, _, _, _, _, promoInfo, _, _, _, _, _): case let .peer(peerData):
let peer = peerData.peer
let threadInfo = peerData.threadInfo
let promoInfo = peerData.promoInfo
switch item.index { switch item.index {
case .chatList: case .chatList:
if case let .channel(channel) = peer.peer, channel.flags.contains(.isForum) { if case let .channel(channel) = peer.peer, channel.flags.contains(.isForum) {

View File

@ -215,7 +215,25 @@ private final class ChatListShimmerNode: ASDisplayNode {
) )
let readState = EnginePeerReadCounters() let readState = EnginePeerReadCounters()
return ChatListItem(presentationData: chatListPresentationData, context: context, chatListLocation: .chatList(groupId: .root), filterData: nil, index: .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: 0, messageIndex: EngineMessage.Index(id: EngineMessage.Id(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1))), content: .peer(messages: [message], peer: EngineRenderedPeer(peer: peer1), threadInfo: nil, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false, forumTopicData: nil, topForumTopicItems: []), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) return ChatListItem(presentationData: chatListPresentationData, context: context, chatListLocation: .chatList(groupId: .root), filterData: nil, index: .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: 0, messageIndex: EngineMessage.Index(id: EngineMessage.Id(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1))), content: .peer(ChatListItemContent.PeerData(
messages: [message],
peer: EngineRenderedPeer(peer: peer1),
threadInfo: nil,
combinedReadState: readState,
isRemovedFromTotalUnreadCount: false,
presence: nil,
hasUnseenMentions: false,
hasUnseenReactions: false,
draftState: nil,
inputActivities: nil,
promoInfo: nil,
ignoreUnreadBadge: false,
displayAsMessage: false,
hasFailedMessages: false,
forumTopicData: nil,
topForumTopicItems: [],
autoremoveTimeout: nil
)), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
} }
var itemNodes: [ChatListItemNode] = [] var itemNodes: [ChatListItemNode] = []

View File

@ -749,7 +749,25 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
index = .chatList( EngineChatList.Item.Index.ChatList(pinningIndex: nil, messageIndex: message.index)) index = .chatList( EngineChatList.Item.Index.ChatList(pinningIndex: nil, messageIndex: message.index))
} }
} }
return ChatListItem(presentationData: presentationData, context: context, chatListLocation: location, filterData: nil, index: index, content: .peer(messages: [message], peer: peer, threadInfo: chatThreadInfo, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: true, displayAsMessage: false, hasFailedMessages: false, forumTopicData: nil, topForumTopicItems: []), editing: false, hasActiveRevealControls: false, selected: false, header: tagMask == nil ? header : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) return ChatListItem(presentationData: presentationData, context: context, chatListLocation: location, filterData: nil, index: index, content: .peer(ChatListItemContent.PeerData(
messages: [message],
peer: peer,
threadInfo: chatThreadInfo,
combinedReadState: readState,
isRemovedFromTotalUnreadCount: false,
presence: nil,
hasUnseenMentions: false,
hasUnseenReactions: false,
draftState: nil,
inputActivities: nil,
promoInfo: nil,
ignoreUnreadBadge: true,
displayAsMessage: false,
hasFailedMessages: false,
forumTopicData: nil,
topForumTopicItems: [],
autoremoveTimeout: nil
)), editing: false, hasActiveRevealControls: false, selected: false, header: tagMask == nil ? header : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
} }
case let .addContact(phoneNumber, theme, strings): case let .addContact(phoneNumber, theme, strings):
return ContactsAddItem(theme: theme, strings: strings, phoneNumber: phoneNumber, header: ChatListSearchItemHeader(type: .phoneNumber, theme: theme, strings: strings, actionTitle: nil, action: nil), action: { return ContactsAddItem(theme: theme, strings: strings, phoneNumber: phoneNumber, header: ChatListSearchItemHeader(type: .phoneNumber, theme: theme, strings: strings, actionTitle: nil, action: nil), action: {
@ -1878,8 +1896,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
return return
} }
switch item.content { switch item.content {
case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .peer(peerData):
if let peer = peer.peer, let message = messages.first { if let peer = peerData.peer.peer, let message = peerData.messages.first {
peerContextAction(peer, .search(message.id), node, gesture, location) peerContextAction(peer, .search(message.id), node, gesture, location)
} }
case .groupReference: case .groupReference:
@ -2935,8 +2953,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
bounds = selectedItemNode.bounds bounds = selectedItemNode.bounds
} }
switch item.content { switch item.content {
case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .peer(peerData):
return (selectedItemNode.view, bounds, messages.last?.id ?? peer.peerId) return (selectedItemNode.view, bounds, peerData.messages.last?.id ?? peerData.peer.peerId)
case let .groupReference(groupId, _, _, _, _): case let .groupReference(groupId, _, _, _, _):
return (selectedItemNode.view, bounds, groupId) return (selectedItemNode.view, bounds, groupId)
} }
@ -3129,7 +3147,25 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
associatedThreadInfo: nil associatedThreadInfo: nil
) )
let readState = EnginePeerReadCounters() let readState = EnginePeerReadCounters()
return ChatListItem(presentationData: chatListPresentationData, context: context, chatListLocation: .chatList(groupId: .root), filterData: nil, index: .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: 0, messageIndex: EngineMessage.Index(id: EngineMessage.Id(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1))), content: .peer(messages: [message], peer: EngineRenderedPeer(peer: peer1), threadInfo: nil, combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false, forumTopicData: nil, topForumTopicItems: []), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction) return ChatListItem(presentationData: chatListPresentationData, context: context, chatListLocation: .chatList(groupId: .root), filterData: nil, index: .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: 0, messageIndex: EngineMessage.Index(id: EngineMessage.Id(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1))), content: .peer(ChatListItemContent.PeerData(
messages: [message],
peer: EngineRenderedPeer(peer: peer1),
threadInfo: nil,
combinedReadState: readState,
isRemovedFromTotalUnreadCount: false,
presence: nil,
hasUnseenMentions: false,
hasUnseenReactions: false,
draftState: nil,
inputActivities: nil,
promoInfo: nil,
ignoreUnreadBadge: false,
displayAsMessage: false,
hasFailedMessages: false,
forumTopicData: nil,
topForumTopicItems: [],
autoremoveTimeout: nil
)), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
case .media: case .media:
return nil return nil
case .links: case .links:

View File

@ -64,16 +64,74 @@ public enum ChatListItemContent {
return true return true
} }
} }
public struct PeerData {
public var messages: [EngineMessage]
public var peer: EngineRenderedPeer
public var threadInfo: ThreadInfo?
public var combinedReadState: EnginePeerReadCounters?
public var isRemovedFromTotalUnreadCount: Bool
public var presence: EnginePeer.Presence?
public var hasUnseenMentions: Bool
public var hasUnseenReactions: Bool
public var draftState: DraftState?
public var inputActivities: [(EnginePeer, PeerInputActivity)]?
public var promoInfo: ChatListNodeEntryPromoInfo?
public var ignoreUnreadBadge: Bool
public var displayAsMessage: Bool
public var hasFailedMessages: Bool
public var forumTopicData: EngineChatList.ForumTopicData?
public var topForumTopicItems: [EngineChatList.ForumTopicData]
public var autoremoveTimeout: Int32?
public init(
messages: [EngineMessage],
peer: EngineRenderedPeer,
threadInfo: ThreadInfo?,
combinedReadState: EnginePeerReadCounters?,
isRemovedFromTotalUnreadCount: Bool,
presence: EnginePeer.Presence?,
hasUnseenMentions: Bool,
hasUnseenReactions: Bool,
draftState: DraftState?,
inputActivities: [(EnginePeer, PeerInputActivity)]?,
promoInfo: ChatListNodeEntryPromoInfo?,
ignoreUnreadBadge: Bool,
displayAsMessage: Bool,
hasFailedMessages: Bool,
forumTopicData: EngineChatList.ForumTopicData?,
topForumTopicItems: [EngineChatList.ForumTopicData],
autoremoveTimeout: Int32?
) {
self.messages = messages
self.peer = peer
self.threadInfo = threadInfo
self.combinedReadState = combinedReadState
self.isRemovedFromTotalUnreadCount = isRemovedFromTotalUnreadCount
self.presence = presence
self.hasUnseenMentions = hasUnseenMentions
self.hasUnseenReactions = hasUnseenReactions
self.draftState = draftState
self.inputActivities = inputActivities
self.promoInfo = promoInfo
self.ignoreUnreadBadge = ignoreUnreadBadge
self.displayAsMessage = displayAsMessage
self.hasFailedMessages = hasFailedMessages
self.forumTopicData = forumTopicData
self.topForumTopicItems = topForumTopicItems
self.autoremoveTimeout = autoremoveTimeout
}
}
case peer(messages: [EngineMessage], peer: EngineRenderedPeer, threadInfo: ThreadInfo?, combinedReadState: EnginePeerReadCounters?, isRemovedFromTotalUnreadCount: Bool, presence: EnginePeer.Presence?, hasUnseenMentions: Bool, hasUnseenReactions: Bool, draftState: DraftState?, inputActivities: [(EnginePeer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool, forumTopicData: EngineChatList.ForumTopicData?, topForumTopicItems: [EngineChatList.ForumTopicData]) case peer(PeerData)
case groupReference(groupId: EngineChatList.Group, peers: [EngineChatList.GroupItem.Item], message: EngineMessage?, unreadCount: Int, hiddenByDefault: Bool) case groupReference(groupId: EngineChatList.Group, peers: [EngineChatList.GroupItem.Item], message: EngineMessage?, unreadCount: Int, hiddenByDefault: Bool)
public var chatLocation: ChatLocation? { public var chatLocation: ChatLocation? {
switch self { switch self {
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .peer(peerData):
return .peer(id: peer.peerId) return .peer(id: peerData.peer.peerId)
case .groupReference: case .groupReference:
return nil return nil
} }
} }
} }
@ -178,23 +236,23 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour {
public func selected(listView: ListView) { public func selected(listView: ListView) {
switch self.content { switch self.content {
case let .peer(messages, peer, _, _, _, _, _, _, _, _, promoInfo, _, _, _, _, _): case let .peer(peerData):
if let message = messages.last, let peer = peer.peer { if let message = peerData.messages.last, let peer = peerData.peer.peer {
var threadId: Int64? var threadId: Int64?
if case let .forum(_, _, threadIdValue, _, _) = self.index { if case let .forum(_, _, threadIdValue, _, _) = self.index {
threadId = threadIdValue threadId = threadIdValue
}
if threadId == nil, self.interaction.searchTextHighightState != nil, case let .channel(channel) = peer, channel.flags.contains(.isForum) {
threadId = message.threadId
}
self.interaction.messageSelected(peer, threadId, message, promoInfo)
} else if let peer = peer.peer {
self.interaction.peerSelected(peer, nil, nil, promoInfo)
} else if let peer = peer.peers[peer.peerId] {
self.interaction.peerSelected(peer, nil, nil, promoInfo)
} }
case let .groupReference(groupId, _, _, _, _): if threadId == nil, self.interaction.searchTextHighightState != nil, case let .channel(channel) = peerData.peer.peer, channel.flags.contains(.isForum) {
self.interaction.groupSelected(groupId) threadId = message.threadId
}
self.interaction.messageSelected(peer, threadId, message, peerData.promoInfo)
} else if let peer = peerData.peer.peer {
self.interaction.peerSelected(peer, nil, nil, peerData.promoInfo)
} else if let peer = peerData.peer.peers[peerData.peer.peerId] {
self.interaction.peerSelected(peer, nil, nil, peerData.promoInfo)
}
case let .groupReference(groupId, _, _, _, _):
self.interaction.groupSelected(groupId)
} }
} }
@ -837,6 +895,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var avatarBadgeNode: ChatListBadgeNode? var avatarBadgeNode: ChatListBadgeNode?
var avatarBadgeBackground: ASImageNode? var avatarBadgeBackground: ASImageNode?
let onlineNode: PeerOnlineMarkerNode let onlineNode: PeerOnlineMarkerNode
var avatarTimerBadge: AvatarBadgeView?
let pinnedIconNode: ASImageNode let pinnedIconNode: ASImageNode
var secretIconNode: ASImageNode? var secretIconNode: ASImageNode?
var credibilityIconView: ComponentHostView<Empty>? var credibilityIconView: ComponentHostView<Empty>?
@ -905,8 +964,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
result += "\n\(item.presentationData.strings.VoiceOver_Chat_UnreadMessages(Int32(allCount)))" result += "\n\(item.presentationData.strings.VoiceOver_Chat_UnreadMessages(Int32(allCount)))"
} }
return result return result
case let .peer(_, peer, _, combinedReadState, _, _, _, _, _, _, _, _, _, _, _, _): case let .peer(peerData):
guard let chatMainPeer = peer.chatMainPeer else { guard let chatMainPeer = peerData.peer.chatMainPeer else {
return nil return nil
} }
var result = "" var result = ""
@ -915,7 +974,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else { } else {
result += chatMainPeer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) result += chatMainPeer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
} }
if let combinedReadState = combinedReadState, combinedReadState.count > 0 { if let combinedReadState = peerData.combinedReadState, combinedReadState.count > 0 {
result += "\n\(item.presentationData.strings.VoiceOver_Chat_UnreadMessages(combinedReadState.count))" result += "\n\(item.presentationData.strings.VoiceOver_Chat_UnreadMessages(combinedReadState.count))"
} }
return result return result
@ -965,19 +1024,19 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else { } else {
return item.presentationData.strings.VoiceOver_ChatList_MessageEmpty return item.presentationData.strings.VoiceOver_ChatList_MessageEmpty
} }
case let .peer(messages, peer, _, combinedReadState, _, _, _, _, _, _, _, _, _, _, _, _): case let .peer(peerData):
if let message = messages.last { if let message = peerData.messages.last {
var result = "" var result = ""
if message.flags.contains(.Incoming) { if message.flags.contains(.Incoming) {
result += item.presentationData.strings.VoiceOver_ChatList_Message result += item.presentationData.strings.VoiceOver_ChatList_Message
} else { } else {
result += item.presentationData.strings.VoiceOver_ChatList_OutgoingMessage result += item.presentationData.strings.VoiceOver_ChatList_OutgoingMessage
} }
let (_, initialHideAuthor, messageText, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: messages, chatPeer: peer, accountPeerId: item.context.account.peerId, isPeerGroup: false) let (_, initialHideAuthor, messageText, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: peerData.messages, chatPeer: peerData.peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, case .user = author { if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, case .user = author {
result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageFrom(author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).string)" result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageFrom(author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).string)"
} }
if !message.flags.contains(.Incoming), let combinedReadState = combinedReadState, combinedReadState.isOutgoingMessageIndexRead(message.index) { if !message.flags.contains(.Incoming), let combinedReadState = peerData.combinedReadState, combinedReadState.isOutgoingMessageIndexRead(message.index) {
result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageRead)" result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageRead)"
} }
result += "\n\(messageText)" result += "\n\(messageText)"
@ -1165,7 +1224,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} }
var threadId: Int64? var threadId: Int64?
if let value = strongSelf.hitTest(location, with: nil), value === strongSelf.compoundTextButtonNode?.view { if let value = strongSelf.hitTest(location, with: nil), value === strongSelf.compoundTextButtonNode?.view {
if case let .peer(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, topForumTopicItems) = item.content, let topicItem = topForumTopicItems.first { if case let .peer(peerData) = item.content, let topicItem = peerData.topForumTopicItems.first {
threadId = topicItem.id threadId = topicItem.id
} }
} }
@ -1193,14 +1252,14 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var displayAsMessage = false var displayAsMessage = false
var enablePreview = true var enablePreview = true
switch item.content { switch item.content {
case let .peer(messages, peerValue, _, _, _, _, _, _, _, _, _, _, displayAsMessageValue, _, _, _): case let .peer(peerData):
displayAsMessage = displayAsMessageValue displayAsMessage = peerData.displayAsMessage
if displayAsMessage, case let .user(author) = messages.last?.author { if displayAsMessage, case let .user(author) = peerData.messages.last?.author {
peer = .user(author) peer = .user(author)
} else { } else {
peer = peerValue.chatMainPeer peer = peerData.peer.chatMainPeer
} }
if peerValue.peerId.namespace == Namespaces.Peer.SecretChat { if peerData.peer.peerId.namespace == Namespaces.Peer.SecretChat {
enablePreview = false enablePreview = false
} }
case let .groupReference(_, _, _, _, hiddenByDefault): case let .groupReference(_, _, _, _, hiddenByDefault):
@ -1371,8 +1430,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
guard let item = self.item, item.editing else { guard let item = self.item, item.editing else {
return return
} }
if case let .peer(_, peer, _, _, _, _, _, _, _, _, promoInfo, _, _, _, _, _) = item.content { if case let .peer(peerData) = item.content {
if promoInfo == nil, let mainPeer = peer.peer { if peerData.promoInfo == nil, let mainPeer = peerData.peer.peer {
switch item.index { switch item.index {
case let .forum(_, _, threadIdValue, _, _): case let .forum(_, _, threadIdValue, _, _):
item.interaction.toggleThreadsSelection([threadIdValue], !item.selected) item.interaction.toggleThreadsSelection([threadIdValue], !item.selected)
@ -1429,12 +1488,30 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var threadInfo: ChatListItemContent.ThreadInfo? var threadInfo: ChatListItemContent.ThreadInfo?
var forumTopicData: EngineChatList.ForumTopicData? var forumTopicData: EngineChatList.ForumTopicData?
var topForumTopicItems: [EngineChatList.ForumTopicData] = [] var topForumTopicItems: [EngineChatList.ForumTopicData] = []
topForumTopicItems.removeAll() var autoremoveTimeout: Int32?
var groupHiddenByDefault = false var groupHiddenByDefault = false
switch item.content { switch item.content {
case let .peer(messagesValue, peerValue, threadInfoValue, combinedReadStateValue, isRemovedFromTotalUnreadCountValue, peerPresenceValue, hasUnseenMentionsValue, hasUnseenReactionsValue, draftStateValue, inputActivitiesValue, promoInfoValue, ignoreUnreadBadge, displayAsMessageValue, _, forumTopicDataValue, topForumTopicItemsValue): case let .peer(peerData):
let messagesValue = peerData.messages
let peerValue = peerData.peer
let threadInfoValue = peerData.threadInfo
let combinedReadStateValue = peerData.combinedReadState
let isRemovedFromTotalUnreadCountValue = peerData.isRemovedFromTotalUnreadCount
let peerPresenceValue = peerData.presence
let hasUnseenMentionsValue = peerData.hasUnseenMentions
let hasUnseenReactionsValue = peerData.hasUnseenReactions
let draftStateValue = peerData.draftState
let inputActivitiesValue = peerData.inputActivities
let promoInfoValue = peerData.promoInfo
let ignoreUnreadBadge = peerData.ignoreUnreadBadge
let displayAsMessageValue = peerData.displayAsMessage
let forumTopicDataValue = peerData.forumTopicData
let topForumTopicItemsValue = peerData.topForumTopicItems
autoremoveTimeout = peerData.autoremoveTimeout
messages = messagesValue messages = messagesValue
contentPeer = .chat(peerValue) contentPeer = .chat(peerValue)
combinedReadState = combinedReadStateValue combinedReadState = combinedReadStateValue
@ -1582,6 +1659,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let badgeDiameter = floor(item.presentationData.fontSize.baseDisplaySize * 20.0 / 17.0) let badgeDiameter = floor(item.presentationData.fontSize.baseDisplaySize * 20.0 / 17.0)
let avatarBadgeDiameter: CGFloat = floor(floor(item.presentationData.fontSize.itemListBaseFontSize * 22.0 / 17.0)) let avatarBadgeDiameter: CGFloat = floor(floor(item.presentationData.fontSize.itemListBaseFontSize * 22.0 / 17.0))
let avatarTimerBadgeDiameter: CGFloat = floor(floor(item.presentationData.fontSize.itemListBaseFontSize * 24.0 / 17.0))
let currentAvatarBadgeCleanBackgroundImage: UIImage? = PresentationResourcesChatList.badgeBackgroundBorder(item.presentationData.theme, diameter: avatarBadgeDiameter + 4.0) let currentAvatarBadgeCleanBackgroundImage: UIImage? = PresentationResourcesChatList.badgeBackgroundBorder(item.presentationData.theme, diameter: avatarBadgeDiameter + 4.0)
@ -1905,8 +1983,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
switch item.content { switch item.content {
case let .groupReference(_, _, message, _, _): case let .groupReference(_, _, message, _, _):
topIndex = message?.index topIndex = message?.index
case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .peer(peerData):
topIndex = messages.first?.index topIndex = peerData.messages.first?.index
} }
if let topIndex { if let topIndex {
var t = Int(topIndex.timestamp) var t = Int(topIndex.timestamp)
@ -2073,8 +2151,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
if !isPeerGroup && !isAccountPeer && threadInfo == nil { if !isPeerGroup && !isAccountPeer && threadInfo == nil {
if displayAsMessage { if displayAsMessage {
switch item.content { switch item.content {
case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .peer(peerData):
if let peer = messages.last?.author { if let peer = peerData.messages.last?.author {
if case let .user(user) = peer, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled { if case let .user(user) = peer, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled {
currentCredibilityIconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2)) currentCredibilityIconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2))
} else if peer.isScam { } else if peer.isScam {
@ -2258,7 +2336,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var peerRevealOptions: [ItemListRevealOption] var peerRevealOptions: [ItemListRevealOption]
var peerLeftRevealOptions: [ItemListRevealOption] var peerLeftRevealOptions: [ItemListRevealOption]
switch item.content { switch item.content {
case let .peer(_, renderedPeer, _, _, _, presence, _, _, _, _, _, _, displayAsMessage, _, _, _): case let .peer(peerData):
let renderedPeer = peerData.peer
let presence = peerData.presence
let displayAsMessage = peerData.displayAsMessage
if !displayAsMessage { if !displayAsMessage {
if case let .user(peer) = renderedPeer.chatMainPeer, let presence = presence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != item.context.account.peerId { if case let .user(peer) = renderedPeer.chatMainPeer, let presence = presence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != item.context.account.peerId {
let updatedPresence = EnginePeer.Presence(status: presence.status, lastActivity: 0) let updatedPresence = EnginePeer.Presence(status: presence.status, lastActivity: 0)
@ -2676,6 +2758,37 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .regular, voiceChat: onlineIsVoiceChat) onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .regular, voiceChat: onlineIsVoiceChat)
} }
strongSelf.onlineNode.setImage(onlineIcon, color: item.presentationData.theme.list.itemCheckColors.foregroundColor, transition: .immediate) strongSelf.onlineNode.setImage(onlineIcon, color: item.presentationData.theme.list.itemCheckColors.foregroundColor, transition: .immediate)
let autoremoveTimeoutFraction: CGFloat
if online {
autoremoveTimeoutFraction = 0.0
} else {
autoremoveTimeoutFraction = 1.0 - onlineInlineNavigationFraction
}
if let autoremoveTimeout = autoremoveTimeout {
let avatarTimerBadge: AvatarBadgeView
var avatarTimerTransition = transition
if let current = strongSelf.avatarTimerBadge {
avatarTimerBadge = current
} else {
avatarTimerTransition = .immediate
avatarTimerBadge = AvatarBadgeView(frame: CGRect())
strongSelf.avatarTimerBadge = avatarTimerBadge
strongSelf.avatarNode.badgeView = avatarTimerBadge
strongSelf.contextContainer.view.addSubview(avatarTimerBadge)
}
let avatarBadgeSize = CGSize(width: avatarTimerBadgeDiameter, height: avatarTimerBadgeDiameter)
avatarTimerBadge.update(size: avatarBadgeSize, text: shortTimeIntervalString(strings: item.presentationData.strings, value: autoremoveTimeout))
let avatarBadgeFrame = CGRect(origin: CGPoint(x: avatarFrame.maxX - avatarBadgeSize.width, y: avatarFrame.maxY - avatarBadgeSize.height), size: avatarBadgeSize)
avatarTimerTransition.updatePosition(layer: avatarTimerBadge.layer, position: avatarBadgeFrame.center)
avatarTimerTransition.updateBounds(layer: avatarTimerBadge.layer, bounds: CGRect(origin: CGPoint(), size: avatarBadgeFrame.size))
avatarTimerTransition.updateTransformScale(layer: avatarTimerBadge.layer, scale: autoremoveTimeoutFraction * 1.0 + (1.0 - autoremoveTimeoutFraction) * 0.001)
} else if let avatarTimerBadge = strongSelf.avatarTimerBadge {
strongSelf.avatarTimerBadge = nil
strongSelf.avatarNode.badgeView = nil
avatarTimerBadge.removeFromSuperview()
}
let _ = measureApply() let _ = measureApply()
let _ = dateApply() let _ = dateApply()
@ -3194,10 +3307,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
guard let item else { guard let item else {
return return
} }
guard case let .peer(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, topForumTopicItems) = item.content else { guard case let .peer(peerData) = item.content else {
return return
} }
guard let topicItem = topForumTopicItems.first else { guard let topicItem = peerData.topForumTopicItems.first else {
return return
} }
guard case let .chatList(index) = item.index else { guard case let .chatList(index) = item.index else {
@ -3522,7 +3635,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
close = false close = false
case RevealOptionKey.delete.rawValue: case RevealOptionKey.delete.rawValue:
var joined = false var joined = false
if case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = item.content, let message = messages.first { if case let .peer(peerData) = item.content, let message = peerData.messages.first {
for media in message.media { for media in message.media {
if let action = media as? TelegramMediaAction, action.action == .peerJoined { if let action = media as? TelegramMediaAction, action.action == .peerJoined {
joined = true joined = true
@ -3558,8 +3671,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
item.interaction.toggleArchivedFolderHiddenByDefault() item.interaction.toggleArchivedFolderHiddenByDefault()
close = false close = false
case RevealOptionKey.hidePsa.rawValue: case RevealOptionKey.hidePsa.rawValue:
if let item = self.item, case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = item.content { if let item = self.item, case let .peer(peerData) = item.content {
item.interaction.hidePsa(peer.peerId) item.interaction.hidePsa(peerData.peer.peerId)
} }
close = false close = false
self.skipFadeout = true self.skipFadeout = true

View File

@ -345,7 +345,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
chatListLocation: location, chatListLocation: location,
filterData: filterData, filterData: filterData,
index: index, index: index,
content: .peer( content: .peer(ChatListItemContent.PeerData(
messages: peerEntry.messages, messages: peerEntry.messages,
peer: peer, peer: peer,
threadInfo: threadInfo, threadInfo: threadInfo,
@ -361,8 +361,9 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
displayAsMessage: false, displayAsMessage: false,
hasFailedMessages: hasFailedMessages, hasFailedMessages: hasFailedMessages,
forumTopicData: forumTopicData, forumTopicData: forumTopicData,
topForumTopicItems: topForumTopicItems topForumTopicItems: topForumTopicItems,
), autoremoveTimeout: peerEntry.autoremoveTimeout
)),
editing: editing, editing: editing,
hasActiveRevealControls: hasActiveRevealControls, hasActiveRevealControls: hasActiveRevealControls,
selected: selected, selected: selected,
@ -601,7 +602,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
chatListLocation: location, chatListLocation: location,
filterData: filterData, filterData: filterData,
index: index, index: index,
content: .peer( content: .peer(ChatListItemContent.PeerData(
messages: peerEntry.messages, messages: peerEntry.messages,
peer: peer, peer: peer,
threadInfo: threadInfo, threadInfo: threadInfo,
@ -617,8 +618,9 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
displayAsMessage: false, displayAsMessage: false,
hasFailedMessages: hasFailedMessages, hasFailedMessages: hasFailedMessages,
forumTopicData: forumTopicData, forumTopicData: forumTopicData,
topForumTopicItems: topForumTopicItems topForumTopicItems: topForumTopicItems,
), autoremoveTimeout: peerEntry.autoremoveTimeout
)),
editing: editing, editing: editing,
hasActiveRevealControls: hasActiveRevealControls, hasActiveRevealControls: hasActiveRevealControls,
selected: selected, selected: selected,
@ -2084,7 +2086,7 @@ public final class ChatListNode: ListView {
var isHiddenItemVisible = false var isHiddenItemVisible = false
strongSelf.forEachItemNode({ itemNode in strongSelf.forEachItemNode({ itemNode in
if let itemNode = itemNode as? ChatListItemNode, let item = itemNode.item { if let itemNode = itemNode as? ChatListItemNode, let item = itemNode.item {
if case let .peer(_, _, threadInfo, _, _, _, _, _, _, _, _, _, _, _, _, _) = item.content, let threadInfo { if case let .peer(peerData) = item.content, let threadInfo = peerData.threadInfo {
if threadInfo.isHidden { if threadInfo.isHidden {
isHiddenItemVisible = true isHiddenItemVisible = true
} }
@ -2368,8 +2370,8 @@ public final class ChatListNode: ListView {
for item in transition.insertItems { for item in transition.insertItems {
if let item = item.item as? ChatListItem { if let item = item.item as? ChatListItem {
switch item.content { switch item.content {
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .peer(peerData):
insertedPeerIds.append(peer.peerId) insertedPeerIds.append(peerData.peer.peerId)
case .groupReference: case .groupReference:
break break
} }
@ -2754,8 +2756,8 @@ public final class ChatListNode: ListView {
if resultPeer == nil, let itemNode = itemNode as? ListViewItemNode, itemNode.frame.contains(point) { if resultPeer == nil, let itemNode = itemNode as? ListViewItemNode, itemNode.frame.contains(point) {
if let itemNode = itemNode as? ChatListItemNode, let item = itemNode.item { if let itemNode = itemNode as? ChatListItemNode, let item = itemNode.item {
switch item.content { switch item.content {
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .peer(peerData):
resultPeer = peer.peer resultPeer = peerData.peer.peer
default: default:
break break
} }
@ -2771,8 +2773,8 @@ public final class ChatListNode: ListView {
if resultThreadId == nil, let itemNode = itemNode as? ListViewItemNode, itemNode.frame.contains(point) { if resultThreadId == nil, let itemNode = itemNode as? ListViewItemNode, itemNode.frame.contains(point) {
if let itemNode = itemNode as? ChatListItemNode, let item = itemNode.item { if let itemNode = itemNode as? ChatListItemNode, let item = itemNode.item {
switch item.content { switch item.content {
case let .peer(_, _, threadInfo, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .peer(peerData):
resultThreadId = threadInfo?.id resultThreadId = peerData.threadInfo?.id
default: default:
break break
} }

View File

@ -143,6 +143,8 @@ public class ImageNode: ASDisplayNode {
} }
} }
public var contentUpdated: ((UIImage?) -> Void)?
public init(enableHasImage: Bool = false, enableEmpty: Bool = false, enableAnimatedTransition: Bool = false) { public init(enableHasImage: Bool = false, enableEmpty: Bool = false, enableAnimatedTransition: Bool = false) {
if enableHasImage { if enableHasImage {
self.hasImage = ValuePromise(false, ignoreRepeated: true) self.hasImage = ValuePromise(false, ignoreRepeated: true)
@ -187,8 +189,10 @@ public class ImageNode: ASDisplayNode {
} else { } else {
strongSelf.contents = image strongSelf.contents = image
} }
strongSelf.contentUpdated?(next)
} else if strongSelf.enableEmpty { } else if strongSelf.enableEmpty {
strongSelf.contents = nil strongSelf.contents = nil
strongSelf.contentUpdated?(nil)
} }
if !reportedHasImage { if !reportedHasImage {
if let hasImage = strongSelf.hasImage { if let hasImage = strongSelf.hasImage {
@ -210,6 +214,7 @@ public class ImageNode: ASDisplayNode {
self.contents = nil self.contents = nil
self.disposable.set(nil) self.disposable.set(nil)
self.contentUpdated?(nil)
} }
public var image: UIImage? { public var image: UIImage? {

View File

@ -76,7 +76,7 @@ class GlobalAutoremoveHeaderItemNode: ListViewItemNode {
func asyncLayout() -> (_ item: GlobalAutoremoveHeaderItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { func asyncLayout() -> (_ item: GlobalAutoremoveHeaderItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
return { item, params, neighbors in return { item, params, neighbors in
//let leftInset: CGFloat = 32.0 + params.leftInset //let leftInset: CGFloat = 32.0 + params.leftInset
let topInset: CGFloat = 92.0 let topInset: CGFloat = 110.0
let contentSize = CGSize(width: params.width, height: topInset) let contentSize = CGSize(width: params.width, height: topInset)
let insets = itemListNeighborsGroupedInsets(neighbors, params) let insets = itemListNeighborsGroupedInsets(neighbors, params)
@ -86,12 +86,12 @@ class GlobalAutoremoveHeaderItemNode: ListViewItemNode {
return (layout, { [weak self] in return (layout, { [weak self] in
if let strongSelf = self { if let strongSelf = self {
if strongSelf.item == nil { if strongSelf.item == nil {
strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "GlobalAutoRemove"), width: 192, height: 192, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "GlobalAutoRemove"), width: 220, height: 220, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
strongSelf.animationNode.visibility = true strongSelf.animationNode.visibility = true
} }
strongSelf.item = item strongSelf.item = item
let iconSize = CGSize(width: 96.0, height: 96.0) let iconSize = CGSize(width: 110.0, height: 110.0)
strongSelf.animationNode.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0), y: -10.0), size: iconSize) strongSelf.animationNode.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0), y: -10.0), size: iconSize)
strongSelf.animationNode.updateLayout(size: iconSize) strongSelf.animationNode.updateLayout(size: iconSize)
} }

View File

@ -267,7 +267,7 @@ public func globalAutoremoveScreen(context: AccountContext, initialValue: Int32,
presentControllerImpl?(standardTextAlertController( presentControllerImpl?(standardTextAlertController(
theme: AlertControllerTheme(presentationData: presentationData), theme: AlertControllerTheme(presentationData: presentationData),
title: "Self-Destruct Timer", title: "Self-Destruct Timer",
text: "Are you sure you want all messages in new chats started by you to be automatically deleted for everyone \(valueText) after they have been sent?", text: "Are you sure you want all messages in your new private chats and in new groups you create to be automatically deleted for everyone \(valueText) after they have been sent?",
actions: [ actions: [
TextAlertAction(type: .defaultAction, title: "Enable Auto-Deletion", action: { TextAlertAction(type: .defaultAction, title: "Enable Auto-Deletion", action: {
apply(timeout) apply(timeout)

View File

@ -242,7 +242,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
chatListLocation: .chatList(groupId: .root), chatListLocation: .chatList(groupId: .root),
filterData: nil, filterData: nil,
index: .chatList(ChatListIndex(pinningIndex: isPinned ? 0 : nil, messageIndex: MessageIndex(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 0), timestamp: timestamp))), index: .chatList(ChatListIndex(pinningIndex: isPinned ? 0 : nil, messageIndex: MessageIndex(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 0), timestamp: timestamp))),
content: .peer( content: .peer(ChatListItemContent.PeerData(
messages: [ messages: [
EngineMessage( EngineMessage(
stableId: 0, stableId: 0,
@ -285,8 +285,9 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
displayAsMessage: false, displayAsMessage: false,
hasFailedMessages: false, hasFailedMessages: false,
forumTopicData: nil, forumTopicData: nil,
topForumTopicItems: [] topForumTopicItems: [],
), autoremoveTimeout: nil
)),
editing: false, editing: false,
hasActiveRevealControls: false, hasActiveRevealControls: false,
selected: false, selected: false,

View File

@ -862,7 +862,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
chatListLocation: .chatList(groupId: .root), chatListLocation: .chatList(groupId: .root),
filterData: nil, filterData: nil,
index: .chatList(ChatListIndex(pinningIndex: isPinned ? 0 : nil, messageIndex: MessageIndex(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 0), timestamp: timestamp))), index: .chatList(ChatListIndex(pinningIndex: isPinned ? 0 : nil, messageIndex: MessageIndex(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 0), timestamp: timestamp))),
content: .peer( content: .peer(ChatListItemContent.PeerData(
messages: [ messages: [
EngineMessage( EngineMessage(
stableId: 0, stableId: 0,
@ -905,8 +905,9 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
displayAsMessage: false, displayAsMessage: false,
hasFailedMessages: false, hasFailedMessages: false,
forumTopicData: nil, forumTopicData: nil,
topForumTopicItems: [] topForumTopicItems: [],
), autoremoveTimeout: nil
)),
editing: false, editing: false,
hasActiveRevealControls: false, hasActiveRevealControls: false,
selected: false, selected: false,

View File

@ -385,7 +385,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
chatListLocation: .chatList(groupId: .root), chatListLocation: .chatList(groupId: .root),
filterData: nil, filterData: nil,
index: .chatList(ChatListIndex(pinningIndex: isPinned ? 0 : nil, messageIndex: MessageIndex(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 0), timestamp: timestamp))), index: .chatList(ChatListIndex(pinningIndex: isPinned ? 0 : nil, messageIndex: MessageIndex(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 0), timestamp: timestamp))),
content: .peer( content: .peer(ChatListItemContent.PeerData(
messages: [ messages: [
EngineMessage( EngineMessage(
stableId: 0, stableId: 0,
@ -428,8 +428,9 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
displayAsMessage: false, displayAsMessage: false,
hasFailedMessages: false, hasFailedMessages: false,
forumTopicData: nil, forumTopicData: nil,
topForumTopicItems: [] topForumTopicItems: [],
), autoremoveTimeout: nil
)),
editing: false, editing: false,
hasActiveRevealControls: false, hasActiveRevealControls: false,
selected: false, selected: false,

View File

@ -336,7 +336,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
} }
case .channelMigratedFromGroup, .groupMigratedToChannel: case .channelMigratedFromGroup, .groupMigratedToChannel:
attributedString = NSAttributedString(string: "", font: titleFont, textColor: primaryTextColor) attributedString = NSAttributedString(string: "", font: titleFont, textColor: primaryTextColor)
case let .messageAutoremoveTimeoutUpdated(timeout, _): case let .messageAutoremoveTimeoutUpdated(timeout, autoSourcePeerId):
let authorString: String let authorString: String
if let author = messageMainPeer(message) { if let author = messageMainPeer(message) {
authorString = author.compactDisplayTitle authorString = author.compactDisplayTitle
@ -349,37 +349,41 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
if timeout > 0 { if timeout > 0 {
let timeValue = timeIntervalString(strings: strings, value: timeout, preferLowerValue: false) let timeValue = timeIntervalString(strings: strings, value: timeout, preferLowerValue: false)
let string: String if let user = messagePeer as? TelegramUser {
if let _ = messagePeer as? TelegramUser { if let autoSourcePeerId = autoSourcePeerId {
if message.author?.id == accountPeerId { if autoSourcePeerId == accountPeerId {
string = strings.Conversation_AutoremoveTimerSetUserYou(timeValue).string attributedString = NSAttributedString(string: strings.Conversation_AutoremoveTimerSetUserGlobalYou(timeValue).string, font: titleFont, textColor: primaryTextColor)
} else {
attributedString = addAttributesToStringWithRanges(strings.Conversation_AutoremoveTimerSetUserGlobal(authorString, timeValue)._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, user.id)]))
}
} else if message.author?.id == accountPeerId {
attributedString = NSAttributedString(string: strings.Conversation_AutoremoveTimerSetUserYou(timeValue).string, font: titleFont, textColor: primaryTextColor)
} else { } else {
string = strings.Conversation_AutoremoveTimerSetUser(authorString, timeValue).string attributedString = NSAttributedString(string: strings.Conversation_AutoremoveTimerSetUser(authorString, timeValue).string, font: titleFont, textColor: primaryTextColor)
} }
} else if let _ = messagePeer as? TelegramGroup { } else if let _ = messagePeer as? TelegramGroup {
if message.author?.id == accountPeerId { if message.author?.id == accountPeerId {
string = strings.Conversation_AutoremoveTimerSetUserYou(timeValue).string attributedString = NSAttributedString(string: strings.Conversation_AutoremoveTimerSetUserYou(timeValue).string, font: titleFont, textColor: primaryTextColor)
} else { } else {
string = strings.Conversation_AutoremoveTimerSetGroup(timeValue).string attributedString = NSAttributedString(string: strings.Conversation_AutoremoveTimerSetGroup(timeValue).string, font: titleFont, textColor: primaryTextColor)
} }
} else if let channel = messagePeer as? TelegramChannel { } else if let channel = messagePeer as? TelegramChannel {
if message.author?.id == accountPeerId { if message.author?.id == accountPeerId {
string = strings.Conversation_AutoremoveTimerSetUserYou(timeValue).string attributedString = NSAttributedString(string: strings.Conversation_AutoremoveTimerSetUserYou(timeValue).string, font: titleFont, textColor: primaryTextColor)
} else { } else {
if case .group = channel.info { if case .group = channel.info {
string = strings.Conversation_AutoremoveTimerSetGroup(timeValue).string attributedString = NSAttributedString(string: strings.Conversation_AutoremoveTimerSetGroup(timeValue).string, font: titleFont, textColor: primaryTextColor)
} else { } else {
string = strings.Conversation_AutoremoveTimerSetChannel(timeValue).string attributedString = NSAttributedString(string: strings.Conversation_AutoremoveTimerSetChannel(timeValue).string, font: titleFont, textColor: primaryTextColor)
} }
} }
} else { } else {
if message.author?.id == accountPeerId { if message.author?.id == accountPeerId {
string = strings.Notification_MessageLifetimeChangedOutgoing(timeValue).string attributedString = NSAttributedString(string: strings.Notification_MessageLifetimeChangedOutgoing(timeValue).string, font: titleFont, textColor: primaryTextColor)
} else { } else {
string = strings.Notification_MessageLifetimeChanged(authorString, timeValue).string attributedString = NSAttributedString(string: strings.Notification_MessageLifetimeChanged(authorString, timeValue).string, font: titleFont, textColor: primaryTextColor)
} }
} }
attributedString = NSAttributedString(string: string, font: titleFont, textColor: primaryTextColor)
} else { } else {
let string: String let string: String
if let _ = messagePeer as? TelegramUser { if let _ = messagePeer as? TelegramUser {

View File

@ -85,7 +85,7 @@ private enum ChatListSearchEntry: Comparable, Identifiable {
chatListLocation: .chatList(groupId: .root), chatListLocation: .chatList(groupId: .root),
filterData: nil, filterData: nil,
index: .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: nil, messageIndex: message.index)), index: .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: nil, messageIndex: message.index)),
content: .peer( content: .peer(ChatListItemContent.PeerData(
messages: [EngineMessage(message)], messages: [EngineMessage(message)],
peer: EngineRenderedPeer(peer), peer: EngineRenderedPeer(peer),
threadInfo: nil, threadInfo: nil,
@ -101,8 +101,9 @@ private enum ChatListSearchEntry: Comparable, Identifiable {
displayAsMessage: true, displayAsMessage: true,
hasFailedMessages: false, hasFailedMessages: false,
forumTopicData: nil, forumTopicData: nil,
topForumTopicItems: [] topForumTopicItems: [],
), autoremoveTimeout: nil
)),
editing: false, editing: false,
hasActiveRevealControls: false, hasActiveRevealControls: false,
selected: false, selected: false,
@ -247,9 +248,9 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
return return
} }
switch item.content { switch item.content {
case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _, _, _, _): case let .peer(peerData):
if let message = messages.first { if let message = peerData.messages.first {
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peer.peerId), subject: .message(id: .id(message.id), highlight: true, timecode: nil), botStart: nil, mode: .standard(previewing: true)) let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerData.peer.peerId), subject: .message(id: .id(message.id), highlight: true, timecode: nil), botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false) chatController.canReadHistory.set(false)
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list([]))), gesture: gesture) let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list([]))), gesture: gesture)
presentInGlobalOverlay(contextController) presentInGlobalOverlay(contextController)