Various improvements

This commit is contained in:
Isaac 2023-12-20 22:17:06 +04:00
parent 46e203137a
commit 6730f7d2fb
25 changed files with 1047 additions and 189 deletions

View File

@ -10773,3 +10773,26 @@ Sorry for the inconvenience.";
"ChatList.PremiumXmasGiftText" = "Gift Telegram Premium for Christmas.";
"ReassignBoost.DescriptionWithLink" = "To boost **%1$@**, reassign a previous boost or [gift Telegram Premium]() to a friend to get **%2$@** additional boosts.";
"Channel.AdminLog.ChannelChangedNameColorAndIcon" = "%1$@ changed the channel name color and icon to %2$@ %3$@";
"Channel.AdminLog.ChannelChangedNameColor" = "%1$@ changed the channel name color to %2$@";
"Channel.AdminLog.ChannelChangedNameIcon" = "%1$@ changed the channel name icon to %2$@";
"Channel.AdminLog.ChannelChangedProfileColorAndIcon" = "%1$@ changed the channel profile color and icon to %2$@ %3$@";
"Channel.AdminLog.ChannelChangedProfileColor" = "%1$@ changed the channel profile color to %2$@";
"Channel.AdminLog.ChannelChangedProfileIcon" = "%1$@ changed the channel profile icon to %2$@";
"Channel.AdminLog.ChannelRemovedProfileColorAndIcon" = "%1$@ changed the removed profile color and icon";
"Channel.AdminLog.ChannelRemovedProfileColor" = "%1$@ removed profile color";
"Channel.AdminLog.ChannelRemovedProfileIcon" = "%1$@ removed profile icon";
"Channel.AdminLog.ChannelUpdatedStatus" = "%1$@ changed the channel status to %2$@";
"Channel.AdminLog.ChannelRemovedStatus" = "%1$@ removed the channel status";
"Channel.AdminLog.ChannelRemovedWallpaper" = "%1$@ removed wallpaper";
"Channel.AdminLog.ChannelChangedWallpaper" = "%1$@ set a new wallpaper";
"Channel.Appearance.UnsavedChangesAlertTitle" = "Unsaved Changes";
"Channel.Appearance.UnsavedChangesAlertText" = "You have changed the channel appearance settings. Apply changes?";
"Channel.Appearance.UnsavedChangesAlertDiscard" = "Discard";
"Channel.Appearance.UnsavedChangesAlertApply" = "Apply";

View File

