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.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.AutoremoveTimerRemovedUser" = "%1$@ disabled the auto-delete timer";
"Conversation.AutoremoveTimerSetGroup" = "A group admin set messages to automatically delete after %1$@";

View File

@ -9,6 +9,7 @@ import AnimationUI
import AppBundle
import AccountContext
import Emoji
import Accelerate
private let deletedIcon = UIImage(bundleImageName: "Avatar/DeletedIcon")?.precomposed()
private let phoneIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/PhoneIcon"), color: .white)
@ -30,6 +31,7 @@ private class AvatarNodeParameters: NSObject {
let theme: PresentationTheme?
let accountPeerId: EnginePeer.Id?
let peerId: EnginePeer.Id?
let colors: [UIColor]
let letters: [String]
let font: UIFont
let icon: AvatarNodeIcon
@ -37,10 +39,11 @@ private class AvatarNodeParameters: NSObject {
let hasImage: Bool
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.accountPeerId = accountPeerId
self.peerId = peerId
self.colors = colors
self.letters = letters
self.font = font
self.icon = icon
@ -52,18 +55,71 @@ private class AvatarNodeParameters: NSObject {
}
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 = [
UIColor(rgb: 0xb1b1b1).cgColor, UIColor(rgb: 0xcdcdcd).cgColor
private let grayscaleColors: [UIColor] = [
UIColor(rgb: 0xb1b1b1), UIColor(rgb: 0xcdcdcd)
]
private let savedMessagesColors: NSArray = [
UIColor(rgb: 0x2a9ef1).cgColor, UIColor(rgb: 0x72d5fd).cgColor
private let savedMessagesColors: [UIColor] = [
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 {
case phone
}
@ -158,21 +214,21 @@ public final class AvatarEditOverlayNode: ASDisplayNode {
}
public final class AvatarNode: ASDisplayNode {
public static let gradientColors: [NSArray] = [
[UIColor(rgb: 0xff516a).cgColor, UIColor(rgb: 0xff885e).cgColor],
[UIColor(rgb: 0xffa85c).cgColor, UIColor(rgb: 0xffcd6a).cgColor],
[UIColor(rgb: 0x665fff).cgColor, UIColor(rgb: 0x82b1ff).cgColor],
[UIColor(rgb: 0x54cb68).cgColor, UIColor(rgb: 0xa0de7e).cgColor],
[UIColor(rgb: 0x4acccd).cgColor, UIColor(rgb: 0x00fcfd).cgColor],
[UIColor(rgb: 0x2a9ef1).cgColor, UIColor(rgb: 0x72d5fd).cgColor],
[UIColor(rgb: 0xd669ed).cgColor, UIColor(rgb: 0xe0a2f3).cgColor],
public static let gradientColors: [[UIColor]] = [
[UIColor(rgb: 0xff516a), UIColor(rgb: 0xff885e)],
[UIColor(rgb: 0xffa85c), UIColor(rgb: 0xffcd6a)],
[UIColor(rgb: 0x665fff), UIColor(rgb: 0x82b1ff)],
[UIColor(rgb: 0x54cb68), UIColor(rgb: 0xa0de7e)],
[UIColor(rgb: 0x4acccd), UIColor(rgb: 0x00fcfd)],
[UIColor(rgb: 0x2a9ef1), UIColor(rgb: 0x72d5fd)],
[UIColor(rgb: 0xd669ed), UIColor(rgb: 0xe0a2f3)],
]
public var font: UIFont {
didSet {
if oldValue.pointSize != font.pointSize {
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 {
@ -193,6 +249,29 @@ public final class AvatarNode: ASDisplayNode {
private var state: AvatarNodeState = .empty
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)
public var ready: Signal<Void, NoError> {
@ -217,6 +296,22 @@ public final class AvatarNode: ASDisplayNode {
self.imageNode.isLayerBacked = true
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() {
@ -365,7 +460,7 @@ public final class AvatarNode: ASDisplayNode {
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 {
self.imageReady.set(.single(true))
self.displaySuspended = false
@ -374,7 +469,18 @@ public final class AvatarNode: ASDisplayNode {
}
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 {
self.parameters = parameters
@ -400,9 +506,9 @@ public final class AvatarNode: ASDisplayNode {
let parameters: AvatarNodeParameters
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 {
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
@ -433,8 +539,10 @@ public final class AvatarNode: ASDisplayNode {
context.fill(bounds)
}
let colorIndex: Int
let colors: [UIColor]
if let parameters = parameters as? AvatarNodeParameters {
colors = parameters.colors
if case .round = parameters.clipStyle {
context.beginPath()
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.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 {
colorIndex = -1
colors = grayscaleColors
}
let colorsArray: NSArray
let colorsArray: NSArray = colors.map(\.cgColor) as NSArray
var iconColor = UIColor.white
if let parameters = parameters as? AvatarNodeParameters, parameters.icon != .none {
if case .deletedIcon = parameters.icon {
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 case let .archivedChatsIcon(hiddenByDefault) = parameters.icon, let theme = parameters.theme {
if hiddenByDefault {
iconColor = theme.chatList.unpinnedArchiveAvatarColor.foregroundColor
backgroundColors = theme.chatList.unpinnedArchiveAvatarColor.backgroundColors.colors
} else {
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]
@ -636,9 +706,9 @@ public func drawPeerAvatarLetters(context: CGContext, size: CGSize, round: Bool
let colorsArray: NSArray
if colorIndex == -1 {
colorsArray = grayscaleColors
colorsArray = grayscaleColors.map(\.cgColor) as NSArray
} 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]
@ -707,9 +777,9 @@ public func generateAvatarImage(size: CGSize, icon: UIImage?, iconScale: CGFloat
let colorsArray: NSArray
if colorIndex == -1 {
colorsArray = grayscaleColors
colorsArray = grayscaleColors.map(\.cgColor) as NSArray
} 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]
@ -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
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 {
if let action = media as? TelegramMediaAction, action.action == .peerJoined {
joined = true
@ -1242,7 +1242,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
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)
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 {
case .chatList:
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()
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] = []

View File

@ -749,7 +749,25 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
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):
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
}
switch item.content {
case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
if let peer = peer.peer, let message = messages.first {
case let .peer(peerData):
if let peer = peerData.peer.peer, let message = peerData.messages.first {
peerContextAction(peer, .search(message.id), node, gesture, location)
}
case .groupReference:
@ -2935,8 +2953,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
bounds = selectedItemNode.bounds
}
switch item.content {
case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
return (selectedItemNode.view, bounds, messages.last?.id ?? peer.peerId)
case let .peer(peerData):
return (selectedItemNode.view, bounds, peerData.messages.last?.id ?? peerData.peer.peerId)
case let .groupReference(groupId, _, _, _, _):
return (selectedItemNode.view, bounds, groupId)
}
@ -3129,7 +3147,25 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
associatedThreadInfo: nil
)
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:
return nil
case .links:

View File

@ -64,16 +64,74 @@ public enum ChatListItemContent {
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)
public var chatLocation: ChatLocation? {
switch self {
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
return .peer(id: peer.peerId)
case .groupReference:
return nil
case let .peer(peerData):
return .peer(id: peerData.peer.peerId)
case .groupReference:
return nil
}
}
}
@ -178,23 +236,23 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour {
public func selected(listView: ListView) {
switch self.content {
case let .peer(messages, peer, _, _, _, _, _, _, _, _, promoInfo, _, _, _, _, _):
if let message = messages.last, let peer = peer.peer {
var threadId: Int64?
if case let .forum(_, _, threadIdValue, _, _) = self.index {
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 .peer(peerData):
if let message = peerData.messages.last, let peer = peerData.peer.peer {
var threadId: Int64?
if case let .forum(_, _, threadIdValue, _, _) = self.index {
threadId = threadIdValue
}
case let .groupReference(groupId, _, _, _, _):
self.interaction.groupSelected(groupId)
if threadId == nil, self.interaction.searchTextHighightState != nil, case let .channel(channel) = peerData.peer.peer, channel.flags.contains(.isForum) {
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 avatarBadgeBackground: ASImageNode?
let onlineNode: PeerOnlineMarkerNode
var avatarTimerBadge: AvatarBadgeView?
let pinnedIconNode: ASImageNode
var secretIconNode: ASImageNode?
var credibilityIconView: ComponentHostView<Empty>?
@ -905,8 +964,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
result += "\n\(item.presentationData.strings.VoiceOver_Chat_UnreadMessages(Int32(allCount)))"
}
return result
case let .peer(_, peer, _, combinedReadState, _, _, _, _, _, _, _, _, _, _, _, _):
guard let chatMainPeer = peer.chatMainPeer else {
case let .peer(peerData):
guard let chatMainPeer = peerData.peer.chatMainPeer else {
return nil
}
var result = ""
@ -915,7 +974,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else {
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))"
}
return result
@ -965,19 +1024,19 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else {
return item.presentationData.strings.VoiceOver_ChatList_MessageEmpty
}
case let .peer(messages, peer, _, combinedReadState, _, _, _, _, _, _, _, _, _, _, _, _):
if let message = messages.last {
case let .peer(peerData):
if let message = peerData.messages.last {
var result = ""
if message.flags.contains(.Incoming) {
result += item.presentationData.strings.VoiceOver_ChatList_Message
} else {
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 {
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\(messageText)"
@ -1165,7 +1224,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
var threadId: Int64?
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
}
}
@ -1193,14 +1252,14 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var displayAsMessage = false
var enablePreview = true
switch item.content {
case let .peer(messages, peerValue, _, _, _, _, _, _, _, _, _, _, displayAsMessageValue, _, _, _):
displayAsMessage = displayAsMessageValue
if displayAsMessage, case let .user(author) = messages.last?.author {
case let .peer(peerData):
displayAsMessage = peerData.displayAsMessage
if displayAsMessage, case let .user(author) = peerData.messages.last?.author {
peer = .user(author)
} 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
}
case let .groupReference(_, _, _, _, hiddenByDefault):
@ -1371,8 +1430,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
guard let item = self.item, item.editing else {
return
}
if case let .peer(_, peer, _, _, _, _, _, _, _, _, promoInfo, _, _, _, _, _) = item.content {
if promoInfo == nil, let mainPeer = peer.peer {
if case let .peer(peerData) = item.content {
if peerData.promoInfo == nil, let mainPeer = peerData.peer.peer {
switch item.index {
case let .forum(_, _, threadIdValue, _, _):
item.interaction.toggleThreadsSelection([threadIdValue], !item.selected)
@ -1429,12 +1488,30 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var threadInfo: ChatListItemContent.ThreadInfo?
var forumTopicData: EngineChatList.ForumTopicData?
var topForumTopicItems: [EngineChatList.ForumTopicData] = []
topForumTopicItems.removeAll()
var autoremoveTimeout: Int32?
var groupHiddenByDefault = false
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
contentPeer = .chat(peerValue)
combinedReadState = combinedReadStateValue
@ -1582,6 +1659,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
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 avatarTimerBadgeDiameter: CGFloat = floor(floor(item.presentationData.fontSize.itemListBaseFontSize * 24.0 / 17.0))
let currentAvatarBadgeCleanBackgroundImage: UIImage? = PresentationResourcesChatList.badgeBackgroundBorder(item.presentationData.theme, diameter: avatarBadgeDiameter + 4.0)
@ -1905,8 +1983,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
switch item.content {
case let .groupReference(_, _, message, _, _):
topIndex = message?.index
case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
topIndex = messages.first?.index
case let .peer(peerData):
topIndex = peerData.messages.first?.index
}
if let topIndex {
var t = Int(topIndex.timestamp)
@ -2073,8 +2151,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
if !isPeerGroup && !isAccountPeer && threadInfo == nil {
if displayAsMessage {
switch item.content {
case let .peer(messages, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
if let peer = messages.last?.author {
case let .peer(peerData):
if let peer = peerData.messages.last?.author {
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))
} else if peer.isScam {
@ -2258,7 +2336,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var peerRevealOptions: [ItemListRevealOption]
var peerLeftRevealOptions: [ItemListRevealOption]
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 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)
@ -2676,6 +2758,37 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .regular, voiceChat: onlineIsVoiceChat)
}
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 _ = dateApply()
@ -3194,10 +3307,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
guard let item else {
return
}
guard case let .peer(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, topForumTopicItems) = item.content else {
guard case let .peer(peerData) = item.content else {
return
}
guard let topicItem = topForumTopicItems.first else {
guard let topicItem = peerData.topForumTopicItems.first else {
return
}
guard case let .chatList(index) = item.index else {
@ -3522,7 +3635,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
close = false
case RevealOptionKey.delete.rawValue:
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 {
if let action = media as? TelegramMediaAction, action.action == .peerJoined {
joined = true
@ -3558,8 +3671,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
item.interaction.toggleArchivedFolderHiddenByDefault()
close = false
case RevealOptionKey.hidePsa.rawValue:
if let item = self.item, case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = item.content {
item.interaction.hidePsa(peer.peerId)
if let item = self.item, case let .peer(peerData) = item.content {
item.interaction.hidePsa(peerData.peer.peerId)
}
close = false
self.skipFadeout = true

View File

@ -345,7 +345,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
chatListLocation: location,
filterData: filterData,
index: index,
content: .peer(
content: .peer(ChatListItemContent.PeerData(
messages: peerEntry.messages,
peer: peer,
threadInfo: threadInfo,
@ -361,8 +361,9 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
displayAsMessage: false,
hasFailedMessages: hasFailedMessages,
forumTopicData: forumTopicData,
topForumTopicItems: topForumTopicItems
),
topForumTopicItems: topForumTopicItems,
autoremoveTimeout: peerEntry.autoremoveTimeout
)),
editing: editing,
hasActiveRevealControls: hasActiveRevealControls,
selected: selected,
@ -601,7 +602,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
chatListLocation: location,
filterData: filterData,
index: index,
content: .peer(
content: .peer(ChatListItemContent.PeerData(
messages: peerEntry.messages,
peer: peer,
threadInfo: threadInfo,
@ -617,8 +618,9 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
displayAsMessage: false,
hasFailedMessages: hasFailedMessages,
forumTopicData: forumTopicData,
topForumTopicItems: topForumTopicItems
),
topForumTopicItems: topForumTopicItems,
autoremoveTimeout: peerEntry.autoremoveTimeout
)),
editing: editing,
hasActiveRevealControls: hasActiveRevealControls,
selected: selected,
@ -2084,7 +2086,7 @@ public final class ChatListNode: ListView {
var isHiddenItemVisible = false
strongSelf.forEachItemNode({ itemNode in
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 {
isHiddenItemVisible = true
}
@ -2368,8 +2370,8 @@ public final class ChatListNode: ListView {
for item in transition.insertItems {
if let item = item.item as? ChatListItem {
switch item.content {
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
insertedPeerIds.append(peer.peerId)
case let .peer(peerData):
insertedPeerIds.append(peerData.peer.peerId)
case .groupReference:
break
}
@ -2754,8 +2756,8 @@ public final class ChatListNode: ListView {
if resultPeer == nil, let itemNode = itemNode as? ListViewItemNode, itemNode.frame.contains(point) {
if let itemNode = itemNode as? ChatListItemNode, let item = itemNode.item {
switch item.content {
case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
resultPeer = peer.peer
case let .peer(peerData):
resultPeer = peerData.peer.peer
default:
break
}
@ -2771,8 +2773,8 @@ public final class ChatListNode: ListView {
if resultThreadId == nil, let itemNode = itemNode as? ListViewItemNode, itemNode.frame.contains(point) {
if let itemNode = itemNode as? ChatListItemNode, let item = itemNode.item {
switch item.content {
case let .peer(_, _, threadInfo, _, _, _, _, _, _, _, _, _, _, _, _, _):
resultThreadId = threadInfo?.id
case let .peer(peerData):
resultThreadId = peerData.threadInfo?.id
default:
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) {
if enableHasImage {
self.hasImage = ValuePromise(false, ignoreRepeated: true)
@ -187,8 +189,10 @@ public class ImageNode: ASDisplayNode {
} else {
strongSelf.contents = image
}
strongSelf.contentUpdated?(next)
} else if strongSelf.enableEmpty {
strongSelf.contents = nil
strongSelf.contentUpdated?(nil)
}
if !reportedHasImage {
if let hasImage = strongSelf.hasImage {
@ -210,6 +214,7 @@ public class ImageNode: ASDisplayNode {
self.contents = nil
self.disposable.set(nil)
self.contentUpdated?(nil)
}
public var image: UIImage? {

View File

@ -76,7 +76,7 @@ class GlobalAutoremoveHeaderItemNode: ListViewItemNode {
func asyncLayout() -> (_ item: GlobalAutoremoveHeaderItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
return { item, params, neighbors in
//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 insets = itemListNeighborsGroupedInsets(neighbors, params)
@ -86,12 +86,12 @@ class GlobalAutoremoveHeaderItemNode: ListViewItemNode {
return (layout, { [weak self] in
if let strongSelf = self {
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.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.updateLayout(size: iconSize)
}

View File

@ -267,7 +267,7 @@ public func globalAutoremoveScreen(context: AccountContext, initialValue: Int32,
presentControllerImpl?(standardTextAlertController(
theme: AlertControllerTheme(presentationData: presentationData),
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: [
TextAlertAction(type: .defaultAction, title: "Enable Auto-Deletion", action: {
apply(timeout)

View File

@ -242,7 +242,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
chatListLocation: .chatList(groupId: .root),
filterData: nil,
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: [
EngineMessage(
stableId: 0,
@ -285,8 +285,9 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
displayAsMessage: false,
hasFailedMessages: false,
forumTopicData: nil,
topForumTopicItems: []
),
topForumTopicItems: [],
autoremoveTimeout: nil
)),
editing: false,
hasActiveRevealControls: false,
selected: false,

View File

@ -862,7 +862,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
chatListLocation: .chatList(groupId: .root),
filterData: nil,
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: [
EngineMessage(
stableId: 0,
@ -905,8 +905,9 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
displayAsMessage: false,
hasFailedMessages: false,
forumTopicData: nil,
topForumTopicItems: []
),
topForumTopicItems: [],
autoremoveTimeout: nil
)),
editing: false,
hasActiveRevealControls: false,
selected: false,

View File

@ -385,7 +385,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
chatListLocation: .chatList(groupId: .root),
filterData: nil,
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: [
EngineMessage(
stableId: 0,
@ -428,8 +428,9 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
displayAsMessage: false,
hasFailedMessages: false,
forumTopicData: nil,
topForumTopicItems: []
),
topForumTopicItems: [],
autoremoveTimeout: nil
)),
editing: false,
hasActiveRevealControls: false,
selected: false,

View File

@ -336,7 +336,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
}
case .channelMigratedFromGroup, .groupMigratedToChannel:
attributedString = NSAttributedString(string: "", font: titleFont, textColor: primaryTextColor)
case let .messageAutoremoveTimeoutUpdated(timeout, _):
case let .messageAutoremoveTimeoutUpdated(timeout, autoSourcePeerId):
let authorString: String
if let author = messageMainPeer(message) {
authorString = author.compactDisplayTitle
@ -349,37 +349,41 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
if timeout > 0 {
let timeValue = timeIntervalString(strings: strings, value: timeout, preferLowerValue: false)
let string: String
if let _ = messagePeer as? TelegramUser {
if message.author?.id == accountPeerId {
string = strings.Conversation_AutoremoveTimerSetUserYou(timeValue).string
if let user = messagePeer as? TelegramUser {
if let autoSourcePeerId = autoSourcePeerId {
if autoSourcePeerId == accountPeerId {
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 {
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 {
if message.author?.id == accountPeerId {
string = strings.Conversation_AutoremoveTimerSetUserYou(timeValue).string
attributedString = NSAttributedString(string: strings.Conversation_AutoremoveTimerSetUserYou(timeValue).string, font: titleFont, textColor: primaryTextColor)
} 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 {
if message.author?.id == accountPeerId {
string = strings.Conversation_AutoremoveTimerSetUserYou(timeValue).string
attributedString = NSAttributedString(string: strings.Conversation_AutoremoveTimerSetUserYou(timeValue).string, font: titleFont, textColor: primaryTextColor)
} else {
if case .group = channel.info {
string = strings.Conversation_AutoremoveTimerSetGroup(timeValue).string
attributedString = NSAttributedString(string: strings.Conversation_AutoremoveTimerSetGroup(timeValue).string, font: titleFont, textColor: primaryTextColor)
} else {
string = strings.Conversation_AutoremoveTimerSetChannel(timeValue).string
attributedString = NSAttributedString(string: strings.Conversation_AutoremoveTimerSetChannel(timeValue).string, font: titleFont, textColor: primaryTextColor)
}
}
} else {
if message.author?.id == accountPeerId {
string = strings.Notification_MessageLifetimeChangedOutgoing(timeValue).string
attributedString = NSAttributedString(string: strings.Notification_MessageLifetimeChangedOutgoing(timeValue).string, font: titleFont, textColor: primaryTextColor)
} 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 {
let string: String
if let _ = messagePeer as? TelegramUser {

View File

@ -85,7 +85,7 @@ private enum ChatListSearchEntry: Comparable, Identifiable {
chatListLocation: .chatList(groupId: .root),
filterData: nil,
index: .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: nil, messageIndex: message.index)),
content: .peer(
content: .peer(ChatListItemContent.PeerData(
messages: [EngineMessage(message)],
peer: EngineRenderedPeer(peer),
threadInfo: nil,
@ -101,8 +101,9 @@ private enum ChatListSearchEntry: Comparable, Identifiable {
displayAsMessage: true,
hasFailedMessages: false,
forumTopicData: nil,
topForumTopicItems: []
),
topForumTopicItems: [],
autoremoveTimeout: nil
)),
editing: false,
hasActiveRevealControls: false,
selected: false,
@ -247,9 +248,9 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
return
}
switch item.content {
case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
if let message = 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))
case let .peer(peerData):
if let message = peerData.messages.first {
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)
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)