@ -1335,19 +1335,19 @@ public class PeerNameColors: Equatable {
public let secondary: UIColor?
public let tertiary: UIColor?
init(main: UIColor, secondary: UIColor?, tertiary: UIColor?) {
public init(main: UIColor, secondary: UIColor?, tertiary: UIColor?) {
self.main = main
self.secondary = secondary
self.tertiary = tertiary
}
init(main: UIColor) {
public init(main: UIColor) {
self.main = main
self.secondary = nil
self.tertiary = nil
}
init?(colors: [UIColor]) {
public init?(colors: [UIColor]) {
guard let first = colors.first else {
return nil
}

View File

@ -969,6 +969,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var avatarTimerBadge: AvatarBadgeView?
let pinnedIconNode: ASImageNode
var secretIconNode: ASImageNode?
var verifiedIconView: ComponentHostView<Empty>?
var verifiedIconComponent: EmojiStatusComponent?
var credibilityIconView: ComponentHostView<Empty>?
var credibilityIconComponent: EmojiStatusComponent?
let mutedIconNode: ASImageNode
@ -1152,6 +1154,14 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
self.textNode.visibilityRect = self.visibilityStatus ? CGRect.infinite : nil
if let verifiedIconView = self.verifiedIconView, let verifiedIconComponent = self.verifiedIconComponent {
let _ = verifiedIconView.update(
transition: .immediate,
component: AnyComponent(verifiedIconComponent.withVisibleForAnimations(self.visibilityStatus)),
environment: {},
containerSize: verifiedIconView.bounds.size
)
}
if let credibilityIconView = self.credibilityIconView, let credibilityIconComponent = self.credibilityIconComponent {
let _ = credibilityIconView.update(
transition: .immediate,
@ -1761,6 +1771,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var currentPinnedIconImage: UIImage?
var currentMutedIconImage: UIImage?
var currentCredibilityIconContent: EmojiStatusComponent.Content?
var currentVerifiedIconContent: EmojiStatusComponent.Content?
var currentSecretIconImage: UIImage?
var currentForwardedIcon: UIImage?
var currentStoryIcon: UIImage?
@ -2478,6 +2489,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else if peer.isFake {
currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased())
} else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled {
if case .channel = peer, peer.isVerified {
currentVerifiedIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
}
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.isVerified {
currentCredibilityIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
@ -2494,6 +2509,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else if peer.isFake {
currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased())
} else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled {
if case .channel = peer, peer.isVerified {
currentVerifiedIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
}
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.isVerified {
currentCredibilityIconContent = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
@ -2505,7 +2524,24 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
if let currentSecretIconImage = currentSecretIconImage {
titleIconsWidth += currentSecretIconImage.size.width + 2.0
}
if let currentCredibilityIconContent = currentCredibilityIconContent {
if let currentVerifiedIconContent {
if titleIconsWidth.isZero {
titleIconsWidth += 4.0
} else {
titleIconsWidth += 2.0
}
switch currentVerifiedIconContent {
case let .text(_, string):
let textString = NSAttributedString(string: string, font: Font.bold(10.0), textColor: .black, paragraphAlignment: .center)
let stringRect = textString.boundingRect(with: CGSize(width: 100.0, height: 16.0), options: .usesLineFragmentOrigin, context: nil)
titleIconsWidth += floor(stringRect.width) + 11.0
default:
titleIconsWidth += 8.0
}
}
if let currentCredibilityIconContent {
if titleIconsWidth.isZero {
titleIconsWidth += 4.0
} else {
@ -3556,7 +3592,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
lastLineRect = CGRect(origin: CGPoint(), size: titleLayout.size)
}
if let currentCredibilityIconContent = currentCredibilityIconContent {
if let currentCredibilityIconContent {
let credibilityIconView: ComponentHostView<Empty>
if let current = strongSelf.credibilityIconView {
credibilityIconView = current
@ -3589,6 +3625,39 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
credibilityIconView.removeFromSuperview()
}
if let currentVerifiedIconContent {
let verifiedIconView: ComponentHostView<Empty>
if let current = strongSelf.verifiedIconView {
verifiedIconView = current
} else {
verifiedIconView = ComponentHostView<Empty>()
strongSelf.verifiedIconView = verifiedIconView
strongSelf.mainContentContainerNode.view.addSubview(verifiedIconView)
}
let verifiedIconComponent = EmojiStatusComponent(
context: item.context,
animationCache: item.interaction.animationCache,
animationRenderer: item.interaction.animationRenderer,
content: currentVerifiedIconContent,
isVisibleForAnimations: strongSelf.visibilityStatus && item.context.sharedContext.energyUsageSettings.loopEmoji,
action: nil
)
strongSelf.verifiedIconComponent = verifiedIconComponent
let iconSize = verifiedIconView.update(
transition: .immediate,
component: AnyComponent(verifiedIconComponent),
environment: {},
containerSize: CGSize(width: 20.0, height: 20.0)
)
transition.updateFrame(view: verifiedIconView, frame: CGRect(origin: CGPoint(x: nextTitleIconOrigin, y: floorToScreenPixels(titleFrame.maxY - lastLineRect.height * 0.5 - iconSize.height / 2.0) - UIScreenPixel), size: iconSize))
nextTitleIconOrigin += verifiedIconView.bounds.width + 4.0
} else if let verifiedIconView = strongSelf.verifiedIconView {
strongSelf.verifiedIconView = nil
verifiedIconView.removeFromSuperview()
}
if let currentMutedIconImage = currentMutedIconImage {
strongSelf.mutedIconNode.image = currentMutedIconImage
strongSelf.mutedIconNode.isHidden = false

View File

@ -404,6 +404,8 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
private let titleNode: TextNode
private var credibilityIconView: ComponentHostView<Empty>?
private var credibilityIconComponent: EmojiStatusComponent?
private var verifiedIconView: ComponentHostView<Empty>?
private var verifiedIconComponent: EmojiStatusComponent?
private let statusNode: TextNode
private var statusIconNode: ASImageNode?
private var badgeBackgroundNode: ASImageNode?
@ -464,6 +466,14 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
containerSize: credibilityIconView.bounds.size
)
}
if let verifiedIconView = self.verifiedIconView, let verifiedIconComponent = self.verifiedIconComponent {
let _ = verifiedIconView.update(
transition: .immediate,
component: AnyComponent(verifiedIconComponent.withVisibleForAnimations(self.visibilityStatus)),
environment: {},
containerSize: verifiedIconView.bounds.size
)
}
if let avatarIconView = self.avatarIconView, let avatarIconComponent = self.avatarIconComponent {
let _ = avatarIconView.update(
transition: .immediate,
@ -692,6 +702,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 })
var credibilityIcon: EmojiStatusComponent.Content?
var verifiedIcon: EmojiStatusComponent.Content?
switch item.peer {
case let .peer(peer, _):
if let peer = peer, peer.id != item.context.account.peerId {
@ -700,6 +711,9 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
} else if peer.isFake {
credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased())
} else if let emojiStatus = peer.emojiStatus {
if case .channel = peer, peer.isVerified {
verifiedIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
}
credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2))
} else if peer.isVerified {
credibilityIcon = .verified(fillColor: item.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .compact)
@ -879,7 +893,18 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
}
var additionalTitleInset: CGFloat = 0.0
if let credibilityIcon = credibilityIcon {
if let verifiedIcon {
additionalTitleInset += 3.0
switch verifiedIcon {
case let .text(_, string):
let textString = NSAttributedString(string: string, font: Font.bold(10.0), textColor: .black, paragraphAlignment: .center)
let stringRect = textString.boundingRect(with: CGSize(width: 100.0, height: 16.0), options: .usesLineFragmentOrigin, context: nil)
additionalTitleInset += floor(stringRect.width) + 11.0
default:
additionalTitleInset += 16.0
}
}
if let credibilityIcon {
additionalTitleInset += 3.0
switch credibilityIcon {
case let .text(_, string):
@ -1193,7 +1218,8 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
}
}
if let credibilityIcon = credibilityIcon {
var nextIconX: CGFloat = titleFrame.maxX
if let credibilityIcon {
let animationCache = item.context.animationCache
let animationRenderer = item.context.animationRenderer
@ -1224,12 +1250,53 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
containerSize: CGSize(width: 20.0, height: 20.0)
)
transition.updateFrame(view: credibilityIconView, frame: CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: floorToScreenPixels(titleFrame.midY - iconSize.height / 2.0)), size: iconSize))
nextIconX += 4.0
transition.updateFrame(view: credibilityIconView, frame: CGRect(origin: CGPoint(x: nextIconX, y: floorToScreenPixels(titleFrame.midY - iconSize.height / 2.0)), size: iconSize))
nextIconX += iconSize.width
} else if let credibilityIconView = strongSelf.credibilityIconView {
strongSelf.credibilityIconView = nil
credibilityIconView.removeFromSuperview()
}
if let verifiedIcon {
let animationCache = item.context.animationCache
let animationRenderer = item.context.animationRenderer
let verifiedIconView: ComponentHostView<Empty>
if let current = strongSelf.verifiedIconView {
verifiedIconView = current
} else {
verifiedIconView = ComponentHostView<Empty>()
strongSelf.offsetContainerNode.view.addSubview(verifiedIconView)
strongSelf.verifiedIconView = verifiedIconView
}
let verifiedIconComponent = EmojiStatusComponent(
context: item.context,
animationCache: animationCache,
animationRenderer: animationRenderer,
content: verifiedIcon,
isVisibleForAnimations: strongSelf.visibilityStatus,
action: nil,
emojiFileUpdated: nil
)
strongSelf.verifiedIconComponent = verifiedIconComponent
let iconSize = verifiedIconView.update(
transition: .immediate,
component: AnyComponent(verifiedIconComponent),
environment: {},
containerSize: CGSize(width: 20.0, height: 20.0)
)
nextIconX += 4.0
transition.updateFrame(view: verifiedIconView, frame: CGRect(origin: CGPoint(x: nextIconX, y: floorToScreenPixels(titleFrame.midY - iconSize.height / 2.0)), size: iconSize))
nextIconX += iconSize.width
} else if let verifiedIconView = strongSelf.verifiedIconView {
strongSelf.verifiedIconView = nil
verifiedIconView.removeFromSuperview()
}
if let actionButtons = actionButtons {
if strongSelf.actionButtonNodes == nil {
var actionButtonNodes: [HighlightableButtonNode] = []
@ -1430,11 +1497,19 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
self.statusNode.frame = statusFrame
transition.animatePositionAdditive(node: self.statusNode, offset: CGPoint(x: previousStatusFrame.minX - statusFrame.minX, y: 0))
var nextIconX = titleFrame.maxX
if let credibilityIconView = self.credibilityIconView {
var iconFrame = credibilityIconView.frame
iconFrame.origin.x = titleFrame.maxX + 4.0
iconFrame.origin.x = nextIconX + 4.0
nextIconX += 4.0 + iconFrame.width
transition.updateFrame(view: credibilityIconView, frame: iconFrame)
}
if let verifiedIconView = self.verifiedIconView {
var iconFrame = verifiedIconView.frame
iconFrame.origin.x = nextIconX + 4.0
nextIconX += 4.0 + iconFrame.width
transition.updateFrame(view: verifiedIconView, frame: iconFrame)
}
if let badgeBackgroundNode = self.badgeBackgroundNode, let badgeTextNode = self.badgeTextNode {
var badgeBackgroundFrame = badgeBackgroundNode.frame

View File

@ -858,10 +858,10 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
let previewText = groupLayouts.count > 1 ? presentationData.strings.Attachment_MessagesPreview : presentationData.strings.Attachment_MessagePreview
let previewMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: previewText, entities: []))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
let previewMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: previewText, entities: [], additionalAttributes: nil))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
let previewItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [previewMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: true)
let dragMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: presentationData.strings.Attachment_DragToReorder, entities: []))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
let dragMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: presentationData.strings.Attachment_DragToReorder, entities: [], additionalAttributes: nil))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
let dragItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [dragMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: true)
let headerItems: [ListViewItem] = [previewItem, dragItem]

View File

@ -1534,9 +1534,25 @@ final class HistoryViewLoadedState {
author = updatedAuthor
rebuild = true
}
var associatedMessages = message.associatedMessages
for (id, associatedMessage) in message.associatedMessages {
var peers = associatedMessage.peers
var author = associatedMessage.author
for (peerId, _) in associatedMessage.peers {
if let updatedPeer = updatedPeers[peerId] {
peers[peerId] = updatedPeer
rebuild = true
}
}
if let authorValue = author, let updatedAuthor = updatedPeers[authorValue.id] {
author = updatedAuthor
rebuild = true
}
associatedMessages[id] = associatedMessage.withUpdatedPeers(peers).withUpdatedAuthor(author)
}
if rebuild {
let updatedMessage = message.withUpdatedPeers(peers).withUpdatedAuthor(author)
let updatedMessage = message.withUpdatedPeers(peers).withUpdatedAuthor(author).withUpdatedAssociatedMessages(associatedMessages)
return .MessageEntry(MessageHistoryMessageEntry(message: updatedMessage, location: value.location, monthLocation: value.monthLocation, attributes: value.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers)
}
case .IntermediateMessageEntry:

View File

@ -49,7 +49,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
case .messageActionScreenshotTaken:
return TelegramMediaAction(action: .historyScreenshot)
case let .messageActionCustomAction(message):
return TelegramMediaAction(action: .customText(text: message, entities: []))
return TelegramMediaAction(action: .customText(text: message, entities: [], additionalAttributes: nil))
case let .messageActionBotAllowed(flags, domain, app):
if let domain = domain {
return TelegramMediaAction(action: .botDomainAccessGranted(domain: domain))

View File

@ -1,4 +1,5 @@
import Postbox
import Foundation
public enum PhoneCallDiscardReason: Int32 {
case missed = 0
@ -73,6 +74,18 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
}
}
public struct CustomTextAttributes: Equatable {
public var attributes: [(NSRange, NSAttributedString.Key, Any)]
public init(attributes: [(NSRange, NSAttributedString.Key, Any)]) {
self.attributes = attributes
}
public static func ==(lhs: CustomTextAttributes, rhs: CustomTextAttributes) -> Bool {
return true
}
}
case unknown
case groupCreated(title: String)
case addedMembers(peerIds: [PeerId])
@ -89,7 +102,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
case gameScore(gameId: Int64, score: Int32)
case phoneCall(callId: Int64, discardReason: PhoneCallDiscardReason?, duration: Int32?, isVideo: Bool)
case paymentSent(currency: String, totalAmount: Int64, invoiceSlug: String?, isRecurringInit: Bool, isRecurringUsed: Bool)
case customText(text: String, entities: [MessageTextEntity])
case customText(text: String, entities: [MessageTextEntity], additionalAttributes: CustomTextAttributes?)
case botDomainAccessGranted(domain: String)
case botAppAccessGranted(appName: String?, type: BotSendMessageAccessGrantedType?)
case botSentSecureValues(types: [SentSecureValueType])
@ -152,7 +165,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
case 15:
self = .paymentSent(currency: decoder.decodeStringForKey("currency", orElse: ""), totalAmount: decoder.decodeInt64ForKey("ta", orElse: 0), invoiceSlug: decoder.decodeOptionalStringForKey("invoiceSlug"), isRecurringInit: decoder.decodeBoolForKey("isRecurringInit", orElse: false), isRecurringUsed: decoder.decodeBoolForKey("isRecurringUsed", orElse: false))
case 16:
self = .customText(text: decoder.decodeStringForKey("text", orElse: ""), entities: decoder.decodeObjectArrayWithDecoderForKey("ent"))
self = .customText(text: decoder.decodeStringForKey("text", orElse: ""), entities: decoder.decodeObjectArrayWithDecoderForKey("ent"), additionalAttributes: nil)
case 17:
self = .botDomainAccessGranted(domain: decoder.decodeStringForKey("do", orElse: ""))
case 18:
@ -298,7 +311,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
encoder.encodeNil(forKey: "d")
}
encoder.encodeInt32(isVideo ? 1 : 0, forKey: "vc")
case let .customText(text, entities):
case let .customText(text, entities, _):
encoder.encodeInt32(16, forKey: "_rawValue")
encoder.encodeString(text, forKey: "text")
encoder.encodeObjectArray(entities, forKey: "ent")

View File

@ -87,7 +87,10 @@ public enum AdminLogEventAction {
case pinTopic(prevInfo: EngineMessageHistoryThread.Info?, newInfo: EngineMessageHistoryThread.Info?)
case toggleForum(isForum: Bool)
case toggleAntiSpam(isEnabled: Bool)
case changeNameColor(prev: PeerNameColor, new: PeerNameColor)
case changeNameColor(prevColor: PeerNameColor, prevIcon: Int64?, newColor: PeerNameColor, newIcon: Int64?)
case changeProfileColor(prevColor: PeerNameColor?, prevIcon: Int64?, newColor: PeerNameColor?, newIcon: Int64?)
case changeWallpaper(prev: TelegramWallpaper?, new: TelegramWallpaper?)
case changeStatus(prev: PeerEmojiStatus?, new: PeerEmojiStatus?)
}
public enum ChannelAdminLogEventError {
@ -348,22 +351,69 @@ func channelAdminLogEvents(accountPeerId: PeerId, postbox: Postbox, network: Net
case let .channelAdminLogEventActionToggleAntiSpam(newValue):
action = .toggleAntiSpam(isEnabled: newValue == .boolTrue)
case let .channelAdminLogEventActionChangePeerColor(prevValue, newValue):
let _ = prevValue
let _ = newValue
action = nil
// action = .changeNameColor(prev: PeerNameColor(rawValue: prevValue), new: PeerNameColor(rawValue: newValue))
var prevColorIndex: Int32
var prevEmojiId: Int64?
switch prevValue {
case let .peerColor(_, color, backgroundEmojiIdValue):
prevColorIndex = color ?? 0
prevEmojiId = backgroundEmojiIdValue
}
var newColorIndex: Int32
var newEmojiId: Int64?
switch newValue {
case let .peerColor(_, color, backgroundEmojiIdValue):
newColorIndex = color ?? 0
newEmojiId = backgroundEmojiIdValue
}
action = .changeNameColor(prevColor: PeerNameColor(rawValue: prevColorIndex), prevIcon: prevEmojiId, newColor: PeerNameColor(rawValue: newColorIndex), newIcon: newEmojiId)
case let .channelAdminLogEventActionChangeProfilePeerColor(prevValue, newValue):
let _ = prevValue
let _ = newValue
action = nil
var prevColorIndex: Int32?
var prevEmojiId: Int64?
switch prevValue {
case let .peerColor(_, color, backgroundEmojiIdValue):
prevColorIndex = color
prevEmojiId = backgroundEmojiIdValue
}
var newColorIndex: Int32?
var newEmojiId: Int64?
switch newValue {
case let .peerColor(_, color, backgroundEmojiIdValue):
newColorIndex = color
newEmojiId = backgroundEmojiIdValue
}
action = .changeProfileColor(prevColor: prevColorIndex.flatMap(PeerNameColor.init(rawValue:)), prevIcon: prevEmojiId, newColor: newColorIndex.flatMap(PeerNameColor.init(rawValue:)), newIcon: newEmojiId)
case let .channelAdminLogEventActionChangeWallpaper(prevValue, newValue):
let _ = prevValue
let _ = newValue
action = nil
let prev: TelegramWallpaper?
if case let .wallPaperNoFile(_, _, settings) = prevValue {
if settings == nil {
prev = nil
} else if case let .wallPaperSettings(flags, _, _, _, _, _, _, _) = settings, flags == 0 {
prev = nil
} else {
prev = TelegramWallpaper(apiWallpaper: prevValue)
}
} else {
prev = TelegramWallpaper(apiWallpaper: prevValue)
}
let new: TelegramWallpaper?
if case let .wallPaperNoFile(_, _, settings) = newValue {
if settings == nil {
new = nil
} else if case let .wallPaperSettings(flags, _, _, _, _, _, _, _) = settings, flags == 0 {
new = nil
} else {
new = TelegramWallpaper(apiWallpaper: newValue)
}
} else {
new = TelegramWallpaper(apiWallpaper: newValue)
}
action = .changeWallpaper(prev: prev, new: new)
case let .channelAdminLogEventActionChangeEmojiStatus(prevValue, newValue):
let _ = prevValue
let _ = newValue
action = nil
action = .changeStatus(prev: PeerEmojiStatus(apiStatus: prevValue), new: PeerEmojiStatus(apiStatus: newValue))
}
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
if let action = action {

View File

@ -718,6 +718,14 @@ public extension TelegramEngine {
return _internal_updatePeerNameColorAndEmoji(account: self.account, peerId: peerId, nameColor: nameColor, backgroundEmojiId: backgroundEmojiId, profileColor: profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId)
}
public func updatePeerNameColor(peerId: EnginePeer.Id, nameColor: PeerNameColor, backgroundEmojiId: Int64?) -> Signal<Void, UpdatePeerNameColorAndEmojiError> {
return _internal_updatePeerNameColor(account: self.account, peerId: peerId, nameColor: nameColor, backgroundEmojiId: backgroundEmojiId)
}
public func updatePeerProfileColor(peerId: EnginePeer.Id, profileColor: PeerNameColor?, profileBackgroundEmojiId: Int64?) -> Signal<Void, UpdatePeerNameColorAndEmojiError> {
return _internal_updatePeerProfileColor(account: self.account, peerId: peerId, profileColor: profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId)
}
public func updatePeerEmojiStatus(peerId: EnginePeer.Id, fileId: Int64?, expirationDate: Int32?) -> Signal<Never, UpdatePeerEmojiStatusError> {
return _internal_updatePeerEmojiStatus(account: self.account, peerId: peerId, fileId: fileId, expirationDate: expirationDate)
}

View File

@ -98,15 +98,21 @@ func _internal_updatePeerNameColorAndEmoji(account: Account, peerId: EnginePeer.
return account.postbox.transaction { transaction -> Signal<Void, UpdatePeerNameColorAndEmojiError> in
if let peer = transaction.getPeer(peerId) {
if let peer = peer as? TelegramChannel, let inputChannel = apiInputChannel(peer) {
let flagsReplies: Int32 = (1 << 0) | (1 << 2)
var flagsReplies: Int32 = (1 << 2)
if backgroundEmojiId != nil {
flagsReplies |= 1 << 0
}
var flagsProfile: Int32 = (1 << 0) | (1 << 1)
var flagsProfile: Int32 = (1 << 1)
if profileBackgroundEmojiId != nil {
flagsProfile |= 1 << 0
}
if profileColor != nil {
flagsProfile |= (1 << 2)
}
return combineLatest(
account.network.request(Api.functions.channels.updateColor(flags: flagsReplies, channel: inputChannel, color: nameColor.rawValue, backgroundEmojiId: backgroundEmojiId ?? 0))
account.network.request(Api.functions.channels.updateColor(flags: flagsReplies, channel: inputChannel, color: nameColor.rawValue, backgroundEmojiId: backgroundEmojiId))
|> map(Optional.init)
|> `catch` { error -> Signal<Api.Updates?, MTRpcError> in
if error.errorDescription.hasPrefix("CHAT_NOT_MODIFIED") {
@ -115,7 +121,7 @@ func _internal_updatePeerNameColorAndEmoji(account: Account, peerId: EnginePeer.
return .fail(error)
}
},
account.network.request(Api.functions.channels.updateColor(flags: flagsProfile, channel: inputChannel, color: profileColor?.rawValue, backgroundEmojiId: profileBackgroundEmojiId ?? 0))
account.network.request(Api.functions.channels.updateColor(flags: flagsProfile, channel: inputChannel, color: profileColor?.rawValue, backgroundEmojiId: profileBackgroundEmojiId))
|> map(Optional.init)
|> `catch` { error -> Signal<Api.Updates?, MTRpcError> in
if error.errorDescription.hasPrefix("CHAT_NOT_MODIFIED") {
@ -162,6 +168,105 @@ func _internal_updatePeerNameColorAndEmoji(account: Account, peerId: EnginePeer.
|> switchToLatest
}
func _internal_updatePeerNameColor(account: Account, peerId: EnginePeer.Id, nameColor: PeerNameColor, backgroundEmojiId: Int64?) -> Signal<Void, UpdatePeerNameColorAndEmojiError> {
return account.postbox.transaction { transaction -> Signal<Void, UpdatePeerNameColorAndEmojiError> in
if let peer = transaction.getPeer(peerId) {
if let peer = peer as? TelegramChannel, let inputChannel = apiInputChannel(peer) {
var flagsReplies: Int32 = (1 << 2)
if backgroundEmojiId != nil {
flagsReplies |= 1 << 0
}
return account.network.request(Api.functions.channels.updateColor(flags: flagsReplies, channel: inputChannel, color: nameColor.rawValue, backgroundEmojiId: backgroundEmojiId))
|> map(Optional.init)
|> `catch` { error -> Signal<Api.Updates?, MTRpcError> in
if error.errorDescription.hasPrefix("CHAT_NOT_MODIFIED") {
return .single(nil)
} else {
return .fail(error)
}
}
|> mapError { error -> UpdatePeerNameColorAndEmojiError in
if error.errorDescription.hasPrefix("BOOSTS_REQUIRED") {
return .channelBoostRequired
}
return .generic
}
|> mapToSignal { repliesResult -> Signal<Void, UpdatePeerNameColorAndEmojiError> in
if let repliesResult = repliesResult {
account.stateManager.addUpdates(repliesResult)
}
return account.postbox.transaction { transaction -> Void in
if let repliesResult = repliesResult, let apiChat = apiUpdatesGroups(repliesResult).first {
let parsedPeers = AccumulatedPeers(transaction: transaction, chats: [apiChat], users: [])
updatePeers(transaction: transaction, accountPeerId: account.peerId, peers: parsedPeers)
}
}
|> mapError { _ -> UpdatePeerNameColorAndEmojiError in }
}
} else {
return .fail(.generic)
}
} else {
return .fail(.generic)
}
}
|> castError(UpdatePeerNameColorAndEmojiError.self)
|> switchToLatest
}
func _internal_updatePeerProfileColor(account: Account, peerId: EnginePeer.Id, profileColor: PeerNameColor?, profileBackgroundEmojiId: Int64?) -> Signal<Void, UpdatePeerNameColorAndEmojiError> {
return account.postbox.transaction { transaction -> Signal<Void, UpdatePeerNameColorAndEmojiError> in
if let peer = transaction.getPeer(peerId) {
if let peer = peer as? TelegramChannel, let inputChannel = apiInputChannel(peer) {
var flagsProfile: Int32 = (1 << 1)
if profileBackgroundEmojiId != nil {
flagsProfile |= 1 << 0
}
if profileColor != nil {
flagsProfile |= (1 << 2)
}
return account.network.request(Api.functions.channels.updateColor(flags: flagsProfile, channel: inputChannel, color: profileColor?.rawValue, backgroundEmojiId: profileBackgroundEmojiId))
|> map(Optional.init)
|> `catch` { error -> Signal<Api.Updates?, MTRpcError> in
if error.errorDescription.hasPrefix("CHAT_NOT_MODIFIED") {
return .single(nil)
} else {
return .fail(error)
}
}
|> mapError { error -> UpdatePeerNameColorAndEmojiError in
if error.errorDescription.hasPrefix("BOOSTS_REQUIRED") {
return .channelBoostRequired
}
return .generic
}
|> mapToSignal { profileResult -> Signal<Void, UpdatePeerNameColorAndEmojiError> in
if let profileResult = profileResult {
account.stateManager.addUpdates(profileResult)
}
return account.postbox.transaction { transaction -> Void in
if let profileResult = profileResult, let apiChat = apiUpdatesGroups(profileResult).first {
let parsedPeers = AccumulatedPeers(transaction: transaction, chats: [apiChat], users: [])
updatePeers(transaction: transaction, accountPeerId: account.peerId, peers: parsedPeers)
}
}
|> mapError { _ -> UpdatePeerNameColorAndEmojiError in }
}
} else {
return .fail(.generic)
}
} else {
return .fail(.generic)
}
}
|> castError(UpdatePeerNameColorAndEmojiError.self)
|> switchToLatest
}
public enum UpdatePeerEmojiStatusError {
case generic
}

View File

@ -625,8 +625,14 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
attributedString = addAttributesToStringWithRanges(titleString._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds))
}
}
case let .customText(text, entities):
attributedString = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false, message: message._asMessage())
case let .customText(text, entities, additionalAttributes):
let mutableAttributedString = NSMutableAttributedString(attributedString: stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false, message: message._asMessage()))
if let additionalAttributes {
for (range, key, value) in additionalAttributes.attributes {
mutableAttributedString.addAttribute(key, value: value, range: range)
}
}
attributedString = mutableAttributedString
case let .botDomainAccessGranted(domain):
attributedString = NSAttributedString(string: strings.AuthSessions_Message(domain).string, font: titleFont, textColor: primaryTextColor)
case let .botAppAccessGranted(appName, type):
@ -733,7 +739,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
case let .topicCreated(title, iconColor, iconFileId):
if forForumOverview {
let maybeFileId = iconFileId ?? 0
attributedString = addAttributesToStringWithRanges(strings.Notification_OverviewTopicCreated(".", title)._tuple, body: bodyAttributes, argumentAttributes: [0: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil, topicInfo: maybeFileId == 0 ? (message.threadId ?? 0, EngineMessageHistoryThread.Info(title: title, icon: nil, iconColor: iconColor)) : nil)])])
attributedString = addAttributesToStringWithRanges(strings.Notification_OverviewTopicCreated(".", title)._tuple, body: bodyAttributes, argumentAttributes: [0: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil, custom: maybeFileId == 0 ? .topic(id: message.threadId ?? 0, info: EngineMessageHistoryThread.Info(title: title, icon: nil, iconColor: iconColor)) : nil)])])
} else {
attributedString = NSAttributedString(string: strings.Notification_ForumTopicCreated, font: titleFont, textColor: primaryTextColor)
}
@ -757,9 +763,9 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
maybeFileId = info.icon ?? 0
}
if isHidden {
attributedString = addAttributesToStringWithRanges(strings.Notification_OverviewTopicHidden(EnginePeer.user(user).displayTitle(strings: strings, displayOrder: nameDisplayOrder), ".", title)._tuple, body: bodyAttributes, argumentAttributes: [0: peerMentionAttributes(primaryTextColor: primaryTextColor, peerId: user.id), 1: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil, topicInfo: maybeFileId == 0 ? (message.threadId ?? 0, EngineMessageHistoryThread.Info(title: title, icon: nil, iconColor: iconColor)) : nil)])])
attributedString = addAttributesToStringWithRanges(strings.Notification_OverviewTopicHidden(EnginePeer.user(user).displayTitle(strings: strings, displayOrder: nameDisplayOrder), ".", title)._tuple, body: bodyAttributes, argumentAttributes: [0: peerMentionAttributes(primaryTextColor: primaryTextColor, peerId: user.id), 1: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil, custom: maybeFileId == 0 ? .topic(id: message.threadId ?? 0, info: EngineMessageHistoryThread.Info(title: title, icon: nil, iconColor: iconColor)) : nil)])])
} else {
attributedString = addAttributesToStringWithRanges(strings.Notification_OverviewTopicUnhidden(EnginePeer.user(user).displayTitle(strings: strings, displayOrder: nameDisplayOrder), ".", title)._tuple, body: bodyAttributes, argumentAttributes: [0: peerMentionAttributes(primaryTextColor: primaryTextColor, peerId: user.id), 1: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil, topicInfo: maybeFileId == 0 ? (message.threadId ?? 0, EngineMessageHistoryThread.Info(title: title, icon: nil, iconColor: iconColor)) : nil)])])
attributedString = addAttributesToStringWithRanges(strings.Notification_OverviewTopicUnhidden(EnginePeer.user(user).displayTitle(strings: strings, displayOrder: nameDisplayOrder), ".", title)._tuple, body: bodyAttributes, argumentAttributes: [0: peerMentionAttributes(primaryTextColor: primaryTextColor, peerId: user.id), 1: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil, custom: maybeFileId == 0 ? .topic(id: message.threadId ?? 0, info: EngineMessageHistoryThread.Info(title: title, icon: nil, iconColor: iconColor)) : nil)])])
}
} else {
if isHidden {
@ -794,9 +800,9 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
maybeFileId = info.icon ?? 0
}
if isClosed {
attributedString = addAttributesToStringWithRanges(strings.Notification_OverviewTopicClosed(EnginePeer.user(user).displayTitle(strings: strings, displayOrder: nameDisplayOrder), ".", title)._tuple, body: bodyAttributes, argumentAttributes: [0: peerMentionAttributes(primaryTextColor: primaryTextColor, peerId: user.id), 1: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil, topicInfo: maybeFileId == 0 ? (message.threadId ?? 0, EngineMessageHistoryThread.Info(title: title, icon: nil, iconColor: iconColor)) : nil)])])
attributedString = addAttributesToStringWithRanges(strings.Notification_OverviewTopicClosed(EnginePeer.user(user).displayTitle(strings: strings, displayOrder: nameDisplayOrder), ".", title)._tuple, body: bodyAttributes, argumentAttributes: [0: peerMentionAttributes(primaryTextColor: primaryTextColor, peerId: user.id), 1: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil, custom: maybeFileId == 0 ? .topic(id: message.threadId ?? 0, info: EngineMessageHistoryThread.Info(title: title, icon: nil, iconColor: iconColor)) : nil)])])
} else {
attributedString = addAttributesToStringWithRanges(strings.Notification_OverviewTopicReopened(EnginePeer.user(user).displayTitle(strings: strings, displayOrder: nameDisplayOrder), ".", title)._tuple, body: bodyAttributes, argumentAttributes: [0: peerMentionAttributes(primaryTextColor: primaryTextColor, peerId: user.id), 1: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil, topicInfo: maybeFileId == 0 ? (message.threadId ?? 0, EngineMessageHistoryThread.Info(title: title, icon: nil, iconColor: iconColor)) : nil)])])
attributedString = addAttributesToStringWithRanges(strings.Notification_OverviewTopicReopened(EnginePeer.user(user).displayTitle(strings: strings, displayOrder: nameDisplayOrder), ".", title)._tuple, body: bodyAttributes, argumentAttributes: [0: peerMentionAttributes(primaryTextColor: primaryTextColor, peerId: user.id), 1: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil, custom: maybeFileId == 0 ? .topic(id: message.threadId ?? 0, info: EngineMessageHistoryThread.Info(title: title, icon: nil, iconColor: iconColor)) : nil)])])
}
} else {
if isClosed {
@ -834,7 +840,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
}
attributedString = addAttributesToStringWithRanges(strings.Notification_ForumTopicRenamedIconChangedAuthor(EnginePeer.user(user).displayTitle(strings: strings, displayOrder: nameDisplayOrder), ".", title)._tuple, body: bodyAttributes, argumentAttributes: [
0: peerMentionAttributes(primaryTextColor: primaryTextColor, peerId: user.id),
1: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil, topicInfo: maybeFileId == 0 ? (message.threadId ?? 0, EngineMessageHistoryThread.Info(title: title, icon: nil, iconColor: iconColor)) : nil)])
1: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil, custom: maybeFileId == 0 ? .topic(id: message.threadId ?? 0, info: EngineMessageHistoryThread.Info(title: title, icon: nil, iconColor: iconColor)) : nil)])
])
} else {
attributedString = NSAttributedString(string: strings.Notification_ForumTopicRenamed(title).string, font: titleFont, textColor: primaryTextColor)
@ -867,9 +873,9 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
title = info.title
}
if case let .user(user) = message.author {
attributedString = addAttributesToStringWithRanges(strings.Notification_ForumTopicIconChangedAuthor(EnginePeer.user(user).displayTitle(strings: strings, displayOrder: nameDisplayOrder), ".")._tuple, body: bodyAttributes, argumentAttributes: [0: peerMentionAttributes(primaryTextColor: primaryTextColor, peerId: user.id), 1: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil, topicInfo: maybeFileId == 0 ? (message.threadId ?? 0, EngineMessageHistoryThread.Info(title: title, icon: nil, iconColor: iconColor)) : nil)])])
attributedString = addAttributesToStringWithRanges(strings.Notification_ForumTopicIconChangedAuthor(EnginePeer.user(user).displayTitle(strings: strings, displayOrder: nameDisplayOrder), ".")._tuple, body: bodyAttributes, argumentAttributes: [0: peerMentionAttributes(primaryTextColor: primaryTextColor, peerId: user.id), 1: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil, custom: maybeFileId == 0 ? .topic(id: message.threadId ?? 0, info: EngineMessageHistoryThread.Info(title: title, icon: nil, iconColor: iconColor)) : nil)])])
} else {
attributedString = addAttributesToStringWithRanges(strings.Notification_ForumTopicIconChanged(".")._tuple, body: bodyAttributes, argumentAttributes: [0: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil, topicInfo: maybeFileId == 0 ? (message.threadId ?? 0, EngineMessageHistoryThread.Info(title: title, icon: nil, iconColor: iconColor)) : nil)])])
attributedString = addAttributesToStringWithRanges(strings.Notification_ForumTopicIconChanged(".")._tuple, body: bodyAttributes, argumentAttributes: [0: MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: maybeFileId, file: nil, custom: maybeFileId == 0 ? .topic(id: message.threadId ?? 0, info: EngineMessageHistoryThread.Info(title: title, icon: nil, iconColor: iconColor)) : nil)])])
}
}
case let .suggestedProfilePhoto(image):

View File

@ -282,7 +282,10 @@ public class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode
}
}
} else {
if item.message.id.peerId.isGroupOrChannel {
if item.associatedData.isRecentActions {
let authorName = item.message.author.flatMap { EnginePeer($0).compactDisplayTitle } ?? ""
text = item.presentationData.strings.Channel_AdminLog_ChannelChangedWallpaper(authorName).string
} else if item.message.id.peerId.isGroupOrChannel {
text = item.presentationData.strings.Notification_ChannelChangedWallpaper
} else {
text = item.presentationData.strings.Notification_ChangedWallpaper(peerName).string

View File

@ -47,6 +47,7 @@ swift_library(
"//submodules/TemporaryCachedPeerDataManager",
"//submodules/UndoUI",
"//submodules/WallpaperBackgroundNode",
"//submodules/TextFormat",
],
visibility = [
"//visibility:public",

View File

@ -9,6 +9,7 @@ import AccountContext
import ChatControllerInteraction
import ChatHistoryEntry
import ChatMessageItemImpl
import TextFormat
enum ChatRecentActionsEntryContentIndex: Int32 {
case header = 0
@ -56,6 +57,22 @@ private func appendAttributedText(text: PresentationStrings.FormattedString, gen
string.append(text.string)
}
private func appendAttributedText(text: PresentationStrings.FormattedString, additionalAttributes: inout [(NSRange, NSAttributedString.Key, Any)], generateEntities: (Int) -> ([MessageTextEntityType], [NSAttributedString.Key: Any]), to string: inout String, entities: inout [MessageTextEntity]) {
let nsString = string as NSString
for rangeItem in text.ranges {
let (types, additionalValues) = generateEntities(rangeItem.index)
for type in types {
entities.append(MessageTextEntity(range: (nsString.length + rangeItem.range.lowerBound) ..< (nsString.length + rangeItem.range.upperBound), type: type))
}
let lowerBound = nsString.length + rangeItem.range.lowerBound
let range = NSRange(location: lowerBound, length: nsString.length + rangeItem.range.upperBound - lowerBound)
for (key, value) in additionalValues {
additionalAttributes.append((range, key, value))
}
}
string.append(text.string)
}
private func appendAttributedText(text: String, withEntities: [MessageTextEntityType], to string: inout String, entities: inout [MessageTextEntity]) {
for type in withEntities {
entities.append(MessageTextEntity(range: string.count ..< (string.count + text.count), type: type))
@ -146,7 +163,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
}
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case .content:
@ -184,7 +201,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
}
let action: TelegramMediaActionType = TelegramMediaActionType.customText(text: text, entities: entities)
let action: TelegramMediaActionType = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case .content:
@ -234,7 +251,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
}
let action: TelegramMediaActionType = TelegramMediaActionType.customText(text: text, entities: entities)
let action: TelegramMediaActionType = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case .content:
@ -318,7 +335,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
}
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case let .toggleSignatures(value):
@ -345,7 +362,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
}
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case let .updatePinned(message):
@ -376,7 +393,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case .content:
@ -415,7 +432,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 0), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -460,7 +477,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -503,7 +520,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -962,7 +979,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
}
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -992,7 +1009,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
}
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1079,7 +1096,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1152,7 +1169,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
}, to: &text, entities: &entities)
}
}
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1179,7 +1196,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: text, attributes: [], media: [mediaMap], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
} else {
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1210,7 +1227,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
}
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1247,7 +1264,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1281,7 +1298,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1310,7 +1327,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1341,7 +1358,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1367,7 +1384,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1393,7 +1410,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1419,7 +1436,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1450,7 +1467,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1481,7 +1498,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1552,7 +1569,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
}, to: &text, entities: &entities)
}
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1583,7 +1600,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1623,7 +1640,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
@ -1651,7 +1668,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
}
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case let .sendMessage(message):
@ -1676,7 +1693,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case .content:
@ -1713,7 +1730,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
}
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case let .deleteTopic(info):
@ -1734,7 +1751,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case let .editTopic(prevInfo, newInfo):
@ -1807,7 +1824,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
}, to: &text, entities: &entities)
}
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case let .pinTopic(prevInfo, newInfo):
@ -1845,7 +1862,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
}, to: &text, entities: &entities)
}
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case let .toggleForum(isForum):
@ -1866,7 +1883,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case let .toggleAntiSpam(isEnabled):
@ -1887,73 +1904,220 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case let .changeNameColor(_, updatedValue):
var peers = SimpleDictionary<PeerId, Peer>()
var author: Peer?
if let peer = self.entry.peers[self.entry.event.peerId] {
author = peer
peers[peer.id] = peer
}
case let .changeNameColor(_, _, updatedColor, updatedIcon):
var peers = SimpleDictionary<PeerId, Peer>()
var author: Peer?
if let peer = self.entry.peers[self.entry.event.peerId] {
author = peer
peers[peer.id] = peer
}
let authorTitle = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""
var text: String = ""
var entities: [MessageTextEntity] = []
let _ = updatedValue
var text: String = ""
var entities: [MessageTextEntity] = []
var additionalAttributes: [(NSRange, NSAttributedString.Key, Any)] = []
let rawText = self.presentationData.strings.Channel_AdminLog_MessageChangedNameColorSet(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", "")
if let updatedIcon {
let rawText = self.presentationData.strings.Channel_AdminLog_ChannelChangedNameColorAndIcon(authorTitle, ".", ".")
appendAttributedText(text: rawText, generateEntities: { index in
let colors = context.peerNameColors.get(updatedColor)
var colorList: [UInt32] = []
colorList.append(colors.main.argb)
if let secondary = colors.secondary {
colorList.append(secondary.argb)
}
if let tertiary = colors.tertiary {
colorList.append(tertiary.argb)
}
appendAttributedText(text: rawText, additionalAttributes: &additionalAttributes, generateEntities: { index in
if index == 0, let author = author {
return ([.TextMention(peerId: author.id)], [:])
} else if index == 1 {
return ([], [
ChatTextInputAttributes.customEmoji: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .nameColors(colorList))
])
} else if index == 2 {
return ([.CustomEmoji(stickerPack: nil, fileId: updatedIcon)], [:])
} else {
return ([], [:])
}
}, to: &text, entities: &entities)
} else {
let rawText = self.presentationData.strings.Channel_AdminLog_ChannelChangedNameColor(authorTitle, ".")
let colors = context.peerNameColors.get(updatedColor)
var colorList: [UInt32] = []
colorList.append(colors.main.argb)
if let secondary = colors.secondary {
colorList.append(secondary.argb)
}
if let tertiary = colors.tertiary {
colorList.append(tertiary.argb)
}
appendAttributedText(text: rawText, additionalAttributes: &additionalAttributes, generateEntities: { index in
if index == 0, let author = author {
return ([.TextMention(peerId: author.id)], [:])
} else if index == 1 {
return ([], [
ChatTextInputAttributes.customEmoji: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .nameColors(colorList))
])
} else {
return ([], [:])
}
}, to: &text, entities: &entities)
}
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: TelegramMediaActionType.CustomTextAttributes(attributes: additionalAttributes))
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case let .changeProfileColor(_, _, updatedColor, updatedIcon):
var peers = SimpleDictionary<PeerId, Peer>()
var author: Peer?
if let peer = self.entry.peers[self.entry.event.peerId] {
author = peer
peers[peer.id] = peer
}
let authorTitle = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""
var text: String = ""
var entities: [MessageTextEntity] = []
var additionalAttributes: [(NSRange, NSAttributedString.Key, Any)] = []
if let updatedColor, let updatedIcon {
let rawText = self.presentationData.strings.Channel_AdminLog_ChannelChangedProfileColorAndIcon(authorTitle, ".", ".")
let colors = context.peerNameColors.get(updatedColor)
var colorList: [UInt32] = []
colorList.append(colors.main.argb)
if let secondary = colors.secondary {
colorList.append(secondary.argb)
}
if let tertiary = colors.tertiary {
colorList.append(tertiary.argb)
}
appendAttributedText(text: rawText, additionalAttributes: &additionalAttributes, generateEntities: { index in
if index == 0, let author = author {
return ([.TextMention(peerId: author.id)], [:])
} else if index == 1 {
return ([], [
ChatTextInputAttributes.customEmoji: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .nameColors(colorList))
])
} else if index == 2 {
return ([.CustomEmoji(stickerPack: nil, fileId: updatedIcon)], [:])
} else {
return ([], [:])
}
}, to: &text, entities: &entities)
} else if let updatedColor {
let rawText = self.presentationData.strings.Channel_AdminLog_ChannelChangedProfileColor(authorTitle, ".")
let colors = context.peerNameColors.get(updatedColor)
var colorList: [UInt32] = []
colorList.append(colors.main.argb)
if let secondary = colors.secondary {
colorList.append(secondary.argb)
}
if let tertiary = colors.tertiary {
colorList.append(tertiary.argb)
}
appendAttributedText(text: rawText, additionalAttributes: &additionalAttributes, generateEntities: { index in
if index == 0, let author = author {
return ([.TextMention(peerId: author.id)], [:])
} else if index == 1 {
return ([], [
ChatTextInputAttributes.customEmoji: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .nameColors(colorList))
])
} else {
return ([], [:])
}
}, to: &text, entities: &entities)
} else {
let rawText = self.presentationData.strings.Channel_AdminLog_ChannelRemovedProfileColorAndIcon(authorTitle)
appendAttributedText(text: rawText, additionalAttributes: &additionalAttributes, generateEntities: { index in
if index == 0, let author = author {
return ([.TextMention(peerId: author.id)], [:])
} else {
return ([], [:])
}
}, to: &text, entities: &entities)
}
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: TelegramMediaActionType.CustomTextAttributes(attributes: additionalAttributes))
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case let .changeStatus(_, status):
var peers = SimpleDictionary<PeerId, Peer>()
var author: Peer?
if let peer = self.entry.peers[self.entry.event.peerId] {
author = peer
peers[peer.id] = peer
}
let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""
var text: String = ""
var entities: [MessageTextEntity] = []
if let status {
appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_ChannelUpdatedStatus(authorTitle, "."), generateEntities: { index in
if index == 0, let author = author {
return [.TextMention(peerId: author.id)]
} else if index == 1 {
return [.Bold]
return [.CustomEmoji(stickerPack: nil, fileId: status.fileId)]
}
return []
}, to: &text, entities: &entities)
let action = TelegramMediaActionType.customText(text: text, entities: entities)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
// case let .changeBackgroundEmojiId(_, updatedValue):
// var peers = SimpleDictionary<PeerId, Peer>()
// var author: Peer?
// if let peer = self.entry.peers[self.entry.event.peerId] {
// author = peer
// peers[peer.id] = peer
// }
//
// var text: String = ""
// var entities: [MessageTextEntity] = []
//
// if let updatedValue, updatedValue != 0 {
// appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedBackgroundEmojiSet(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", "."), generateEntities: { index in
// if index == 0, let author = author {
// return [.TextMention(peerId: author.id)]
// } else if index == 1 {
// return [.CustomEmoji(stickerPack: nil, fileId: updatedValue)]
// }
// return []
// }, to: &text, entities: &entities)
// } else {
// let rawText = self.presentationData.strings.Channel_AdminLog_MessageChangedBackgroundEmojiRemoved(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "")
//
// appendAttributedText(text: rawText, generateEntities: { index in
// if index == 0, let author = author {
// return [.TextMention(peerId: author.id)]
// }
// return []
// }, to: &text, entities: &entities)
// }
//
// let action = TelegramMediaActionType.customText(text: text, entities: entities)
//
// let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
// return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
} else {
appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_ChannelRemovedStatus(authorTitle), generateEntities: { index in
if index == 0, let author = author {
return [.TextMention(peerId: author.id)]
}
return []
}, to: &text, entities: &entities)
}
let action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
case let .changeWallpaper(_, wallpaper):
var peers = SimpleDictionary<PeerId, Peer>()
var author: Peer?
if let peer = self.entry.peers[self.entry.event.peerId] {
author = peer
peers[peer.id] = peer
}
let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""
var text: String = ""
var entities: [MessageTextEntity] = []
let action: TelegramMediaActionType
if let wallpaper {
action = TelegramMediaActionType.setChatWallpaper(wallpaper: wallpaper, forBoth: false)
} else {
appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_ChannelRemovedWallpaper(authorTitle), generateEntities: { index in
if index == 0, let author = author {
return [.TextMention(peerId: author.id)]
}
return []
}, to: &text, entities: &entities)
action = TelegramMediaActionType.customText(text: text, entities: entities, additionalAttributes: nil)
}
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
}
}
}

View File

@ -166,6 +166,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
public let titleLeftIconNode: ASImageNode
public let titleRightIconNode: ASImageNode
public let titleCredibilityIconView: ComponentHostView<Empty>
public let titleVerifiedIconView: ComponentHostView<Empty>
public let activityNode: ChatTitleActivityNode
private let button: HighlightTrackingButtonNode
@ -178,6 +179,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
private var titleLeftIcon: ChatTitleIcon = .none
private var titleRightIcon: ChatTitleIcon = .none
private var titleCredibilityIcon: ChatTitleCredibilityIcon = .none
private var titleVerifiedIcon: ChatTitleCredibilityIcon = .none
private var presenceManager: PeerPresenceStatusManager?
@ -224,6 +226,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
var titleLeftIcon: ChatTitleIcon = .none
var titleRightIcon: ChatTitleIcon = .none
var titleCredibilityIcon: ChatTitleCredibilityIcon = .none
var titleVerifiedIcon: ChatTitleCredibilityIcon = .none
var isEnabled = true
switch titleContent {
case let .peer(peerView, customTitle, _, isScheduledMessages, isMuted, _, isEnabledValue):
@ -258,6 +261,9 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
} else if peer.isScam {
titleCredibilityIcon = .scam
} else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled {
if peer is TelegramChannel, peer.isVerified {
titleVerifiedIcon = .verified
}
titleCredibilityIcon = .emojiStatus(emojiStatus)
} else if peer.isVerified {
titleCredibilityIcon = .verified
@ -381,18 +387,11 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
if titleCredibilityIcon != self.titleCredibilityIcon {
self.titleCredibilityIcon = titleCredibilityIcon
/*switch titleCredibilityIcon {
case .none:
self.titleCredibilityIconNode.image = nil
case .fake:
self.titleCredibilityIconNode.image = PresentationResourcesChatList.fakeIcon(titleTheme, strings: self.strings, type: .regular)
case .scam:
self.titleCredibilityIconNode.image = PresentationResourcesChatList.scamIcon(titleTheme, strings: self.strings, type: .regular)
case .verified:
self.titleCredibilityIconNode.image = PresentationResourcesChatList.verifiedIcon(titleTheme)
case .premium:
self.titleCredibilityIconNode.image = PresentationResourcesChatList.premiumIcon(titleTheme)
}*/
updated = true
}
if titleVerifiedIcon != self.titleVerifiedIcon {
self.titleVerifiedIcon = titleVerifiedIcon
updated = true
}
@ -696,6 +695,9 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
self.titleCredibilityIconView = ComponentHostView()
self.titleCredibilityIconView.isUserInteractionEnabled = false
self.titleVerifiedIconView = ComponentHostView()
self.titleVerifiedIconView.isUserInteractionEnabled = false
self.activityNode = ChatTitleActivityNode()
self.button = HighlightTrackingButtonNode()
@ -721,13 +723,16 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
strongSelf.titleTextNode.layer.removeAnimation(forKey: "opacity")
strongSelf.activityNode.layer.removeAnimation(forKey: "opacity")
strongSelf.titleCredibilityIconView.layer.removeAnimation(forKey: "opacity")
strongSelf.titleVerifiedIconView.layer.removeAnimation(forKey: "opacity")
strongSelf.titleTextNode.alpha = 0.4
strongSelf.activityNode.alpha = 0.4
strongSelf.titleCredibilityIconView.alpha = 0.4
strongSelf.titleVerifiedIconView.alpha = 0.4
} else {
strongSelf.titleTextNode.alpha = 1.0
strongSelf.activityNode.alpha = 1.0
strongSelf.titleCredibilityIconView.alpha = 1.0
strongSelf.titleVerifiedIconView.alpha = 1.0
strongSelf.titleTextNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
strongSelf.activityNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
}
@ -756,6 +761,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
let titleContent = self.titleContent
self.titleCredibilityIcon = .none
self.titleVerifiedIcon = .none
self.titleContent = titleContent
let _ = self.updateStatus()
@ -774,6 +780,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
var leftIconWidth: CGFloat = 0.0
var rightIconWidth: CGFloat = 0.0
var credibilityIconWidth: CGFloat = 0.0
var verifiedIconWidth: CGFloat = 0.0
if let image = self.titleLeftIconNode.image {
if self.titleLeftIconNode.supernode == nil {
@ -800,6 +807,22 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
titleCredibilityContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: self.theme.list.mediaPlaceholderColor, themeColor: self.theme.list.itemAccentColor, loopMode: .count(2))
}
let titleVerifiedContent: EmojiStatusComponent.Content
switch self.titleVerifiedIcon {
case .none:
titleVerifiedContent = .none
case .premium:
titleVerifiedContent = .premium(color: self.theme.list.itemAccentColor)
case .verified:
titleVerifiedContent = .verified(fillColor: self.theme.list.itemCheckColors.fillColor, foregroundColor: self.theme.list.itemCheckColors.foregroundColor, sizeType: .large)
case .fake:
titleVerifiedContent = .text(color: self.theme.chat.message.incoming.scamColor, string: self.strings.Message_FakeAccount.uppercased())
case .scam:
titleVerifiedContent = .text(color: self.theme.chat.message.incoming.scamColor, string: self.strings.Message_ScamAccount.uppercased())
case let .emojiStatus(emojiStatus):
titleVerifiedContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: self.theme.list.mediaPlaceholderColor, themeColor: self.theme.list.itemAccentColor, loopMode: .count(2))
}
let titleCredibilitySize = self.titleCredibilityIconView.update(
transition: .immediate,
component: AnyComponent(EmojiStatusComponent(
@ -814,6 +837,20 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
containerSize: CGSize(width: 20.0, height: 20.0)
)
let titleVerifiedSize = self.titleVerifiedIconView.update(
transition: .immediate,
component: AnyComponent(EmojiStatusComponent(
context: self.context,
animationCache: self.animationCache,
animationRenderer: self.animationRenderer,
content: titleVerifiedContent,
isVisibleForAnimations: true,
action: nil
)),
environment: {},
containerSize: CGSize(width: 20.0, height: 20.0)
)
if self.titleCredibilityIcon != .none {
self.titleTextNode.view.addSubview(self.titleCredibilityIconView)
credibilityIconWidth = titleCredibilitySize.width + 3.0
@ -823,6 +860,15 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
}
}
if self.titleVerifiedIcon != .none {
self.titleTextNode.view.addSubview(self.titleVerifiedIconView)
verifiedIconWidth = titleVerifiedSize.width + 3.0
} else {
if self.titleVerifiedIconView.superview != nil {
self.titleVerifiedIconView.removeFromSuperview()
}
}
if let image = self.titleRightIconNode.image {
if self.titleRightIconNode.supernode == nil {
self.titleTextNode.addSubnode(self.titleRightIconNode)
@ -840,8 +886,9 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
let titleSideInset: CGFloat = 6.0
var titleFrame: CGRect
if size.height > 40.0 {
var titleSize = self.titleTextNode.updateLayout(size: CGSize(width: clearBounds.width - leftIconWidth - credibilityIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height), animated: titleTransition.isAnimated)
var titleSize = self.titleTextNode.updateLayout(size: CGSize(width: clearBounds.width - leftIconWidth - credibilityIconWidth - verifiedIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height), animated: titleTransition.isAnimated)
titleSize.width += credibilityIconWidth
titleSize.width += verifiedIconWidth
let activitySize = self.activityNode.updateLayout(CGSize(width: clearBounds.size.width - titleSideInset * 2.0, height: clearBounds.size.height), alignment: .center)
let titleInfoSpacing: CGFloat = 0.0
@ -874,30 +921,48 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
self.titleLeftIconNode.frame = CGRect(origin: CGPoint(x: -image.size.width - 3.0 - UIScreenPixel, y: 4.0), size: image.size)
}
self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: titleFrame.width - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize)
var nextIconX: CGFloat = titleFrame.width
self.titleVerifiedIconView.frame = CGRect(origin: CGPoint(x: titleFrame.width - titleVerifiedSize.width, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize)
nextIconX -= titleVerifiedSize.width
if !titleVerifiedSize.width.isZero {
nextIconX -= 2.0
}
self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize)
nextIconX -= titleCredibilitySize.width
if let image = self.titleRightIconNode.image {
self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.width + 3.0 + UIScreenPixel, y: 6.0), size: image.size)
}
} else {
let titleSize = self.titleTextNode.updateLayout(size: CGSize(width: floor(clearBounds.width / 2.0 - leftIconWidth - credibilityIconWidth - rightIconWidth - titleSideInset * 2.0), height: size.height), animated: titleTransition.isAnimated)
let titleSize = self.titleTextNode.updateLayout(size: CGSize(width: floor(clearBounds.width / 2.0 - leftIconWidth - credibilityIconWidth - verifiedIconWidth - rightIconWidth - titleSideInset * 2.0), height: size.height), animated: titleTransition.isAnimated)
let activitySize = self.activityNode.updateLayout(CGSize(width: floor(clearBounds.width / 2.0), height: size.height), alignment: .center)
let titleInfoSpacing: CGFloat = 8.0
let combinedWidth = titleSize.width + leftIconWidth + credibilityIconWidth + rightIconWidth + activitySize.width + titleInfoSpacing
let combinedWidth = titleSize.width + leftIconWidth + credibilityIconWidth + verifiedIconWidth + rightIconWidth + activitySize.width + titleInfoSpacing
titleFrame = CGRect(origin: CGPoint(x: leftIconWidth + floor((clearBounds.width - combinedWidth) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
titleTransition.updateFrameAdditiveToCenter(view: self.titleContainerView, frame: titleFrame)
titleTransition.updateFrameAdditiveToCenter(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size))
self.activityNode.frame = CGRect(origin: CGPoint(x: floor((clearBounds.width - combinedWidth) / 2.0 + titleSize.width + leftIconWidth + credibilityIconWidth + rightIconWidth + titleInfoSpacing), y: floor((size.height - activitySize.height) / 2.0)), size: activitySize)
self.activityNode.frame = CGRect(origin: CGPoint(x: floor((clearBounds.width - combinedWidth) / 2.0 + titleSize.width + leftIconWidth + credibilityIconWidth + verifiedIconWidth + rightIconWidth + titleInfoSpacing), y: floor((size.height - activitySize.height) / 2.0)), size: activitySize)
if let image = self.titleLeftIconNode.image {
self.titleLeftIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.minY + 4.0), size: image.size)
}
self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: titleFrame.maxX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize)
var nextIconX: CGFloat = titleFrame.maxX
self.titleVerifiedIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleVerifiedSize.width, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize)
nextIconX -= titleVerifiedSize.width
if !titleVerifiedSize.width.isZero {
nextIconX -= 2.0
}
self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize)
nextIconX -= titleCredibilitySize.width
if let image = self.titleRightIconNode.image {
self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX - image.size.width, y: titleFrame.minY + 6.0), size: image.size)

View File

@ -144,6 +144,58 @@ public func animationCacheFetchFile(postbox: Postbox, userLocation: MediaResourc
}
}
private func generatePeerNameColorImage(nameColor: PeerNameColors.Colors, isDark: Bool, bounds: CGSize = CGSize(width: 40.0, height: 40.0), size: CGSize = CGSize(width: 40.0, height: 40.0)) -> UIImage? {
return generateImage(bounds, rotatedContext: { contextSize, context in
let bounds = CGRect(origin: CGPoint(), size: contextSize)
context.clear(bounds)
let circleBounds = CGRect(origin: CGPoint(x: floorToScreenPixels((bounds.width - size.width) / 2.0), y: floorToScreenPixels((bounds.height - size.height) / 2.0)), size: size)
context.addEllipse(in: circleBounds)
context.clip()
if let secondColor = nameColor.secondary {
var firstColor = nameColor.main
var secondColor = secondColor
if isDark, nameColor.tertiary == nil {
firstColor = secondColor
secondColor = nameColor.main
}
context.setFillColor(secondColor.cgColor)
context.fill(circleBounds)
if let thirdColor = nameColor.tertiary {
context.move(to: CGPoint(x: contextSize.width, y: 0.0))
context.addLine(to: CGPoint(x: contextSize.width, y: contextSize.height))
context.addLine(to: CGPoint(x: 0.0, y: contextSize.height))
context.closePath()
context.setFillColor(firstColor.cgColor)
context.fillPath()
context.setFillColor(thirdColor.cgColor)
context.translateBy(x: contextSize.width / 2.0, y: contextSize.height / 2.0)
context.rotate(by: .pi / 4.0)
let rectSide = size.width / 40.0 * 18.0
let rectCornerRadius = round(size.width / 40.0 * 4.0)
let path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: -rectSide / 2.0, y: -rectSide / 2.0), size: CGSize(width: rectSide, height: rectSide)), cornerRadius: rectCornerRadius)
context.addPath(path.cgPath)
context.fillPath()
} else {
context.move(to: .zero)
context.addLine(to: CGPoint(x: contextSize.width, y: 0.0))
context.addLine(to: CGPoint(x: 0.0, y: contextSize.height))
context.closePath()
context.setFillColor(firstColor.cgColor)
context.fillPath()
}
} else {
context.setFillColor(nameColor.main.cgColor)
context.fill(circleBounds)
}
})
}
public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
public enum Context: Equatable {
public final class Custom: Equatable {
@ -317,8 +369,13 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
super.init()
if let topicInfo = emoji.topicInfo {
self.updateTopicInfo(topicInfo: topicInfo)
if let custom = emoji.custom {
switch custom {
case let .topic(id, info):
self.updateTopicInfo(topicInfo: (id, info))
case let .nameColors(colors):
self.updateNameColors(colors: colors)
}
} else if let file = file {
self.updateFile(file: file, attemptSynchronousLoad: attemptSynchronousLoad)
} else {
@ -467,6 +524,30 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
}
}
private func updateNameColors(colors: [UInt32]) {
if colors.isEmpty {
return
}
let main = UIColor(rgb: colors[0])
var secondary: UIColor?
if colors.count >= 2 {
secondary = UIColor(rgb: colors[1])
}
var tertiary: UIColor?
if colors.count >= 3 {
tertiary = UIColor(rgb: colors[2])
}
let mappedColor = PeerNameColors.Colors(main: main, secondary: secondary, tertiary: tertiary)
let image = generateImage(CGSize(width: 18.0, height: 18.0), contextGenerator: { size, context in
context.clear(CGRect(origin: .zero, size: size))
if let cgImage = generatePeerNameColorImage(nameColor: mappedColor, isDark: false, bounds: CGSize(width: 18.0, height: 18.0), size: CGSize(width: 16.0, height: 16.0))?.cgImage {
context.draw(cgImage, in: CGRect(origin: .zero, size: size))
}
})
self.contents = image?.cgImage
}
private func updateFile(file: TelegramMediaFile, attemptSynchronousLoad: Bool) {
if self.file?.fileId == file.fileId {
return

View File

@ -830,9 +830,9 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
}
}
if peerId == context.account.peerId, case .peer = chatLocation {
/*if peerId == context.account.peerId, case .peer = chatLocation {
availablePanes?.insert(.savedMessagesChats, at: 0)
}
}*/
} else {
availablePanes = nil
}

View File

@ -108,10 +108,17 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let titleNodeContainer: ASDisplayNode
let titleNodeRawContainer: ASDisplayNode
let titleNode: MultiScaleTextNode
let titleCredibilityIconView: ComponentHostView<Empty>
var credibilityIconSize: CGSize?
let titleExpandedCredibilityIconView: ComponentHostView<Empty>
var titleExpandedCredibilityIconSize: CGSize?
let titleVerifiedIconView: ComponentHostView<Empty>
var verifiedIconSize: CGSize?
let titleExpandedVerifiedIconView: ComponentHostView<Empty>
var titleExpandedVerifiedIconSize: CGSize?
let subtitleNodeContainer: ASDisplayNode
let subtitleNodeRawContainer: ASDisplayNode
let subtitleNode: MultiScaleTextNode
@ -189,6 +196,12 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.titleExpandedCredibilityIconView = ComponentHostView<Empty>()
self.titleNode.stateNode(forKey: TitleNodeStateExpanded)?.view.addSubview(self.titleExpandedCredibilityIconView)
self.titleVerifiedIconView = ComponentHostView<Empty>()
self.titleNode.stateNode(forKey: TitleNodeStateRegular)?.view.addSubview(self.titleVerifiedIconView)
self.titleExpandedVerifiedIconView = ComponentHostView<Empty>()
self.titleNode.stateNode(forKey: TitleNodeStateExpanded)?.view.addSubview(self.titleExpandedVerifiedIconView)
self.subtitleNodeContainer = ASDisplayNode()
self.subtitleNodeRawContainer = ASDisplayNode()
self.subtitleNode = MultiScaleTextNode(stateKeys: [TitleNodeStateRegular, TitleNodeStateExpanded])
@ -432,6 +445,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
}
private var currentCredibilityIcon: CredibilityIcon?
private var currentVerifiedIcon: CredibilityIcon?
private var currentPanelStatusData: PeerInfoStatusData?
func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, paneContainerY: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, threadData: MessageHistoryThreadData?, peerNotificationSettings: TelegramPeerNotificationSettings?, threadNotificationSettings: TelegramPeerNotificationSettings?, globalNotificationSettings: EngineGlobalNotificationSettings?, statusData: PeerInfoStatusData?, panelStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?), isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, metrics: LayoutMetrics, deviceMetrics: DeviceMetrics, transition: ContainedViewLayoutTransition, additive: Bool, animateHeader: Bool) -> CGFloat {
@ -471,12 +485,16 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
let credibilityIcon: CredibilityIcon
var verifiedIcon: CredibilityIcon = .none
if let peer = peer {
if peer.isFake {
credibilityIcon = .fake
} else if peer.isScam {
credibilityIcon = .scam
} else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled {
if peer is TelegramChannel, peer.isVerified {
verifiedIcon = .verified
}
credibilityIcon = .emojiStatus(emojiStatus)
} else if peer.isVerified {
credibilityIcon = .verified
@ -714,8 +732,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
emojiExpandedStatusContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 80.0, height: 80.0), placeholderColor: navigationContentsAccentColor, themeColor: navigationContentsAccentColor, loopMode: .forever)
}
//let animateStatusIcon = !self.titleCredibilityIconView.bounds.isEmpty
let iconSize = self.titleCredibilityIconView.update(
transition: Transition(navigationTransition),
component: AnyComponent(EmojiStatusComponent(
@ -796,6 +812,73 @@ final class PeerInfoHeaderNode: ASDisplayNode {
self.titleExpandedCredibilityIconSize = expandedIconSize
}
do {
self.currentVerifiedIcon = verifiedIcon
var currentEmojiStatus: PeerEmojiStatus?
let emojiRegularStatusContent: EmojiStatusComponent.Content
let emojiExpandedStatusContent: EmojiStatusComponent.Content
switch verifiedIcon {
case .none:
emojiRegularStatusContent = .none
emojiExpandedStatusContent = .none
case .premium:
emojiRegularStatusContent = .premium(color: navigationContentsAccentColor)
emojiExpandedStatusContent = .premium(color: navigationContentsAccentColor)
case .verified:
emojiRegularStatusContent = .verified(fillColor: presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: presentationData.theme.list.itemCheckColors.foregroundColor, sizeType: .large)
emojiExpandedStatusContent = .verified(fillColor: navigationContentsAccentColor, foregroundColor: .clear, sizeType: .large)
case .fake:
emojiRegularStatusContent = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_FakeAccount.uppercased())
emojiExpandedStatusContent = emojiRegularStatusContent
case .scam:
emojiRegularStatusContent = .text(color: presentationData.theme.chat.message.incoming.scamColor, string: presentationData.strings.Message_ScamAccount.uppercased())
emojiExpandedStatusContent = emojiRegularStatusContent
case let .emojiStatus(emojiStatus):
currentEmojiStatus = emojiStatus
emojiRegularStatusContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 80.0, height: 80.0), placeholderColor: presentationData.theme.list.mediaPlaceholderColor, themeColor: navigationContentsAccentColor, loopMode: .forever)
emojiExpandedStatusContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 80.0, height: 80.0), placeholderColor: navigationContentsAccentColor, themeColor: navigationContentsAccentColor, loopMode: .forever)
}
let iconSize = self.titleVerifiedIconView.update(
transition: Transition(navigationTransition),
component: AnyComponent(EmojiStatusComponent(
context: self.context,
animationCache: self.animationCache,
animationRenderer: self.animationRenderer,
content: emojiRegularStatusContent,
isVisibleForAnimations: true,
useSharedAnimation: true,
action: nil,
emojiFileUpdated: nil
)),
environment: {},
containerSize: CGSize(width: 34.0, height: 34.0)
)
let expandedIconSize = self.titleExpandedVerifiedIconView.update(
transition: Transition(navigationTransition),
component: AnyComponent(EmojiStatusComponent(
context: self.context,
animationCache: self.animationCache,
animationRenderer: self.animationRenderer,
content: emojiExpandedStatusContent,
isVisibleForAnimations: true,
useSharedAnimation: true,
action: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.displayPremiumIntro?(strongSelf.titleExpandedVerifiedIconView, currentEmojiStatus, strongSelf.emojiStatusFileAndPackTitle.get(), true)
}
)),
environment: {},
containerSize: CGSize(width: 34.0, height: 34.0)
)
self.verifiedIconSize = iconSize
self.titleExpandedVerifiedIconSize = expandedIconSize
}
self.navigationButtonContainer.updateContentsColor(backgroundContentColor: headerButtonBackgroundColor, contentsColor: navigationContentsAccentColor, canBeExpanded: navigationContentsCanBeExpanded, transition: navigationTransition)
self.titleNode.updateTintColor(color: navigationContentsPrimaryColor, transition: navigationTransition)
@ -1128,16 +1211,35 @@ final class PeerInfoHeaderNode: ASDisplayNode {
let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size
var titleHorizontalOffset: CGFloat = 0.0
var nextIconX: CGFloat = titleSize.width
var nextExpandedIconX: CGFloat = titleExpandedSize.width
if let credibilityIconSize = self.credibilityIconSize, let titleExpandedCredibilityIconSize = self.titleExpandedCredibilityIconSize {
titleHorizontalOffset = -(credibilityIconSize.width + 4.0) / 2.0
titleHorizontalOffset += -(credibilityIconSize.width + 4.0) / 2.0
var collapsedTransitionOffset: CGFloat = 0.0
if let navigationTransition = self.navigationTransition {
collapsedTransitionOffset = -10.0 * navigationTransition.fraction
}
transition.updateFrame(view: self.titleCredibilityIconView, frame: CGRect(origin: CGPoint(x: titleSize.width + 4.0 + collapsedTransitionOffset, y: floor((titleSize.height - credibilityIconSize.height) / 2.0)), size: credibilityIconSize))
transition.updateFrame(view: self.titleExpandedCredibilityIconView, frame: CGRect(origin: CGPoint(x: titleExpandedSize.width + 4.0, y: floor((titleExpandedSize.height - titleExpandedCredibilityIconSize.height) / 2.0) + 1.0), size: titleExpandedCredibilityIconSize))
transition.updateFrame(view: self.titleCredibilityIconView, frame: CGRect(origin: CGPoint(x: nextIconX + 4.0 + collapsedTransitionOffset, y: floor((titleSize.height - credibilityIconSize.height) / 2.0)), size: credibilityIconSize))
nextIconX += 4.0 + credibilityIconSize.width
transition.updateFrame(view: self.titleExpandedCredibilityIconView, frame: CGRect(origin: CGPoint(x: nextExpandedIconX + 4.0, y: floor((titleExpandedSize.height - titleExpandedCredibilityIconSize.height) / 2.0) + 1.0), size: titleExpandedCredibilityIconSize))
nextExpandedIconX += 4.0 + titleExpandedCredibilityIconSize.width
}
if let verifiedIconSize = self.verifiedIconSize, let titleExpandedVerifiedIconSize = self.titleExpandedVerifiedIconSize {
titleHorizontalOffset += -(verifiedIconSize.width + 4.0) / 2.0
var collapsedTransitionOffset: CGFloat = 0.0
if let navigationTransition = self.navigationTransition {
collapsedTransitionOffset = -10.0 * navigationTransition.fraction
}
transition.updateFrame(view: self.titleVerifiedIconView, frame: CGRect(origin: CGPoint(x: nextIconX + 4.0 + collapsedTransitionOffset, y: floor((titleSize.height - verifiedIconSize.height) / 2.0)), size: verifiedIconSize))
nextIconX += 4.0 + verifiedIconSize.width
transition.updateFrame(view: self.titleExpandedVerifiedIconView, frame: CGRect(origin: CGPoint(x: nextExpandedIconX + 4.0, y: floor((titleExpandedSize.height - titleExpandedVerifiedIconSize.height) / 2.0) + 1.0), size: titleExpandedVerifiedIconSize))
nextExpandedIconX += 4.0 + titleExpandedVerifiedIconSize.width
}
var titleFrame: CGRect

View File

@ -2257,6 +2257,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
private var translationState: ChatTranslationState?
private var translationStateDisposable: Disposable?
private var boostStatus: ChannelBoostStatus?
private var boostStatusDisposable: Disposable?
private var expiringStoryList: PeerExpiringStoryListContext?
private var expiringStoryListState: PeerExpiringStoryListContext.State?
private var expiringStoryListDisposable: Disposable?
@ -4048,6 +4051,21 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
})
let _ = context.engine.peers.requestRecommendedChannels(peerId: peerId, forceUpdate: true).startStandalone()
self.boostStatusDisposable = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|> mapToSignal { peer -> Signal<ChannelBoostStatus?, NoError> in
if case let .channel(channel) = peer, (channel.flags.contains(.isCreator) || channel.adminRights != nil) {
return context.engine.peers.getChannelBoostStatus(peerId: peerId)
} else {
return .single(nil)
}
}
|> deliverOnMainQueue).start(next: { [weak self] boostStatus in
guard let self else {
return
}
self.boostStatus = boostStatus
})
}
if peerId.namespace == Namespaces.Peer.CloudChannel || peerId.namespace == Namespaces.Peer.CloudUser {
@ -4154,6 +4172,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
self.storyUploadProgressDisposable?.dispose()
self.updateAvatarDisposable.dispose()
self.joinChannelDisposable.dispose()
self.boostStatusDisposable?.dispose()
}
override func didLoad() {
@ -7196,7 +7215,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
let controller = PeerNameColorScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, subject: .account)
self.controller?.push(controller)
} else if let peer = self.data?.peer, peer is TelegramChannel {
self.controller?.push(ChannelAppearanceScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: self.peerId))
self.controller?.push(ChannelAppearanceScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: self.peerId, boostStatus: self.boostStatus))
}
}

View File

@ -134,13 +134,16 @@ final class ChannelAppearanceScreenComponent: Component {
let context: AccountContext
let peerId: EnginePeer.Id
let boostStatus: ChannelBoostStatus?
init(
context: AccountContext,
peerId: EnginePeer.Id
peerId: EnginePeer.Id,
boostStatus: ChannelBoostStatus?
) {
self.context = context
self.peerId = peerId
self.boostStatus = boostStatus
}
static func ==(lhs: ChannelAppearanceScreenComponent, rhs: ChannelAppearanceScreenComponent) -> Bool {
@ -322,6 +325,33 @@ final class ChannelAppearanceScreenComponent: Component {
}
func attemptNavigation(complete: @escaping () -> Void) -> Bool {
guard let component = self.component, let resolvedState = self.resolveState() else {
return true
}
if self.isApplyingSettings {
return false
}
if !resolvedState.changes.isEmpty {
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.Channel_Appearance_UnsavedChangesAlertTitle, text: presentationData.strings.Channel_Appearance_UnsavedChangesAlertText, actions: [
TextAlertAction(type: .genericAction, title: presentationData.strings.Channel_Appearance_UnsavedChangesAlertDiscard, action: { [weak self] in
guard let self else {
return
}
self.environment?.controller()?.dismiss()
}),
TextAlertAction(type: .defaultAction, title: presentationData.strings.Channel_Appearance_UnsavedChangesAlertApply, action: { [weak self] in
guard let self else {
return
}
self.applySettings()
})
]), in: .window(.root))
return false
}
return true
}
@ -462,8 +492,15 @@ final class ChannelAppearanceScreenComponent: Component {
}
var signals: [Signal<Never, ApplyError>] = []
if !resolvedState.changes.intersection([.nameColor, .profileColor, .replyFileId, .backgroundFileId]).isEmpty {
signals.append(component.context.engine.peers.updatePeerNameColorAndEmoji(peerId: component.peerId, nameColor: resolvedState.nameColor, backgroundEmojiId: resolvedState.replyFileId, profileColor: resolvedState.profileColor, profileBackgroundEmojiId: resolvedState.backgroundFileId)
if !resolvedState.changes.intersection([.nameColor, .replyFileId]).isEmpty {
signals.append(component.context.engine.peers.updatePeerNameColor(peerId: component.peerId, nameColor: resolvedState.nameColor, backgroundEmojiId: resolvedState.replyFileId)
|> ignoreValues
|> mapError { _ -> ApplyError in
return .generic
})
}
if !resolvedState.changes.intersection([.profileColor, .backgroundFileId]).isEmpty {
signals.append(component.context.engine.peers.updatePeerProfileColor(peerId: component.peerId, profileColor: resolvedState.profileColor, profileBackgroundEmojiId: resolvedState.backgroundFileId)
|> ignoreValues
|> mapError { _ -> ApplyError in
return .generic
@ -512,7 +549,17 @@ final class ChannelAppearanceScreenComponent: Component {
guard let self else {
return
}
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let navigationController: NavigationController? = self.environment?.controller()?.navigationController as? NavigationController
self.environment?.controller()?.dismiss()
if let lastController = navigationController?.viewControllers.last as? ViewController {
//TODO:localize
let tipController = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: "Appearance settings have been updated.", cancel: nil, destructive: false), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false })
lastController.present(tipController, in: .window(.root))
}
})
}
@ -733,8 +780,13 @@ final class ChannelAppearanceScreenComponent: Component {
guard let self, let component = self.component else {
return
}
if self.contentsData == nil, case let .channel(channel) = contentsData.peer {
self.boostLevel = channel.approximateBoostLevel.flatMap(Int.init)
if self.contentsData == nil && self.boostStatus == nil {
if let boostStatus = component.boostStatus {
self.boostStatus = boostStatus
self.boostLevel = boostStatus.level
} else if case let .channel(channel) = contentsData.peer {
self.boostLevel = channel.approximateBoostLevel.flatMap(Int.init)
}
}
if self.contentsData == nil, let peerWallpaper = contentsData.peerWallpaper {
for cloudTheme in contentsData.availableThemes {
@ -1448,13 +1500,15 @@ public class ChannelAppearanceScreen: ViewControllerComponentContainer {
public init(
context: AccountContext,
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?,
peerId: EnginePeer.Id
peerId: EnginePeer.Id,
boostStatus: ChannelBoostStatus?
) {
self.context = context
super.init(context: context, component: ChannelAppearanceScreenComponent(
context: context,
peerId: peerId
peerId: peerId,
boostStatus: boostStatus
), navigationBarAppearance: .default, theme: .default, updatedPresentationData: updatedPresentationData)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }

View File

@ -1633,7 +1633,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
let attributedText = convertMarkdownToAttributes(NSAttributedString(string: serviceMessageText))
let entities = generateChatInputTextEntities(attributedText)
let message3 = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: attributedText.string, entities: entities))], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
let message3 = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: attributedText.string, entities: entities, additionalAttributes: nil))], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, accountPeer: nil, isCentered: false))
}

View File

@ -277,16 +277,21 @@ public final class ChatTextInputTextCustomEmojiAttribute: NSObject, Codable {
case topicInfo
}
public enum Custom: Codable {
case topic(id: Int64, info: EngineMessageHistoryThread.Info)
case nameColors([UInt32])
}
public let interactivelySelectedFromPackId: ItemCollectionId?
public let fileId: Int64
public let file: TelegramMediaFile?
public let topicInfo: (Int64, EngineMessageHistoryThread.Info)?
public let custom: Custom?
public init(interactivelySelectedFromPackId: ItemCollectionId?, fileId: Int64, file: TelegramMediaFile?, topicInfo: (Int64, EngineMessageHistoryThread.Info)? = nil) {
public init(interactivelySelectedFromPackId: ItemCollectionId?, fileId: Int64, file: TelegramMediaFile?, custom: Custom? = nil) {
self.interactivelySelectedFromPackId = interactivelySelectedFromPackId
self.fileId = fileId
self.file = file
self.topicInfo = topicInfo
self.custom = custom
super.init()
}
@ -296,11 +301,7 @@ public final class ChatTextInputTextCustomEmojiAttribute: NSObject, Codable {
self.interactivelySelectedFromPackId = try container.decodeIfPresent(ItemCollectionId.self, forKey: .interactivelySelectedFromPackId)
self.fileId = try container.decode(Int64.self, forKey: .fileId)
self.file = try container.decodeIfPresent(TelegramMediaFile.self, forKey: .file)
if let topicId = try container.decodeIfPresent(Int64.self, forKey: .topicId), let topicInfo = try container.decodeIfPresent(EngineMessageHistoryThread.Info.self, forKey: .topicInfo) {
self.topicInfo = (topicId, topicInfo)
} else {
self.topicInfo = nil
}
self.custom = nil
}
public func encode(to encoder: Encoder) throws {
@ -308,16 +309,11 @@ public final class ChatTextInputTextCustomEmojiAttribute: NSObject, Codable {
try container.encodeIfPresent(self.interactivelySelectedFromPackId, forKey: .interactivelySelectedFromPackId)
try container.encode(self.fileId, forKey: .fileId)
try container.encodeIfPresent(self.file, forKey: .file)
if let (topicId, topicInfo) = self.topicInfo {
try container.encode(topicId, forKey: .topicId)
try container.encode(topicInfo, forKey: .topicInfo)
}
}
override public func isEqual(_ object: Any?) -> Bool {
if let other = object as? ChatTextInputTextCustomEmojiAttribute {
return self === other
//return self.stickerPack == other.stickerPack && self.fileId == other.fileId && self.file?.fileId == other.file?.fileId
} else {
return false
}

View File

@ -15,7 +15,7 @@ public enum UndoOverlayContent {
case info(title: String?, text: String, timeout: Double?, customUndoText: String?)
case emoji(name: String, text: String)
case swipeToReply(title: String, text: String)
case actionSucceeded(title: String, text: String, cancel: String, destructive: Bool)
case actionSucceeded(title: String?, text: String, cancel: String?, destructive: Bool)
case stickersModified(title: String, text: String, undo: Bool, info: StickerPackCollectionInfo, topItem: StickerPackItem?, context: AccountContext)
case dice(dice: TelegramMediaDice, context: AccountContext, text: String, action: String?)
case chatAddedToFolder(chatTitle: String, folderTitle: String)

View File

@ -262,10 +262,16 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor)
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural)
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
if let title {
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
}
self.textNode.attributedText = attributedText
displayUndo = true
undoText = cancel
if let cancel {
displayUndo = true
undoText = cancel
} else {
displayUndo = false
}
self.originalRemainingSeconds = 5
case let .linkCopied(text):
self.avatarNode = nil
@ -1341,7 +1347,9 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor)
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural)
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
if let title {
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
}
self.textNode.attributedText = attributedText
default:
break