diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 816225b03b..97a0bfa489 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -15828,3 +15828,7 @@ Error: %8$@"; "Channel.AdminLog.EditRankOwn" = "Edit Member Tag"; "Channel.AdminLog.EditRank" = "Edit Member Tags"; + +"Channel.AdminLog.MessageRankNameNew" = "changed member tag for %1$@:\n%2$@"; +"Channel.AdminLog.MessageRankUsernameNew" = "changed member tag for %1$@ (%2$@):\n%3$@"; +"Channel.AdminLog.MessageRankNew" = "changed member tag:\n%1$@"; diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 0b6d511a1f..4bf66ba784 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -1528,7 +1528,7 @@ public protocol SharedAccountContext: AnyObject { func makeLoginEmailSetupController(context: AccountContext, blocking: Bool, emailPattern: String?, canAutoDismissIfNeeded: Bool, navigationController: NavigationController?, completion: @escaping () -> Void, dismiss: @escaping () -> Void) -> ViewController func makePasskeySetupController(context: AccountContext, displaySkip: Bool, navigationController: NavigationController?, completion: @escaping () -> Void, dismiss: @escaping () -> Void) -> ViewController - func makeChatCustomRankSetupScreen(context: AccountContext, peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?) -> ViewController + func makeChatCustomRankSetupScreen(context: AccountContext, peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?, role: ChatRankInfoScreenRole) -> ViewController func makePeerCopyProtectionInfoScreen(context: AccountContext, completion: @escaping () -> Void) -> ViewController diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index d87f126be4..706b8b719e 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -5263,7 +5263,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let context = self.context let controller = UndoOverlayController(presentationData: self.presentationData, content: .actionSucceeded(title: nil, text: "You joined the group", cancel: "Add Tag", destructive: false), elevatedLayout: true, action: { [weak self] action in if let self, case .undo = action { - let tagController = context.sharedContext.makeChatCustomRankSetupScreen(context: context, peerId: peerId, participantId: context.account.peerId, rank: nil) + let tagController = context.sharedContext.makeChatCustomRankSetupScreen(context: context, peerId: peerId, participantId: context.account.peerId, rank: nil, role: .member) self.push(tagController) } return true diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 8818f01d1b..2c355cf32a 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -152,6 +152,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1515256996] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionExportedInviteDelete($0) } dict[-384910503] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionExportedInviteEdit($0) } dict[1091179342] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionExportedInviteRevoke($0) } + dict[1476834540] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantEditRank($0) } dict[-484690728] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantInvite($0) } dict[405815507] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantJoin($0) } dict[-23084712] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantJoinByInvite($0) } diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index 2273a5af70..abc84a4d70 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -787,6 +787,19 @@ public extension Api { return ("channelAdminLogEventActionExportedInviteRevoke", [("invite", self.invite as Any)]) } } + public class Cons_channelAdminLogEventActionParticipantEditRank: TypeConstructorDescription { + public var userId: Int64 + public var prevRank: String + public var newRank: String + public init(userId: Int64, prevRank: String, newRank: String) { + self.userId = userId + self.prevRank = prevRank + self.newRank = newRank + } + public func descriptionFields() -> (String, [(String, Any)]) { + return ("channelAdminLogEventActionParticipantEditRank", [("userId", self.userId as Any), ("prevRank", self.prevRank as Any), ("newRank", self.newRank as Any)]) + } + } public class Cons_channelAdminLogEventActionParticipantInvite: TypeConstructorDescription { public var participant: Api.ChannelParticipant public init(participant: Api.ChannelParticipant) { @@ -1044,6 +1057,7 @@ public extension Api { case channelAdminLogEventActionExportedInviteDelete(Cons_channelAdminLogEventActionExportedInviteDelete) case channelAdminLogEventActionExportedInviteEdit(Cons_channelAdminLogEventActionExportedInviteEdit) case channelAdminLogEventActionExportedInviteRevoke(Cons_channelAdminLogEventActionExportedInviteRevoke) + case channelAdminLogEventActionParticipantEditRank(Cons_channelAdminLogEventActionParticipantEditRank) case channelAdminLogEventActionParticipantInvite(Cons_channelAdminLogEventActionParticipantInvite) case channelAdminLogEventActionParticipantJoin case channelAdminLogEventActionParticipantJoinByInvite(Cons_channelAdminLogEventActionParticipantJoinByInvite) @@ -1250,6 +1264,14 @@ public extension Api { } _data.invite.serialize(buffer, true) break + case .channelAdminLogEventActionParticipantEditRank(let _data): + if boxed { + buffer.appendInt32(1476834540) + } + serializeInt64(_data.userId, buffer: buffer, boxed: false) + serializeString(_data.prevRank, buffer: buffer, boxed: false) + serializeString(_data.newRank, buffer: buffer, boxed: false) + break case .channelAdminLogEventActionParticipantInvite(let _data): if boxed { buffer.appendInt32(-484690728) @@ -1471,6 +1493,8 @@ public extension Api { return ("channelAdminLogEventActionExportedInviteEdit", [("prevInvite", _data.prevInvite as Any), ("newInvite", _data.newInvite as Any)]) case .channelAdminLogEventActionExportedInviteRevoke(let _data): return ("channelAdminLogEventActionExportedInviteRevoke", [("invite", _data.invite as Any)]) + case .channelAdminLogEventActionParticipantEditRank(let _data): + return ("channelAdminLogEventActionParticipantEditRank", [("userId", _data.userId as Any), ("prevRank", _data.prevRank as Any), ("newRank", _data.newRank as Any)]) case .channelAdminLogEventActionParticipantInvite(let _data): return ("channelAdminLogEventActionParticipantInvite", [("participant", _data.participant as Any)]) case .channelAdminLogEventActionParticipantJoin: @@ -1926,6 +1950,23 @@ public extension Api { return nil } } + public static func parse_channelAdminLogEventActionParticipantEditRank(_ reader: BufferReader) -> ChannelAdminLogEventAction? { + var _1: Int64? + _1 = reader.readInt64() + var _2: String? + _2 = parseString(reader) + var _3: String? + _3 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantEditRank(Cons_channelAdminLogEventActionParticipantEditRank(userId: _1!, prevRank: _2!, newRank: _3!)) + } + else { + return nil + } + } public static func parse_channelAdminLogEventActionParticipantInvite(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.ChannelParticipant? if let signature = reader.readInt32() { diff --git a/submodules/TelegramApi/Sources/Api40.swift b/submodules/TelegramApi/Sources/Api40.swift index c9eed1486d..7dcb134207 100644 --- a/submodules/TelegramApi/Sources/Api40.swift +++ b/submodules/TelegramApi/Sources/Api40.swift @@ -3157,14 +3157,17 @@ public extension Api.functions.channels { } } public extension Api.functions.channels { - static func editAdmin(channel: Api.InputChannel, userId: Api.InputUser, adminRights: Api.ChatAdminRights, rank: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func editAdmin(flags: Int32, channel: Api.InputChannel, userId: Api.InputUser, adminRights: Api.ChatAdminRights, rank: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(-751007486) + buffer.appendInt32(-1701270168) + serializeInt32(flags, buffer: buffer, boxed: false) channel.serialize(buffer, true) userId.serialize(buffer, true) adminRights.serialize(buffer, true) - serializeString(rank, buffer: buffer, boxed: false) - return (FunctionDescription(name: "channels.editAdmin", parameters: [("channel", String(describing: channel)), ("userId", String(describing: userId)), ("adminRights", String(describing: adminRights)), ("rank", String(describing: rank))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + if Int(flags) & Int(1 << 0) != 0 { + serializeString(rank!, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "channels.editAdmin", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("userId", String(describing: userId)), ("adminRights", String(describing: adminRights)), ("rank", String(describing: rank))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in let reader = BufferReader(buffer) var result: Api.Updates? if let signature = reader.readInt32() { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogContext.swift index 6b69465557..3e03cd2694 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogContext.swift @@ -180,6 +180,7 @@ public final class ChannelAdminEventLogContext { clipIndex = i - 1 } } + clipIndex = max(0, clipIndex) if clipIndex < events.count { events.removeSubrange(clipIndex ..< events.count) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogs.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogs.swift index 60f0512b42..f1481f4e1c 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogs.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChannelAdminEventLogs.swift @@ -95,6 +95,7 @@ public enum AdminLogEventAction { case changeEmojiPack(prev: StickerPackReference?, new: StickerPackReference?) case participantSubscriptionExtended(prev: RenderedChannelParticipant, new: RenderedChannelParticipant) case toggleAutoTranslation(Bool) + case editRank(peerId: EnginePeer.Id, prev: String, new: String) } public enum ChannelAdminLogEventError { @@ -129,12 +130,13 @@ public struct AdminLogEventsFlags: OptionSet { public static let invites = AdminLogEventsFlags(rawValue: 1 << 15) public static let sendMessages = AdminLogEventsFlags(rawValue: 1 << 16) public static let forums = AdminLogEventsFlags(rawValue: 1 << 17) + public static let ranks = AdminLogEventsFlags(rawValue: 1 << 19) public static var all: AdminLogEventsFlags { - return [.join, .leave, .invite, .ban, .unban, .kick, .unkick, .promote, .demote, .info, .settings, .sendMessages, .pinnedMessages, .editMessages, .deleteMessages, .calls, .invites, .forums] + return [.join, .leave, .invite, .ban, .unban, .kick, .unkick, .promote, .demote, .info, .settings, .sendMessages, .pinnedMessages, .editMessages, .deleteMessages, .calls, .invites, .forums, .ranks] } public static var flags: AdminLogEventsFlags { - return [.join, .leave, .invite, .ban, .unban, .kick, .unkick, .promote, .demote, .info, .settings, .sendMessages, .pinnedMessages, .editMessages, .deleteMessages, .calls, .invites, .forums] + return [.join, .leave, .invite, .ban, .unban, .kick, .unkick, .promote, .demote, .info, .settings, .sendMessages, .pinnedMessages, .editMessages, .deleteMessages, .calls, .invites, .forums, .ranks] } } @@ -483,6 +485,8 @@ func channelAdminLogEvents(accountPeerId: PeerId, postbox: Postbox, network: Net case let .channelAdminLogEventActionToggleAutotranslation(channelAdminLogEventActionToggleAutotranslationData): let newValue = channelAdminLogEventActionToggleAutotranslationData.newValue action = .toggleAutoTranslation(boolFromApiValue(newValue)) + case let .channelAdminLogEventActionParticipantEditRank(channelAdminLogEventActionParticipantEditRankData): + action = .editRank(peerId: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(channelAdminLogEventActionParticipantEditRankData.userId)), prev: channelAdminLogEventActionParticipantEditRankData.prevRank, new: channelAdminLogEventActionParticipantEditRankData.newRank) } let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)) if let action = action { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/PeerAdmins.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/PeerAdmins.swift index c2387f623e..211559c596 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/PeerAdmins.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/PeerAdmins.swift @@ -184,7 +184,12 @@ func _internal_updateChannelAdminRights(account: Account, peerId: PeerId, adminI } updatedParticipant = .member(id: adminId, invitedAt: Int32(Date().timeIntervalSince1970), adminInfo: adminInfo, banInfo: nil, rank: rank, subscriptionUntilDate: nil) } - return account.network.request(Api.functions.channels.editAdmin(channel: inputChannel, userId: inputUser, adminRights: rights?.apiAdminRights ?? .chatAdminRights(Api.ChatAdminRights.Cons_chatAdminRights(flags: 0)), rank: rank ?? "")) + + var flags: Int32 = 0 + if let _ = rank { + flags = (1 << 0) + } + return account.network.request(Api.functions.channels.editAdmin(flags: flags, channel: inputChannel, userId: inputUser, adminRights: rights?.apiAdminRights ?? .chatAdminRights(Api.ChatAdminRights.Cons_chatAdminRights(flags: 0)), rank: rank ?? "")) |> map { [$0] } |> `catch` { error -> Signal<[Api.Updates], UpdateChannelAdminRightsError> in if error.errorDescription == "USER_NOT_PARTICIPANT" { @@ -196,7 +201,7 @@ func _internal_updateChannelAdminRights(account: Account, peerId: PeerId, adminI return .addMemberError(error) } |> then( - account.network.request(Api.functions.channels.editAdmin(channel: inputChannel, userId: inputUser, adminRights: rights?.apiAdminRights ?? .chatAdminRights(Api.ChatAdminRights.Cons_chatAdminRights(flags: 0)), rank: rank ?? "")) + account.network.request(Api.functions.channels.editAdmin(flags: flags, channel: inputChannel, userId: inputUser, adminRights: rights?.apiAdminRights ?? .chatAdminRights(Api.ChatAdminRights.Cons_chatAdminRights(flags: 0)), rank: rank ?? "")) |> mapError { error -> UpdateChannelAdminRightsError in return .generic } diff --git a/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift index f970400d6e..71c8ad4310 100644 --- a/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift @@ -268,7 +268,7 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: "You joined the group", cancel: "Add Tag", destructive: false), elevatedLayout: true, action: { action in if case .undo = action { - let tagController = context.sharedContext.makeChatCustomRankSetupScreen(context: context, peerId: peer.id, participantId: context.account.peerId, rank: nil) + let tagController = context.sharedContext.makeChatCustomRankSetupScreen(context: context, peerId: peer.id, participantId: context.account.peerId, rank: nil, role: .member) self.interfaceInteraction?.getNavigationController()?.pushViewController(tagController) } return true diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift index e706cb2c4b..f0babf06cc 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift @@ -1054,7 +1054,7 @@ public final class ChatMessageAvatarHeaderNodeImpl: ListViewItemHeaderNode, Chat } public func updatePeer(peer: Peer) { - if let previousPeer = self.peer, previousPeer.nameColor != peer.nameColor { + if let previousPeer = self.peer, previousPeer.nameColor != peer.nameColor || previousPeer.smallProfileImage != peer.smallProfileImage { self.peer = peer if peer.smallProfileImage != nil { self.avatarNode.setPeerV2(context: self.context, theme: self.presentationData.theme.theme, peer: EnginePeer(peer), authorOfMessage: self.messageReference, overrideImage: nil, emptyColor: .black, synchronousLoad: false, displayDimensions: CGSize(width: avatarHeaderSize(), height: avatarHeaderSize())) diff --git a/submodules/TelegramUI/Components/Chat/ChatRankInfoScreen/Sources/ChatRankInfoScreen.swift b/submodules/TelegramUI/Components/Chat/ChatRankInfoScreen/Sources/ChatRankInfoScreen.swift index de9b8d5a7e..69e350ec6c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRankInfoScreen/Sources/ChatRankInfoScreen.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRankInfoScreen/Sources/ChatRankInfoScreen.swift @@ -122,7 +122,7 @@ private final class ChatRankInfoSheetContent: CombinedComponent { let textString: String let linkColor: UIColor var linkHasBackground = true - let additionalTextString: String? = "Only admins can assign member tags in this group." + let additionalTextString: String? = component.canChange ? nil : "Only admins can assign member tags in this group." var adminPreviewTag = "Admin Tag" var adminPreviewRole: ChatRankInfoScreenRole = .admin @@ -379,7 +379,7 @@ private final class ChatRankInfoSheetContent: CombinedComponent { var buttonTitle: [AnyComponentWithIdentity] = [] if canEdit { buttonTitle.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(ButtonTextContentComponent( - text: "Add My Tag", + text: "Set My Tag", badge: 0, textColor: theme.list.itemCheckColors.foregroundColor, badgeBackground: theme.list.itemCheckColors.foregroundColor, diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift index 5a0cb73a5a..497131afb6 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsHistoryTransition.swift @@ -132,212 +132,40 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { func item(context: AccountContext, peer: Peer, controllerInteraction: ChatControllerInteraction, chatThemes: [TelegramTheme], availableReactions: AvailableReactions?, availableMessageEffects: AvailableMessageEffects?) -> ListViewItem { switch self.entry.event.action { - case let .changeTitle(_, new): - var peers = SimpleDictionary() - var author: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer - } + case let .changeTitle(_, new): + var peers = SimpleDictionary() + var author: Peer? + if let peer = self.entry.peers[self.entry.event.peerId] { + author = peer peers[peer.id] = peer - - let action = TelegramMediaActionType.titleUpdated(title: new) - 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .changeAbout(prev, new): - var peers = SimpleDictionary() - var author: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer - } + } + peers[peer.id] = peer + + let action = TelegramMediaActionType.titleUpdated(title: new) + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .changeAbout(prev, new): + var peers = SimpleDictionary() + var author: Peer? + if let peer = self.entry.peers[self.entry.event.peerId] { + author = peer peers[peer.id] = peer - - switch self.id.contentIndex { - case .header: - var text: String = "" - var entities: [MessageTextEntity] = [] - if let peer = peer as? TelegramChannel, case .broadcast = peer.info { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedChannelAbout(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)] - } - return [] - }, to: &text, entities: &entities) - } else { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedGroupAbout(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)] - } - return [] - }, to: &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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case .content: - let peers = SimpleDictionary() - let attributes: [MessageAttribute] = [] - let prevMessage = 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: [], customTags: [], forwardInfo: nil, author: author, text: prev, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - - 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: [], customTags: [], forwardInfo: nil, author: author, text: new, attributes: attributes, media: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousDescription(prevMessage) : nil) - } - case let .changeUsername(prev, new): - var peers = SimpleDictionary() - var author: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer - } - - switch self.id.contentIndex { - case .header: - var text: String = "" - var entities: [MessageTextEntity] = [] - if let peer = peer as? TelegramChannel, case .broadcast = peer.info { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedChannelUsername(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)] - } - return [] - }, to: &text, entities: &entities) - } else { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedGroupUsername(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)] - } - return [] - }, to: &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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case .content: - var previousAttributes: [MessageAttribute] = [] - var attributes: [MessageAttribute] = [] - - let prevText = "https://t.me/\(prev)" - previousAttributes.append(TextEntitiesMessageAttribute(entities: [MessageTextEntity(range: 0 ..< prevText.count, type: .Url)])) - - let text: String - if !new.isEmpty { - text = "https://t.me/\(new)" - attributes.append(TextEntitiesMessageAttribute(entities: [MessageTextEntity(range: 0 ..< text.count, type: .Url)])) - } else { - text = self.presentationData.strings.Channel_AdminLog_EmptyMessageText - attributes.append(TextEntitiesMessageAttribute(entities: [MessageTextEntity(range: 0 ..< text.count, type: .Italic)])) - } - - let prevMessage = Message(stableId: 0, 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: [], customTags: [], forwardInfo: nil, author: author, text: prevText, attributes: previousAttributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - 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: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousLink(prevMessage) : nil) - } - case let .changeUsernames(prev, new): - var peers = SimpleDictionary() - var author: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer - } - - switch self.id.contentIndex { - case .header: - var text: String = "" - var entities: [MessageTextEntity] = [] - if let peer = peer as? TelegramChannel, case .broadcast = peer.info { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedChannelUsernames(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)] - } - return [] - }, to: &text, entities: &entities) - } else { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedGroupUsernames(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)] - } - return [] - }, to: &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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case .content: - var previousAttributes: [MessageAttribute] = [] - var attributes: [MessageAttribute] = [] - - var prevTextEntities: [MessageTextEntity] = [] - var textEntities: [MessageTextEntity] = [] - - var prevText: String = "" - for username in prev { - let link = "https://t.me/\(username)" - prevTextEntities.append(MessageTextEntity(range: prevText.count ..< prevText.count + link.count, type: .Url)) - prevText.append(link) - prevText.append("\n") - } - prevText.removeLast() - if !prevTextEntities.isEmpty { - previousAttributes.append(TextEntitiesMessageAttribute(entities: prevTextEntities)) - } - var text: String = "" - if !new.isEmpty { - for username in new { - let link = "https://t.me/\(username)" - textEntities.append(MessageTextEntity(range: text.count ..< text.count + link.count, type: .Url)) - text.append(link) - text.append("\n") - } - text.removeLast() - if !textEntities.isEmpty { - attributes.append(TextEntitiesMessageAttribute(entities: prevTextEntities)) - } - } else { - text = self.presentationData.strings.Channel_AdminLog_EmptyMessageText - attributes.append(TextEntitiesMessageAttribute(entities: [MessageTextEntity(range: 0 ..< text.count, type: .Italic)])) - } - - let prevMessage = Message(stableId: 0, 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: [], customTags: [], forwardInfo: nil, author: author, text: prevText, attributes: previousAttributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - 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: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousLink(prevMessage) : nil) - } - case let .changePhoto(_, new): - var peers = SimpleDictionary() - var author: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer - } - peers[peer.id] = peer - - var photo: TelegramMediaImage? - let (newPhoto, newVideo) = new - if !newPhoto.isEmpty || !newVideo.isEmpty { - photo = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: newPhoto, videoRepresentations: newVideo, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: []) - } - - let action = TelegramMediaActionType.photoUpdated(image: photo) - 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .toggleInvites(value): - var peers = SimpleDictionary() - var author: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer - } + } + peers[peer.id] = peer + + switch self.id.contentIndex { + case .header: var text: String = "" var entities: [MessageTextEntity] = [] - if value { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageToggleInvitesOn(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""), generateEntities: { index in + if let peer = peer as? TelegramChannel, case .broadcast = peer.info { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedChannelAbout(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)] } return [] }, to: &text, entities: &entities) } else { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageToggleInvitesOff(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""), generateEntities: { index in + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedGroupAbout(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)] } @@ -345,462 +173,354 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { }, 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: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case .toggleSignatures(let value), .toggleSignatureProfiles(let value): - var peers = SimpleDictionary() - var author: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer - } + case .content: + let peers = SimpleDictionary() + let attributes: [MessageAttribute] = [] + let prevMessage = 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: [], customTags: [], forwardInfo: nil, author: author, text: prev, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + + 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: [], customTags: [], forwardInfo: nil, author: author, text: new, attributes: attributes, media: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousDescription(prevMessage) : nil) + } + case let .changeUsername(prev, new): + var peers = SimpleDictionary() + var author: Peer? + if let peer = self.entry.peers[self.entry.event.peerId] { + author = peer + peers[peer.id] = peer + } + + switch self.id.contentIndex { + case .header: var text: String = "" var entities: [MessageTextEntity] = [] - if value { - let pattern: (String) -> PresentationStrings.FormattedString - if case .toggleSignatureProfiles = self.entry.event.action { - pattern = self.presentationData.strings.Channel_AdminLog_MessageToggleProfileSignaturesOn - } else { - pattern = self.presentationData.strings.Channel_AdminLog_MessageToggleSignaturesOn - } - - appendAttributedText(text: pattern(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""), generateEntities: { index in + if let peer = peer as? TelegramChannel, case .broadcast = peer.info { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedChannelUsername(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)] } return [] }, to: &text, entities: &entities) } else { - let pattern: (String) -> PresentationStrings.FormattedString - if case .toggleSignatureProfiles = self.entry.event.action { - pattern = self.presentationData.strings.Channel_AdminLog_MessageToggleProfileSignaturesOff - } else { - pattern = self.presentationData.strings.Channel_AdminLog_MessageToggleSignaturesOff - } - - appendAttributedText(text: pattern(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""), generateEntities: { index in + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedGroupUsername(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)] } 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: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .updatePinned(message): - switch self.id.contentIndex { - case .header: - var peers = SimpleDictionary() - var author: Peer? - if self.entry.event.peerId == PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(136817688)) { - author = message?.effectiveAuthor - } else if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer - } - var text: String = "" - var entities: [MessageTextEntity] = [] - - let textFormat: (String) -> PresentationStrings.FormattedString - if let message = message, message.tags.contains(.pinned) { - textFormat = self.presentationData.strings.Channel_AdminLog_MessagePinned - } else { - textFormat = self.presentationData.strings.Channel_AdminLog_MessageUnpinnedExtended - } - - appendAttributedText(text: textFormat(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)] - } - return [] - }, to: &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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case .content: - if let message = message { - var peers = SimpleDictionary() - var attributes: [MessageAttribute] = [] - for attribute in message.attributes { - if let attribute = attribute as? TextEntitiesMessageAttribute { - attributes.append(attribute) - } - if let attribute = attribute as? ReplyMessageAttribute { - attributes.append(attribute) - } - } - for attribute in attributes { - for peerId in attribute.associatedPeerIds { - if let peer = self.entry.peers[peerId] { - peers[peer.id] = peer - } - } - } - if let peer = self.entry.peers[message.id.peerId] { - peers[peer.id] = peer - } - 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: message.threadId, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: message.associatedThreadInfo, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - } else { - var peers = SimpleDictionary() - 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] = [] - - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageUnpinned(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)] - } - 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: 0), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - } - } - case let .editMessage(prev, message): - switch self.id.contentIndex { - case .header: - var peers = SimpleDictionary() - 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] = [] - - var mediaUpdated = false - if prev.media.count == message.media.count { - for i in 0 ..< prev.media.count { - if !prev.media[i].isEqual(to: message.media[i]) { - mediaUpdated = true - break - } - } - } else { - mediaUpdated = true - } - - let titleText: PresentationStrings.FormattedString - if mediaUpdated || message.media.isEmpty { - titleText = self.presentationData.strings.Channel_AdminLog_MessageEdited(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") - } else { - titleText = self.presentationData.strings.Channel_AdminLog_CaptionEdited(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") - } - - appendAttributedText(text: titleText, 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.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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case .content: - var peers = SimpleDictionary() - var attributes: [MessageAttribute] = [] - for attribute in message.attributes { - if let attribute = attribute as? TextEntitiesMessageAttribute { - attributes.append(attribute) - } - if let attribute = attribute as? ReplyMessageAttribute { - attributes.append(attribute) - } - } - for attribute in attributes { - for peerId in attribute.associatedPeerIds { - if let peer = self.entry.peers[peerId] { - peers[peer.id] = peer - } - } - } - if let peer = self.entry.peers[message.id.peerId] { - peers[peer.id] = peer - } - 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: message.threadId, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: message.associatedMessages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: message.associatedThreadInfo, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: filterOriginalMessageFlags(message), read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.text.isEmpty || !message.text.isEmpty ? .eventLogPreviousMessage(filterOriginalMessageFlags(prev)) : nil) - } - case let .deleteMessage(message): - switch self.id.contentIndex { - case .header: - var peers = SimpleDictionary() - var author: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer - } - peers[peer.id] = peer - - var text: String = "" - var entities: [MessageTextEntity] = [] - - let authorName = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" - - if !self.subEntries.isEmpty { - var peers: [EnginePeer] = [] - var existingPeerIds = Set() - for entry in self.subEntries { - if case let .deleteMessage(message) = entry.event.action, let author = message.author { - guard !existingPeerIds.contains(author.id) else { - continue - } - peers.append(EnginePeer(author)) - existingPeerIds.insert(author.id) - } - } - let peerNames = peers.map { $0.compactDisplayTitle }.joined(separator: ", ") - let messagesString = self.presentationData.strings.Channel_AdminLog_MessageManyDeleted_Messages(Int32(self.subEntries.count)) - - let fullText: PresentationStrings.FormattedString - if let isExpanded = self.isExpanded { - let moreText = (isExpanded ? self.presentationData.strings.Channel_AdminLog_MessageManyDeleted_HideAll : self.presentationData.strings.Channel_AdminLog_MessageManyDeleted_ShowAll).replacingOccurrences(of: " ", with: "\u{00A0}") - fullText = self.presentationData.strings.Channel_AdminLog_MessageManyDeletedMore(authorName, messagesString, peerNames, moreText) - } else { - fullText = self.presentationData.strings.Channel_AdminLog_MessageManyDeleted(authorName, messagesString, peerNames) - } - - appendAttributedText(text: fullText, generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } else if index == 3 { - return [.Custom(type: ApplicationSpecificEntityType.Button)] - } - return [] - }, to: &text, entities: &entities) - } else { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageDeleted(authorName), 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.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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case .content: - var peers = SimpleDictionary() - var attributes: [MessageAttribute] = [] - for attribute in message.attributes { - if let attribute = attribute as? TextEntitiesMessageAttribute { - attributes.append(attribute) - } - if let attribute = attribute as? ReplyMessageAttribute { - attributes.append(attribute) - } - } - for attribute in attributes { - for peerId in attribute.associatedPeerIds { - if let peer = self.entry.peers[peerId] { - peers[peer.id] = peer - } - } - } - for media in message.media { - for peerId in media.peerIds { - if let peer = self.entry.peers[peerId] { - peers[peer.id] = peer - } - } - } - if let peer = self.entry.peers[self.entry.event.peerId] { - peers[peer.id] = peer - } - if let peer = self.entry.peers[message.id.peerId] { - peers[peer.id] = peer - } - - var additionalContent: ChatMessageItemAdditionalContent? - if !self.subEntries.isEmpty { - var messages: [Message] = [] - for entry in self.subEntries { - if case let .deleteMessage(message) = entry.event.action { - messages.append(message) - } - } - var hasButton = false - if let isExpanded = self.isExpanded, !isExpanded { - hasButton = true - } - additionalContent = .eventLogGroupedMessages(messages, hasButton) - } - - let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: message.id.id), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: message.threadId, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: message.associatedMessages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: message.associatedThreadInfo, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: additionalContent) - } - case .participantJoin, .participantLeave: - var peers = SimpleDictionary() - var author: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer - } - peers[peer.id] = peer - - let action: TelegramMediaActionType - if case .participantJoin = self.entry.event.action { - action = TelegramMediaActionType.addedMembers(peerIds: [self.entry.event.peerId]) - } else { - action = TelegramMediaActionType.removedMembers(peerIds: [self.entry.event.peerId]) - } - 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .participantInvite(participant): - var peers = SimpleDictionary() - var author: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer - } - peers[peer.id] = peer - for (_, peer) in participant.peers { - peers[peer.id] = peer - } - peers[participant.peer.id] = participant.peer - - let action: TelegramMediaActionType - action = TelegramMediaActionType.addedMembers(peerIds: [participant.peer.id]) - 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .participantToggleBan(prev, new): - var peers = SimpleDictionary() + case .content: + var previousAttributes: [MessageAttribute] = [] var attributes: [MessageAttribute] = [] + let prevText = "https://t.me/\(prev)" + previousAttributes.append(TextEntitiesMessageAttribute(entities: [MessageTextEntity(range: 0 ..< prevText.count, type: .Url)])) + + let text: String + if !new.isEmpty { + text = "https://t.me/\(new)" + attributes.append(TextEntitiesMessageAttribute(entities: [MessageTextEntity(range: 0 ..< text.count, type: .Url)])) + } else { + text = self.presentationData.strings.Channel_AdminLog_EmptyMessageText + attributes.append(TextEntitiesMessageAttribute(entities: [MessageTextEntity(range: 0 ..< text.count, type: .Italic)])) + } + + let prevMessage = Message(stableId: 0, 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: [], customTags: [], forwardInfo: nil, author: author, text: prevText, attributes: previousAttributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + 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: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousLink(prevMessage) : nil) + } + case let .changeUsernames(prev, new): + var peers = SimpleDictionary() + var author: Peer? + if let peer = self.entry.peers[self.entry.event.peerId] { + author = peer + peers[peer.id] = peer + } + + switch self.id.contentIndex { + case .header: + var text: String = "" + var entities: [MessageTextEntity] = [] + if let peer = peer as? TelegramChannel, case .broadcast = peer.info { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedChannelUsernames(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)] + } + return [] + }, to: &text, entities: &entities) + } else { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedGroupUsernames(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)] + } + return [] + }, to: &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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case .content: + var previousAttributes: [MessageAttribute] = [] + var attributes: [MessageAttribute] = [] + + var prevTextEntities: [MessageTextEntity] = [] + var textEntities: [MessageTextEntity] = [] + + var prevText: String = "" + for username in prev { + let link = "https://t.me/\(username)" + prevTextEntities.append(MessageTextEntity(range: prevText.count ..< prevText.count + link.count, type: .Url)) + prevText.append(link) + prevText.append("\n") + } + prevText.removeLast() + if !prevTextEntities.isEmpty { + previousAttributes.append(TextEntitiesMessageAttribute(entities: prevTextEntities)) + } + var text: String = "" + if !new.isEmpty { + for username in new { + let link = "https://t.me/\(username)" + textEntities.append(MessageTextEntity(range: text.count ..< text.count + link.count, type: .Url)) + text.append(link) + text.append("\n") + } + text.removeLast() + if !textEntities.isEmpty { + attributes.append(TextEntitiesMessageAttribute(entities: prevTextEntities)) + } + } else { + text = self.presentationData.strings.Channel_AdminLog_EmptyMessageText + attributes.append(TextEntitiesMessageAttribute(entities: [MessageTextEntity(range: 0 ..< text.count, type: .Italic)])) + } + + let prevMessage = Message(stableId: 0, 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: [], customTags: [], forwardInfo: nil, author: author, text: prevText, attributes: previousAttributes, media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + 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: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.isEmpty ? .eventLogPreviousLink(prevMessage) : nil) + } + case let .changePhoto(_, new): + var peers = SimpleDictionary() + var author: Peer? + if let peer = self.entry.peers[self.entry.event.peerId] { + author = peer + peers[peer.id] = peer + } + peers[peer.id] = peer + + var photo: TelegramMediaImage? + let (newPhoto, newVideo) = new + if !newPhoto.isEmpty || !newVideo.isEmpty { + photo = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: newPhoto, videoRepresentations: newVideo, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: []) + } + + let action = TelegramMediaActionType.photoUpdated(image: photo) + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .toggleInvites(value): + var peers = SimpleDictionary() + 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 value { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageToggleInvitesOn(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)] + } + return [] + }, to: &text, entities: &entities) + } else { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageToggleInvitesOff(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)] + } + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case .toggleSignatures(let value), .toggleSignatureProfiles(let value): + var peers = SimpleDictionary() + 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 value { + let pattern: (String) -> PresentationStrings.FormattedString + if case .toggleSignatureProfiles = self.entry.event.action { + pattern = self.presentationData.strings.Channel_AdminLog_MessageToggleProfileSignaturesOn + } else { + pattern = self.presentationData.strings.Channel_AdminLog_MessageToggleSignaturesOn + } + + appendAttributedText(text: pattern(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)] + } + return [] + }, to: &text, entities: &entities) + } else { + let pattern: (String) -> PresentationStrings.FormattedString + if case .toggleSignatureProfiles = self.entry.event.action { + pattern = self.presentationData.strings.Channel_AdminLog_MessageToggleProfileSignaturesOff + } else { + pattern = self.presentationData.strings.Channel_AdminLog_MessageToggleSignaturesOff + } + + appendAttributedText(text: pattern(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)] + } + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .updatePinned(message): + switch self.id.contentIndex { + case .header: + var peers = SimpleDictionary() + var author: Peer? + if self.entry.event.peerId == PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(136817688)) { + author = message?.effectiveAuthor + } else if let peer = self.entry.peers[self.entry.event.peerId] { + author = peer + peers[peer.id] = peer + } + var text: String = "" + var entities: [MessageTextEntity] = [] + + let textFormat: (String) -> PresentationStrings.FormattedString + if let message = message, message.tags.contains(.pinned) { + textFormat = self.presentationData.strings.Channel_AdminLog_MessagePinned + } else { + textFormat = self.presentationData.strings.Channel_AdminLog_MessageUnpinnedExtended + } + + appendAttributedText(text: textFormat(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)] + } + return [] + }, to: &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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case .content: + if let message = message { + var peers = SimpleDictionary() + var attributes: [MessageAttribute] = [] + for attribute in message.attributes { + if let attribute = attribute as? TextEntitiesMessageAttribute { + attributes.append(attribute) + } + if let attribute = attribute as? ReplyMessageAttribute { + attributes.append(attribute) + } + } + for attribute in attributes { + for peerId in attribute.associatedPeerIds { + if let peer = self.entry.peers[peerId] { + peers[peer.id] = peer + } + } + } + if let peer = self.entry.peers[message.id.peerId] { + peers[peer.id] = peer + } + 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: message.threadId, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: message.associatedThreadInfo, associatedStories: [:]) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + } else { + var peers = SimpleDictionary() + 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] = [] + + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageUnpinned(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)] + } + 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: 0), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + } + } + case let .editMessage(prev, message): + switch self.id.contentIndex { + case .header: + var peers = SimpleDictionary() var author: Peer? if let peer = self.entry.peers[self.entry.event.peerId] { author = peer peers[peer.id] = peer } - peers[peer.id] = filterMessageChannelPeer(peer) var text: String = "" var entities: [MessageTextEntity] = [] - let isBroadcast: Bool - if let peer = peer as? TelegramChannel { - switch peer.info { - case .broadcast: - isBroadcast = true - case .group: - isBroadcast = false + var mediaUpdated = false + if prev.media.count == message.media.count { + for i in 0 ..< prev.media.count { + if !prev.media[i].isEqual(to: message.media[i]) { + mediaUpdated = true + break + } } } else { - isBroadcast = false + mediaUpdated = true } - if case let .member(_, _, _, prevBanInfo, _, _) = prev.participant { - if case let .member(_, _, _, newBanInfo, _, _) = new.participant { - let newFlags = newBanInfo?.rights.flags ?? [] - - var addedRights = newBanInfo?.rights.flags ?? [] - var removedRights:TelegramChatBannedRightsFlags = [] - if let prevBanInfo = prevBanInfo { - addedRights = addedRights.subtracting(prevBanInfo.rights.flags) - removedRights = prevBanInfo.rights.flags.subtracting(newBanInfo?.rights.flags ?? []) - } - - if (prevBanInfo == nil || !prevBanInfo!.rights.flags.contains(.banReadMessages)) && newFlags.contains(.banReadMessages) { - appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageKickedName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessageKickedNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in - var result: [MessageTextEntityType] = [] - if index == 0 { - result.append(.TextMention(peerId: new.peer.id)) - } else if index == 1 { - result.append(.Mention) - } - return result - }, to: &text, entities: &entities) - text += "\n" - } else if isBroadcast, newBanInfo == nil, prevBanInfo != nil { - appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageUnkickedName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessageUnkickedNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in - var result: [MessageTextEntityType] = [] - if index == 0 { - result.append(.TextMention(peerId: new.peer.id)) - } else if index == 1 { - result.append(.Mention) - } - return result - }, to: &text, entities: &entities) - } else { - appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageRestrictedName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessageRestrictedNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in - var result: [MessageTextEntityType] = [] - if index == 0 { - result.append(.TextMention(peerId: new.peer.id)) - } else if index == 1 { - result.append(.Mention) - } - return result - }, to: &text, entities: &entities) - text += "\n" - - if let newBanInfo = newBanInfo, newBanInfo.rights.untilDate != 0 && newBanInfo.rights.untilDate != Int32.max { - let formatter = DateFormatter() - formatter.locale = Locale(identifier: self.presentationData.strings.baseLanguageCode) - formatter.dateFormat = "E, d MMM HH:mm" - let dateString = formatter.string(from: Date(timeIntervalSince1970: Double(newBanInfo.rights.untilDate))) - - if prevBanInfo?.rights.flags != newBanInfo.rights.flags { - text += self.presentationData.strings.Channel_AdminLog_MessageRestrictedUntil(dateString).string - } else { - text += self.presentationData.strings.Channel_AdminLog_MessageRestrictedNewSetting(dateString).string - } - text += "\n" - } else { - if prevBanInfo?.rights.flags != newBanInfo?.rights.flags { - text += self.presentationData.strings.Channel_AdminLog_MessageRestrictedForever - } else { - text += self.presentationData.strings.Channel_AdminLog_MessageRestrictedNewSetting(self.presentationData.strings.Channel_AdminLog_MessageRestrictedForever).string - } - text += "\n" - } - - let order: [(TelegramChatBannedRightsFlags, String)] = [ - (.banReadMessages, self.presentationData.strings.Channel_AdminLog_BanReadMessages), - (.banSendText, self.presentationData.strings.Channel_AdminLog_BanSendMessages), - (.banSendMedia, self.presentationData.strings.Channel_AdminLog_BanSendMedia), - (.banSendStickers, self.presentationData.strings.Channel_AdminLog_BanSendStickersAndGifs), - (.banEmbedLinks, self.presentationData.strings.Channel_AdminLog_BanEmbedLinks), - (.banSendPolls, self.presentationData.strings.Channel_AdminLog_SendPolls), - (.banAddMembers, self.presentationData.strings.Channel_AdminLog_AddMembers), - (.banEditRank, self.presentationData.strings.Channel_AdminLog_EditRankOwn), - (.banPinMessages, self.presentationData.strings.Channel_AdminLog_PinMessages), - (.banManageTopics, self.presentationData.strings.Channel_AdminLog_ManageTopics), - (.banChangeInfo, self.presentationData.strings.Channel_AdminLog_ChangeInfo) - ] - - for (flag, string) in order { - if addedRights.contains(flag) { - text += "\n-" - appendAttributedText(text: string, withEntities: [.Italic], to: &text, entities: &entities) - } - if removedRights.contains(flag) { - text += "\n+" - appendAttributedText(text: string, withEntities: [.Italic], to: &text, entities: &entities) - } - } - } + let titleText: PresentationStrings.FormattedString + if mediaUpdated || message.media.isEmpty { + titleText = self.presentationData.strings.Channel_AdminLog_MessageEdited(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") + } else { + titleText = self.presentationData.strings.Channel_AdminLog_CaptionEdited(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") + } + + appendAttributedText(text: titleText, 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.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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case .content: + var peers = SimpleDictionary() + var attributes: [MessageAttribute] = [] + for attribute in message.attributes { + if let attribute = attribute as? TextEntitiesMessageAttribute { + attributes.append(attribute) + } + if let attribute = attribute as? ReplyMessageAttribute { + attributes.append(attribute) } } - - if !entities.isEmpty { - attributes.append(TextEntitiesMessageAttribute(entities: entities)) - } - for attribute in attributes { for peerId in attribute.associatedPeerIds { if let peer = self.entry.peers[peerId] { @@ -808,63 +528,463 @@ 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: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .participantToggleAdmin(prev, new): + if let peer = self.entry.peers[message.id.peerId] { + peers[peer.id] = peer + } + 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: message.threadId, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: message.associatedMessages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: message.associatedThreadInfo, associatedStories: [:]) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: filterOriginalMessageFlags(message), read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: !prev.text.isEmpty || !message.text.isEmpty ? .eventLogPreviousMessage(filterOriginalMessageFlags(prev)) : nil) + } + case let .deleteMessage(message): + switch self.id.contentIndex { + case .header: var peers = SimpleDictionary() - var attributes: [MessageAttribute] = [] - var author: Peer? if let peer = self.entry.peers[self.entry.event.peerId] { author = peer peers[peer.id] = peer } - peers[peer.id] = filterMessageChannelPeer(peer) + peers[peer.id] = peer var text: String = "" var entities: [MessageTextEntity] = [] - if case .member = prev.participant, case .creator = new.participant { - appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageTransferedName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessageTransferedNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in - var result: [MessageTextEntityType] = [] - if index == 0 { - result.append(.TextMention(peerId: new.peer.id)) - } else if index == 1 { - result.append(.Mention) + let authorName = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" + + if !self.subEntries.isEmpty { + var peers: [EnginePeer] = [] + var existingPeerIds = Set() + for entry in self.subEntries { + if case let .deleteMessage(message) = entry.event.action, let author = message.author { + guard !existingPeerIds.contains(author.id) else { + continue + } + peers.append(EnginePeer(author)) + existingPeerIds.insert(author.id) } - return result + } + let peerNames = peers.map { $0.compactDisplayTitle }.joined(separator: ", ") + let messagesString = self.presentationData.strings.Channel_AdminLog_MessageManyDeleted_Messages(Int32(self.subEntries.count)) + + let fullText: PresentationStrings.FormattedString + if let isExpanded = self.isExpanded { + let moreText = (isExpanded ? self.presentationData.strings.Channel_AdminLog_MessageManyDeleted_HideAll : self.presentationData.strings.Channel_AdminLog_MessageManyDeleted_ShowAll).replacingOccurrences(of: " ", with: "\u{00A0}") + fullText = self.presentationData.strings.Channel_AdminLog_MessageManyDeletedMore(authorName, messagesString, peerNames, moreText) + } else { + fullText = self.presentationData.strings.Channel_AdminLog_MessageManyDeleted(authorName, messagesString, peerNames) + } + + appendAttributedText(text: fullText, generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } else if index == 3 { + return [.Custom(type: ApplicationSpecificEntityType.Button)] + } + return [] }, to: &text, entities: &entities) } else { - var appendedRightsHeader = false - - if case let .creator(_, prevAdminInfo, prevRank) = prev.participant, case let .creator(_, newAdminInfo, newRank) = new.participant, (prevRank != newRank || prevAdminInfo?.rights.rights.contains(.canBeAnonymous) != newAdminInfo?.rights.rights.contains(.canBeAnonymous)) { - if prevRank != newRank { - appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageRankName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), newRank ?? "") : self.presentationData.strings.Channel_AdminLog_MessageRankUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!, newRank ?? ""), generateEntities: { index in - var result: [MessageTextEntityType] = [] - if index == 0 { - result.append(.TextMention(peerId: new.peer.id)) - } else if index == 1 { - result.append(.Mention) - } else if index == 2 { - result.append(.Bold) - } - return result - }, to: &text, entities: &entities) + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageDeleted(authorName), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] } - if prevAdminInfo?.rights.rights.contains(.canBeAnonymous) != newAdminInfo?.rights.rights.contains(.canBeAnonymous) { - let order: [(TelegramChatAdminRightsFlags, String)] - - if let peer = peer as? TelegramChannel, case .broadcast = peer.info { - order = [] - } else { - order = [ - (.canBeAnonymous, self.presentationData.strings.Channel_AdminLog_CanBeAnonymous) - ] + return [] + }, to: &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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case .content: + var peers = SimpleDictionary() + var attributes: [MessageAttribute] = [] + for attribute in message.attributes { + if let attribute = attribute as? TextEntitiesMessageAttribute { + attributes.append(attribute) + } + if let attribute = attribute as? ReplyMessageAttribute { + attributes.append(attribute) + } + } + for attribute in attributes { + for peerId in attribute.associatedPeerIds { + if let peer = self.entry.peers[peerId] { + peers[peer.id] = peer + } + } + } + for media in message.media { + for peerId in media.peerIds { + if let peer = self.entry.peers[peerId] { + peers[peer.id] = peer + } + } + } + if let peer = self.entry.peers[self.entry.event.peerId] { + peers[peer.id] = peer + } + if let peer = self.entry.peers[message.id.peerId] { + peers[peer.id] = peer + } + + var additionalContent: ChatMessageItemAdditionalContent? + if !self.subEntries.isEmpty { + var messages: [Message] = [] + for entry in self.subEntries { + if case let .deleteMessage(message) = entry.event.action { + messages.append(message) + } + } + var hasButton = false + if let isExpanded = self.isExpanded, !isExpanded { + hasButton = true + } + additionalContent = .eventLogGroupedMessages(messages, hasButton) + } + + let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: message.id.id), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: message.threadId, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: message.associatedMessages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: message.associatedThreadInfo, associatedStories: [:]) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: additionalContent) + } + case .participantJoin, .participantLeave: + var peers = SimpleDictionary() + var author: Peer? + if let peer = self.entry.peers[self.entry.event.peerId] { + author = peer + peers[peer.id] = peer + } + peers[peer.id] = peer + + let action: TelegramMediaActionType + if case .participantJoin = self.entry.event.action { + action = TelegramMediaActionType.addedMembers(peerIds: [self.entry.event.peerId]) + } else { + action = TelegramMediaActionType.removedMembers(peerIds: [self.entry.event.peerId]) + } + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .participantInvite(participant): + var peers = SimpleDictionary() + var author: Peer? + if let peer = self.entry.peers[self.entry.event.peerId] { + author = peer + peers[peer.id] = peer + } + peers[peer.id] = peer + for (_, peer) in participant.peers { + peers[peer.id] = peer + } + peers[participant.peer.id] = participant.peer + + let action: TelegramMediaActionType + action = TelegramMediaActionType.addedMembers(peerIds: [participant.peer.id]) + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .participantToggleBan(prev, new): + var peers = SimpleDictionary() + var attributes: [MessageAttribute] = [] + + var author: Peer? + if let peer = self.entry.peers[self.entry.event.peerId] { + author = peer + peers[peer.id] = peer + } + peers[peer.id] = filterMessageChannelPeer(peer) + + var text: String = "" + var entities: [MessageTextEntity] = [] + + let isBroadcast: Bool + if let peer = peer as? TelegramChannel { + switch peer.info { + case .broadcast: + isBroadcast = true + case .group: + isBroadcast = false + } + } else { + isBroadcast = false + } + + if case let .member(_, _, _, prevBanInfo, _, _) = prev.participant { + if case let .member(_, _, _, newBanInfo, _, _) = new.participant { + let newFlags = newBanInfo?.rights.flags ?? [] + + var addedRights = newBanInfo?.rights.flags ?? [] + var removedRights:TelegramChatBannedRightsFlags = [] + if let prevBanInfo = prevBanInfo { + addedRights = addedRights.subtracting(prevBanInfo.rights.flags) + removedRights = prevBanInfo.rights.flags.subtracting(newBanInfo?.rights.flags ?? []) + } + + if (prevBanInfo == nil || !prevBanInfo!.rights.flags.contains(.banReadMessages)) && newFlags.contains(.banReadMessages) { + appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageKickedName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessageKickedNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in + var result: [MessageTextEntityType] = [] + if index == 0 { + result.append(.TextMention(peerId: new.peer.id)) + } else if index == 1 { + result.append(.Mention) } + return result + }, to: &text, entities: &entities) + text += "\n" + } else if isBroadcast, newBanInfo == nil, prevBanInfo != nil { + appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageUnkickedName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessageUnkickedNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in + var result: [MessageTextEntityType] = [] + if index == 0 { + result.append(.TextMention(peerId: new.peer.id)) + } else if index == 1 { + result.append(.Mention) + } + return result + }, to: &text, entities: &entities) + } else { + appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageRestrictedName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessageRestrictedNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in + var result: [MessageTextEntityType] = [] + if index == 0 { + result.append(.TextMention(peerId: new.peer.id)) + } else if index == 1 { + result.append(.Mention) + } + return result + }, to: &text, entities: &entities) + text += "\n" + + if let newBanInfo = newBanInfo, newBanInfo.rights.untilDate != 0 && newBanInfo.rights.untilDate != Int32.max { + let formatter = DateFormatter() + formatter.locale = Locale(identifier: self.presentationData.strings.baseLanguageCode) + formatter.dateFormat = "E, d MMM HH:mm" + let dateString = formatter.string(from: Date(timeIntervalSince1970: Double(newBanInfo.rights.untilDate))) - var appendedRightsHeader = false + if prevBanInfo?.rights.flags != newBanInfo.rights.flags { + text += self.presentationData.strings.Channel_AdminLog_MessageRestrictedUntil(dateString).string + } else { + text += self.presentationData.strings.Channel_AdminLog_MessageRestrictedNewSetting(dateString).string + } + text += "\n" + } else { + if prevBanInfo?.rights.flags != newBanInfo?.rights.flags { + text += self.presentationData.strings.Channel_AdminLog_MessageRestrictedForever + } else { + text += self.presentationData.strings.Channel_AdminLog_MessageRestrictedNewSetting(self.presentationData.strings.Channel_AdminLog_MessageRestrictedForever).string + } + text += "\n" + } + + let order: [(TelegramChatBannedRightsFlags, String)] = [ + (.banReadMessages, self.presentationData.strings.Channel_AdminLog_BanReadMessages), + (.banSendText, self.presentationData.strings.Channel_AdminLog_BanSendMessages), + (.banSendMedia, self.presentationData.strings.Channel_AdminLog_BanSendMedia), + (.banSendStickers, self.presentationData.strings.Channel_AdminLog_BanSendStickersAndGifs), + (.banEmbedLinks, self.presentationData.strings.Channel_AdminLog_BanEmbedLinks), + (.banSendPolls, self.presentationData.strings.Channel_AdminLog_SendPolls), + (.banAddMembers, self.presentationData.strings.Channel_AdminLog_AddMembers), + (.banEditRank, self.presentationData.strings.Channel_AdminLog_EditRankOwn), + (.banPinMessages, self.presentationData.strings.Channel_AdminLog_PinMessages), + (.banManageTopics, self.presentationData.strings.Channel_AdminLog_ManageTopics), + (.banChangeInfo, self.presentationData.strings.Channel_AdminLog_ChangeInfo) + ] + + for (flag, string) in order { + if addedRights.contains(flag) { + text += "\n-" + appendAttributedText(text: string, withEntities: [.Italic], to: &text, entities: &entities) + } + if removedRights.contains(flag) { + text += "\n+" + appendAttributedText(text: string, withEntities: [.Italic], to: &text, entities: &entities) + } + } + } + } + } + + if !entities.isEmpty { + attributes.append(TextEntitiesMessageAttribute(entities: entities)) + } + + for attribute in attributes { + for peerId in attribute.associatedPeerIds { + if let peer = self.entry.peers[peerId] { + peers[peer.id] = peer + } + } + } + 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: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .participantToggleAdmin(prev, new): + var peers = SimpleDictionary() + var attributes: [MessageAttribute] = [] + + var author: Peer? + if let peer = self.entry.peers[self.entry.event.peerId] { + author = peer + peers[peer.id] = peer + } + peers[peer.id] = filterMessageChannelPeer(peer) + + var text: String = "" + var entities: [MessageTextEntity] = [] + + if case .member = prev.participant, case .creator = new.participant { + appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageTransferedName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessageTransferedNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in + var result: [MessageTextEntityType] = [] + if index == 0 { + result.append(.TextMention(peerId: new.peer.id)) + } else if index == 1 { + result.append(.Mention) + } + return result + }, to: &text, entities: &entities) + } else { + var appendedRightsHeader = false + + if case let .creator(_, prevAdminInfo, prevRank) = prev.participant, case let .creator(_, newAdminInfo, newRank) = new.participant, (prevRank != newRank || prevAdminInfo?.rights.rights.contains(.canBeAnonymous) != newAdminInfo?.rights.rights.contains(.canBeAnonymous)) { + if prevRank != newRank { + appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageRankNameNew(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), newRank ?? "") : self.presentationData.strings.Channel_AdminLog_MessageRankUsernameNew(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!, newRank ?? ""), generateEntities: { index in + var result: [MessageTextEntityType] = [] + if index == 0 { + result.append(.TextMention(peerId: new.peer.id)) + } else if index == 1 { + result.append(.Mention) + } else if index == 2 { + result.append(.Bold) + } + return result + }, to: &text, entities: &entities) + } + if prevAdminInfo?.rights.rights.contains(.canBeAnonymous) != newAdminInfo?.rights.rights.contains(.canBeAnonymous) { + let order: [(TelegramChatAdminRightsFlags, String)] + + if let peer = peer as? TelegramChannel, case .broadcast = peer.info { + order = [] + } else { + order = [ + (.canBeAnonymous, self.presentationData.strings.Channel_AdminLog_CanBeAnonymous) + ] + } + + var appendedRightsHeader = false + for (flag, string) in order { + if prevAdminInfo?.rights.rights.contains(flag) != newAdminInfo?.rights.rights.contains(flag) { + if !appendedRightsHeader { + appendedRightsHeader = true + appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessagePromotedName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessagePromotedNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in + var result: [MessageTextEntityType] = [] + if index == 0 { + result.append(.TextMention(peerId: new.peer.id)) + } else if index == 1 { + result.append(.Mention) + } else if index == 2 { + result.append(.Bold) + } + return result + }, to: &text, entities: &entities) + text += "\n" + } + + text += "\n" + if prevAdminInfo?.rights.rights.contains(flag) != true { + text += "+" + } else { + text += "-" + } + appendAttributedText(text: string, withEntities: [.Italic], to: &text, entities: &entities) + } + } + } + } else if case let .member(_, _, prevAdminRights, _, prevRank, _) = prev.participant { + if case let .member(_, _, newAdminRights, _, newRank, _) = new.participant { + var prevFlags = prevAdminRights?.rights.rights ?? [] + var newFlags = newAdminRights?.rights.rights ?? [] + + let order: [(TelegramChatAdminRightsFlags, String)] + + if let peer = peer as? TelegramChannel, case .broadcast = peer.info { + order = [ + (.canChangeInfo, self.presentationData.strings.Channel_AdminLog_CanChangeInfo), + (.canPostMessages, self.presentationData.strings.Channel_AdminLog_CanSendMessages), + (.canDeleteMessages, self.presentationData.strings.Channel_AdminLog_CanDeleteMessagesOfOthers), + (.canEditMessages, self.presentationData.strings.Channel_AdminLog_CanEditMessages), + (.canPostStories, self.presentationData.strings.Channel_AdminLog_CanPostStories), + (.canDeleteStories, self.presentationData.strings.Channel_AdminLog_CanDeleteStoriesOfOthers), + (.canEditStories, self.presentationData.strings.Channel_AdminLog_CanEditStoriesOfOthers), + (.canInviteUsers, self.presentationData.strings.Channel_AdminLog_CanInviteUsersViaLink), + (.canManageRanks, self.presentationData.strings.Channel_AdminLog_EditRank), + (.canPinMessages, self.presentationData.strings.Channel_AdminLog_CanPinMessages), + (.canAddAdmins, self.presentationData.strings.Channel_AdminLog_CanAddAdmins), + (.canManageDirect, self.presentationData.strings.Channel_AdminLog_CanManageDirect), + (.canManageCalls, self.presentationData.strings.Channel_AdminLog_CanManageLiveStreams) + ] + prevFlags = prevFlags.intersection(TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(peer))) + newFlags = newFlags.intersection(TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(peer))) + } else { + order = [ + (.canChangeInfo, self.presentationData.strings.Channel_AdminLog_CanChangeInfo), + (.canDeleteMessages, self.presentationData.strings.Channel_AdminLog_CanDeleteMessages), + (.canPostStories, self.presentationData.strings.Channel_AdminLog_CanPostStories), + (.canDeleteStories, self.presentationData.strings.Channel_AdminLog_CanDeleteStoriesOfOthers), + (.canEditStories, self.presentationData.strings.Channel_AdminLog_CanEditStoriesOfOthers), + (.canBanUsers, self.presentationData.strings.Channel_AdminLog_CanBanUsers), + (.canInviteUsers, self.presentationData.strings.Channel_AdminLog_CanInviteUsersViaLink), + (.canManageRanks, self.presentationData.strings.Channel_AdminLog_EditRank), + (.canPinMessages, self.presentationData.strings.Channel_AdminLog_CanPinMessages), + (.canManageTopics, self.presentationData.strings.Channel_AdminLog_CanManageTopics), + (.canBeAnonymous, self.presentationData.strings.Channel_AdminLog_CanBeAnonymous), + (.canAddAdmins, self.presentationData.strings.Channel_AdminLog_CanAddAdmins), + (.canManageCalls, self.presentationData.strings.Channel_AdminLog_CanManageCalls) + ] + prevFlags = prevFlags.intersection(TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(peer))) + newFlags = newFlags.intersection(TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(peer))) + } + + if prevFlags.isEmpty && newFlags.isEmpty && (prevAdminRights != nil) != (newAdminRights != nil) { + if !appendedRightsHeader { + appendedRightsHeader = true + if prevAdminRights == nil { + appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageAddedAdminName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessageAddedAdminNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in + var result: [MessageTextEntityType] = [] + if index == 0 { + result.append(.TextMention(peerId: new.peer.id)) + } else if index == 1 { + result.append(.Mention) + } else if index == 2 { + result.append(.Bold) + } + return result + }, to: &text, entities: &entities) + } else { + appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageRemovedAdminName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessageRemovedAdminNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in + var result: [MessageTextEntityType] = [] + if index == 0 { + result.append(.TextMention(peerId: new.peer.id)) + } else if index == 1 { + result.append(.Mention) + } else if index == 2 { + result.append(.Bold) + } + return result + }, to: &text, entities: &entities) + } + } + } + + if !prevFlags.isEmpty && newFlags.isEmpty { + if !appendedRightsHeader { + appendedRightsHeader = true + appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageRemovedAdminName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessageRemovedAdminNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in + var result: [MessageTextEntityType] = [] + if index == 0 { + result.append(.TextMention(peerId: new.peer.id)) + } else if index == 1 { + result.append(.Mention) + } else if index == 2 { + result.append(.Bold) + } + return result + }, to: &text, entities: &entities) + } + } else { for (flag, string) in order { - if prevAdminInfo?.rights.rights.contains(flag) != newAdminInfo?.rights.rights.contains(flag) { + if prevFlags.contains(flag) != newFlags.contains(flag) { if !appendedRightsHeader { appendedRightsHeader = true appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessagePromotedName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessagePromotedNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in @@ -882,7 +1002,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } text += "\n" - if prevAdminInfo?.rights.rights.contains(flag) != true { + if !prevFlags.contains(flag) { text += "+" } else { text += "-" @@ -891,196 +1011,170 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { } } } - } else if case let .member(_, _, prevAdminRights, _, prevRank, _) = prev.participant { - if case let .member(_, _, newAdminRights, _, newRank, _) = new.participant { - var prevFlags = prevAdminRights?.rights.rights ?? [] - var newFlags = newAdminRights?.rights.rights ?? [] - - let order: [(TelegramChatAdminRightsFlags, String)] - - if let peer = peer as? TelegramChannel, case .broadcast = peer.info { - order = [ - (.canChangeInfo, self.presentationData.strings.Channel_AdminLog_CanChangeInfo), - (.canPostMessages, self.presentationData.strings.Channel_AdminLog_CanSendMessages), - (.canDeleteMessages, self.presentationData.strings.Channel_AdminLog_CanDeleteMessagesOfOthers), - (.canEditMessages, self.presentationData.strings.Channel_AdminLog_CanEditMessages), - (.canPostStories, self.presentationData.strings.Channel_AdminLog_CanPostStories), - (.canDeleteStories, self.presentationData.strings.Channel_AdminLog_CanDeleteStoriesOfOthers), - (.canEditStories, self.presentationData.strings.Channel_AdminLog_CanEditStoriesOfOthers), - (.canInviteUsers, self.presentationData.strings.Channel_AdminLog_CanInviteUsersViaLink), - (.canPinMessages, self.presentationData.strings.Channel_AdminLog_CanPinMessages), - (.canAddAdmins, self.presentationData.strings.Channel_AdminLog_CanAddAdmins), - (.canManageDirect, self.presentationData.strings.Channel_AdminLog_CanManageDirect), - (.canManageCalls, self.presentationData.strings.Channel_AdminLog_CanManageLiveStreams) - ] - prevFlags = prevFlags.intersection(TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(peer))) - newFlags = newFlags.intersection(TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(peer))) - } else { - order = [ - (.canChangeInfo, self.presentationData.strings.Channel_AdminLog_CanChangeInfo), - (.canDeleteMessages, self.presentationData.strings.Channel_AdminLog_CanDeleteMessages), - (.canPostStories, self.presentationData.strings.Channel_AdminLog_CanPostStories), - (.canDeleteStories, self.presentationData.strings.Channel_AdminLog_CanDeleteStoriesOfOthers), - (.canEditStories, self.presentationData.strings.Channel_AdminLog_CanEditStoriesOfOthers), - (.canBanUsers, self.presentationData.strings.Channel_AdminLog_CanBanUsers), - (.canInviteUsers, self.presentationData.strings.Channel_AdminLog_CanInviteUsersViaLink), - (.canPinMessages, self.presentationData.strings.Channel_AdminLog_CanPinMessages), - (.canManageTopics, self.presentationData.strings.Channel_AdminLog_CanManageTopics), - (.canBeAnonymous, self.presentationData.strings.Channel_AdminLog_CanBeAnonymous), - (.canAddAdmins, self.presentationData.strings.Channel_AdminLog_CanAddAdmins), - (.canManageCalls, self.presentationData.strings.Channel_AdminLog_CanManageCalls) - ] - prevFlags = prevFlags.intersection(TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(peer))) - newFlags = newFlags.intersection(TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(peer))) - } - - if prevFlags.isEmpty && newFlags.isEmpty && (prevAdminRights != nil) != (newAdminRights != nil) { - if !appendedRightsHeader { - appendedRightsHeader = true - if prevAdminRights == nil { - appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageAddedAdminName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessageAddedAdminNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in - var result: [MessageTextEntityType] = [] - if index == 0 { - result.append(.TextMention(peerId: new.peer.id)) - } else if index == 1 { - result.append(.Mention) - } else if index == 2 { - result.append(.Bold) - } - return result - }, to: &text, entities: &entities) - } else { - appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageRemovedAdminName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessageRemovedAdminNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in - var result: [MessageTextEntityType] = [] - if index == 0 { - result.append(.TextMention(peerId: new.peer.id)) - } else if index == 1 { - result.append(.Mention) - } else if index == 2 { - result.append(.Bold) - } - return result - }, to: &text, entities: &entities) + + if prevRank != newRank { + if appendedRightsHeader { + text += "\n\n" + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageRankNew(newRank ?? ""), generateEntities: { index in + var result: [MessageTextEntityType] = [] + if index == 0 { + result.append(.Bold) } - } - } - - if !prevFlags.isEmpty && newFlags.isEmpty { - if !appendedRightsHeader { - appendedRightsHeader = true - appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageRemovedAdminName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessageRemovedAdminNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in - var result: [MessageTextEntityType] = [] - if index == 0 { - result.append(.TextMention(peerId: new.peer.id)) - } else if index == 1 { - result.append(.Mention) - } else if index == 2 { - result.append(.Bold) - } - return result - }, to: &text, entities: &entities) - } + return result + }, to: &text, entities: &entities) } else { - for (flag, string) in order { - if prevFlags.contains(flag) != newFlags.contains(flag) { - if !appendedRightsHeader { - appendedRightsHeader = true - appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessagePromotedName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)) : self.presentationData.strings.Channel_AdminLog_MessagePromotedNameUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!), generateEntities: { index in - var result: [MessageTextEntityType] = [] - if index == 0 { - result.append(.TextMention(peerId: new.peer.id)) - } else if index == 1 { - result.append(.Mention) - } else if index == 2 { - result.append(.Bold) - } - return result - }, to: &text, entities: &entities) - text += "\n" - } - - text += "\n" - if !prevFlags.contains(flag) { - text += "+" - } else { - text += "-" - } - appendAttributedText(text: string, withEntities: [.Italic], to: &text, entities: &entities) + appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageRankNameNew(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), newRank ?? "") : self.presentationData.strings.Channel_AdminLog_MessageRankUsernameNew(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!, newRank ?? ""), generateEntities: { index in + var result: [MessageTextEntityType] = [] + if index == 0 { + result.append(.TextMention(peerId: new.peer.id)) + } else if index == 1 { + result.append(.Mention) + } else if index == 2 { + result.append(.Bold) } - } - } - - if prevRank != newRank { - if appendedRightsHeader { - text += "\n\n" - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageRank(newRank ?? ""), generateEntities: { index in - var result: [MessageTextEntityType] = [] - if index == 0 { - result.append(.Bold) - } - return result - }, to: &text, entities: &entities) - } else { - appendAttributedText(text: new.peer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageRankName(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), newRank ?? "") : self.presentationData.strings.Channel_AdminLog_MessageRankUsername(EnginePeer(new.peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + new.peer.addressName!, newRank ?? ""), generateEntities: { index in - var result: [MessageTextEntityType] = [] - if index == 0 { - result.append(.TextMention(peerId: new.peer.id)) - } else if index == 1 { - result.append(.Mention) - } else if index == 2 { - result.append(.Bold) - } - return result - }, to: &text, entities: &entities) - } + return result + }, to: &text, entities: &entities) } } } } + } - if !entities.isEmpty { - attributes.append(TextEntitiesMessageAttribute(entities: entities)) - } - - for attribute in attributes { - for peerId in attribute.associatedPeerIds { - if let peer = self.entry.peers[peerId] { - peers[peer.id] = peer - } + if !entities.isEmpty { + attributes.append(TextEntitiesMessageAttribute(entities: entities)) + } + + for attribute in attributes { + for peerId in attribute.associatedPeerIds { + if let peer = self.entry.peers[peerId] { + peers[peer.id] = peer } } - 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: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .changeStickerPack(_, new): - var peers = SimpleDictionary() - var author: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer + } + 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: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .changeStickerPack(_, new): + var peers = SimpleDictionary() + 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 new != nil { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedGroupStickerPack(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)] + } + return [] + }, to: &text, entities: &entities) + } else { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageRemovedGroupStickerPack(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)] + } + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .togglePreHistoryHidden(value): + var peers = SimpleDictionary() + 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 !value { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageGroupPreHistoryVisible(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)] + } + return [] + }, to: &text, entities: &entities) + } else { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageGroupPreHistoryHidden(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)] + } + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .updateDefaultBannedRights(prev, new): + var peers = SimpleDictionary() + var attributes: [MessageAttribute] = [] + + var author: Peer? + if let peer = self.entry.peers[self.entry.event.peerId] { + author = peer + peers[peer.id] = peer + } + peers[peer.id] = filterMessageChannelPeer(peer) + + var text: String = "" + var entities: [MessageTextEntity] = [] + + var addedRights = new.flags + var removedRights: TelegramChatBannedRightsFlags = [] + addedRights = addedRights.subtracting(prev.flags) + removedRights = prev.flags.subtracting(new.flags) + + text += self.presentationData.strings.Channel_AdminLog_DefaultRestrictionsUpdated + text += "\n" + + let order: [(TelegramChatBannedRightsFlags, String)] = [ + (.banReadMessages, self.presentationData.strings.Channel_AdminLog_BanReadMessages), + (.banSendText, self.presentationData.strings.Channel_AdminLog_BanSendMessages), + (.banSendMedia, self.presentationData.strings.Channel_AdminLog_BanSendMedia), + (.banSendStickers, self.presentationData.strings.Channel_AdminLog_BanSendStickersAndGifs), + (.banEmbedLinks, self.presentationData.strings.Channel_AdminLog_BanEmbedLinks), + (.banSendPolls, self.presentationData.strings.Channel_AdminLog_SendPolls), + (.banAddMembers, self.presentationData.strings.Channel_AdminLog_AddMembers), + (.banEditRank, self.presentationData.strings.Channel_AdminLog_EditRank), + (.banPinMessages, self.presentationData.strings.Channel_AdminLog_PinMessages), + (.banManageTopics, self.presentationData.strings.Channel_AdminLog_ManageTopics), + (.banChangeInfo, self.presentationData.strings.Channel_AdminLog_ChangeInfo) + ] + + for (flag, string) in order { + if addedRights.contains(flag) { + text += "\n-" + appendAttributedText(text: string, withEntities: [.Italic], to: &text, entities: &entities) } - var text: String = "" - var entities: [MessageTextEntity] = [] - - if new != nil { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedGroupStickerPack(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)] - } - return [] - }, to: &text, entities: &entities) - } else { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageRemovedGroupStickerPack(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)] - } - return [] - }, to: &text, entities: &entities) + if removedRights.contains(flag) { + text += "\n+" + appendAttributedText(text: string, withEntities: [.Italic], 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .togglePreHistoryHidden(value): + } + + if !entities.isEmpty { + attributes.append(TextEntitiesMessageAttribute(entities: entities)) + } + + for attribute in attributes { + for peerId in attribute.associatedPeerIds { + if let peer = self.entry.peers[peerId] { + peers[peer.id] = peer + } + } + } + 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: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .pollStopped(message): + switch self.id.contentIndex { + case .header: var peers = SimpleDictionary() var author: Peer? if let peer = self.entry.peers[self.entry.event.peerId] { @@ -1091,76 +1185,32 @@ struct ChatRecentActionsEntry: Comparable, Identifiable { var text: String = "" var entities: [MessageTextEntity] = [] - if !value { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageGroupPreHistoryVisible(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)] - } - return [] - }, to: &text, entities: &entities) - } else { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageGroupPreHistoryHidden(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)] - } - return [] - }, to: &text, entities: &entities) - } + let titleText: PresentationStrings.FormattedString + + titleText = self.presentationData.strings.Channel_AdminLog_PollStopped(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") + + appendAttributedText(text: titleText, 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: [], customTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .updateDefaultBannedRights(prev, new): + case .content: var peers = SimpleDictionary() var attributes: [MessageAttribute] = [] - - var author: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer - } - peers[peer.id] = filterMessageChannelPeer(peer) - - var text: String = "" - var entities: [MessageTextEntity] = [] - - var addedRights = new.flags - var removedRights: TelegramChatBannedRightsFlags = [] - addedRights = addedRights.subtracting(prev.flags) - removedRights = prev.flags.subtracting(new.flags) - - text += self.presentationData.strings.Channel_AdminLog_DefaultRestrictionsUpdated - text += "\n" - - let order: [(TelegramChatBannedRightsFlags, String)] = [ - (.banReadMessages, self.presentationData.strings.Channel_AdminLog_BanReadMessages), - (.banSendText, self.presentationData.strings.Channel_AdminLog_BanSendMessages), - (.banSendMedia, self.presentationData.strings.Channel_AdminLog_BanSendMedia), - (.banSendStickers, self.presentationData.strings.Channel_AdminLog_BanSendStickersAndGifs), - (.banEmbedLinks, self.presentationData.strings.Channel_AdminLog_BanEmbedLinks), - (.banSendPolls, self.presentationData.strings.Channel_AdminLog_SendPolls), - (.banAddMembers, self.presentationData.strings.Channel_AdminLog_AddMembers), - (.banEditRank, self.presentationData.strings.Channel_AdminLog_EditRank), - (.banPinMessages, self.presentationData.strings.Channel_AdminLog_PinMessages), - (.banManageTopics, self.presentationData.strings.Channel_AdminLog_ManageTopics), - (.banChangeInfo, self.presentationData.strings.Channel_AdminLog_ChangeInfo) - ] - - for (flag, string) in order { - if addedRights.contains(flag) { - text += "\n-" - appendAttributedText(text: string, withEntities: [.Italic], to: &text, entities: &entities) + for attribute in message.attributes { + if let attribute = attribute as? TextEntitiesMessageAttribute { + attributes.append(attribute) } - if removedRights.contains(flag) { - text += "\n+" - appendAttributedText(text: string, withEntities: [.Italic], to: &text, entities: &entities) + if let attribute = attribute as? ReplyMessageAttribute { + attributes.append(attribute) } } - - if !entities.isEmpty { - attributes.append(TextEntitiesMessageAttribute(entities: entities)) - } - for attribute in attributes { for peerId in attribute.associatedPeerIds { if let peer = self.entry.peers[peerId] { @@ -1168,857 +1218,809 @@ 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: [], customTags: [], forwardInfo: nil, author: author, text: text, attributes: attributes, media: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .pollStopped(message): - switch self.id.contentIndex { - case .header: - var peers = SimpleDictionary() - 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] = [] - - let titleText: PresentationStrings.FormattedString - - titleText = self.presentationData.strings.Channel_AdminLog_PollStopped(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") - - appendAttributedText(text: titleText, 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.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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case .content: - var peers = SimpleDictionary() - var attributes: [MessageAttribute] = [] - for attribute in message.attributes { - if let attribute = attribute as? TextEntitiesMessageAttribute { - attributes.append(attribute) - } - if let attribute = attribute as? ReplyMessageAttribute { - attributes.append(attribute) - } - } - for attribute in attributes { - for peerId in attribute.associatedPeerIds { - if let peer = self.entry.peers[peerId] { - peers[peer.id] = peer - } - } - } - if let peer = self.entry.peers[message.id.peerId] { - peers[peer.id] = peer - } - 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: [], customTags: [], forwardInfo: nil, author: message.author, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: message.associatedMessages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: message.associatedThreadInfo, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: filterOriginalMessageFlags(message), read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: nil) - } - case let .linkedPeerUpdated(previous, updated): - var peers = SimpleDictionary() - var author: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer + if let peer = self.entry.peers[message.id.peerId] { peers[peer.id] = peer } - var text: String = "" - var entities: [MessageTextEntity] = [] - - if let updated = updated { - if let peer = peer as? TelegramChannel, case .group = peer.info { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedLinkedChannel(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", EnginePeer(updated).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 [.TextMention(peerId: updated.id)] - } - return [] - }, to: &text, entities: &entities) - } else { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedLinkedGroup(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", EnginePeer(updated).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 [.TextMention(peerId: updated.id)] - } - return [] - }, to: &text, entities: &entities) - } - } else { - if let peer = peer as? TelegramChannel, case .group = peer.info { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedUnlinkedChannel(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", previous.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, let previous = previous { - return [.TextMention(peerId: previous.id)] - } - return [] - }, to: &text, entities: &entities) - } else { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedUnlinkedGroup(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", previous.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 == 0, let previous = previous { - return [.TextMention(peerId: previous.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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .changeGeoLocation(_, updated): - var peers = SimpleDictionary() - 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 updated = updated { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedGroupGeoLocation(updated.address.replacingOccurrences(of: "\n", with: ", ")), generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } - return [] - }, to: &text, entities: &entities) - - let mediaMap = TelegramMediaMap(latitude: updated.latitude, longitude: updated.longitude, heading: nil, accuracyRadius: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, 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, 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - } - case let .updateSlowmode(_, newValue): - var peers = SimpleDictionary() - 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 newValue = newValue { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_SetSlowmode(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", shortTimeIntervalString(strings: self.presentationData.strings, value: newValue)), generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } - return [] - }, to: &text, entities: &entities) - } else { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_DisabledSlowmode(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)] - } - 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case .startGroupCall, .endGroupCall: - var peers = SimpleDictionary() - 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] = [] - - let rawText: PresentationStrings.FormattedString - if case .startGroupCall = self.entry.event.action { - if let peer = peer as? TelegramChannel, case .broadcast = peer.info { - rawText = self.presentationData.strings.Channel_AdminLog_StartedLiveStream(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") - } else { - rawText = self.presentationData.strings.Channel_AdminLog_StartedVoiceChat(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") - } - } else { - if let peer = peer as? TelegramChannel, case .broadcast = peer.info { - rawText = self.presentationData.strings.Channel_AdminLog_EndedLiveStream(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") - } else { - rawText = self.presentationData.strings.Channel_AdminLog_EndedVoiceChat(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, 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .groupCallUpdateParticipantMuteStatus(participantId, isMuted): - var peers = SimpleDictionary() - var author: Peer? - var participant: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer - } - if let participantPeer = self.entry.peers[participantId] { - participant = participantPeer - peers[peer.id] = participantPeer - } - - var text: String = "" - var entities: [MessageTextEntity] = [] - - let rawText: PresentationStrings.FormattedString - if isMuted { - rawText = self.presentationData.strings.Channel_AdminLog_MutedParticipant(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", participant.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") - } else { - rawText = self.presentationData.strings.Channel_AdminLog_UnmutedMutedParticipant(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", participant.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, 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .updateGroupCallSettings(joinMuted): - var peers = SimpleDictionary() - 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] = [] - - let rawText: PresentationStrings.FormattedString - if joinMuted { - rawText = self.presentationData.strings.Channel_AdminLog_MutedNewMembers(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") - } else { - rawText = self.presentationData.strings.Channel_AdminLog_AllowedNewMembersToSpeak(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, 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .groupCallUpdateParticipantVolume(participantId, volume): - var peers = SimpleDictionary() - var author: Peer? - var participant: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer - } - if let participantPeer = self.entry.peers[participantId] { - participant = participantPeer - peers[peer.id] = participantPeer - } - - var text: String = "" - var entities: [MessageTextEntity] = [] - - let rawText: PresentationStrings.FormattedString = self.presentationData.strings.Channel_AdminLog_UpdatedParticipantVolume(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", participant.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", "\(volume / 100)%") - - appendAttributedText(text: rawText, generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } else if index == 1 { - return [.Bold] - } - 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .deleteExportedInvitation(invite): - var peers = SimpleDictionary() - 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] = [] - - let rawText: PresentationStrings.FormattedString = self.presentationData.strings.Channel_AdminLog_DeletedInviteLink(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", invite.link?.replacingOccurrences(of: "https://", with: "") ?? "") - - appendAttributedText(text: rawText, generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } else if index == 1 { - return [.Bold] - } - 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .revokeExportedInvitation(invite): - var peers = SimpleDictionary() - 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] = [] - - let rawText: PresentationStrings.FormattedString = self.presentationData.strings.Channel_AdminLog_RevokedInviteLink(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", invite.link?.replacingOccurrences(of: "https://", with: "") ?? "") - - appendAttributedText(text: rawText, generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } else if index == 1 { - return [.Bold] - } - 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .editExportedInvitation(_, updatedInvite): - var peers = SimpleDictionary() - 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] = [] - - let rawText: PresentationStrings.FormattedString = self.presentationData.strings.Channel_AdminLog_EditedInviteLink(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", updatedInvite.link?.replacingOccurrences(of: "https://", with: "") ?? "") - - appendAttributedText(text: rawText, generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } else if index == 1 { - return [.Bold] - } - 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .participantJoinedViaInvite(invite, joinedViaFolderLink): - var peers = SimpleDictionary() - 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] = [] - - let rawText: PresentationStrings.FormattedString - if joinedViaFolderLink { - rawText = self.presentationData.strings.Channel_AdminLog_JoinedViaFolderInviteLink(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", invite.link?.replacingOccurrences(of: "https://", with: "") ?? "") - } else { - rawText = self.presentationData.strings.Channel_AdminLog_JoinedViaInviteLink(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", invite.link?.replacingOccurrences(of: "https://", with: "") ?? "") - } - - appendAttributedText(text: rawText, generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } else if index == 1 { - return [.Bold] - } - 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .changeHistoryTTL(_, updatedValue): - var peers = SimpleDictionary() - 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] = [] - - let rawText: PresentationStrings.FormattedString - if let updatedValue = updatedValue { - rawText = self.presentationData.strings.Channel_AdminLog_MessageChangedAutoremoveTimeoutSet(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", timeIntervalString(strings: self.presentationData.strings, value: updatedValue)) - } else { - rawText = self.presentationData.strings.Channel_AdminLog_MessageChangedAutoremoveTimeoutRemove(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)] - } else if index == 1 { - return [.Bold] - } - 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .changeAvailableReactions(_, updatedValue): - var peers = SimpleDictionary() - 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] = [] - - switch updatedValue { - case .all: - let rawText = self.presentationData.strings.Channel_AdminLog_ReactionsEnabled(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)] - } else if index == 1 { - return [.Bold] - } - return [] - }, to: &text, entities: &entities) - case let .limited(reactions): - let authorTitle = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" - let rawText = self.presentationData.strings.Channel_AdminLog_AllowedReactionsUpdated(authorTitle, "") - var previousIndex = 0 - let nsText = rawText.string as NSString - for range in rawText.ranges.sorted(by: { $0.range.lowerBound < $1.range.lowerBound }) { - if range.range.lowerBound > previousIndex { - text.append(nsText.substring(with: NSRange(location: previousIndex, length: range.range.lowerBound - previousIndex))) - } - if range.index == 0 { - if let author { - entities.append(MessageTextEntity(range: (text as NSString).length ..< (text as NSString).length + (authorTitle as NSString).length, type: .TextMention(peerId: author.id))) - } - text.append(authorTitle) - } else if range.index == 1 { - for reaction in reactions { - let reactionText: String - switch reaction { - case let .builtin(value): - reactionText = value - text.append(reactionText) - case let .custom(fileId): - reactionText = "." - entities.append(MessageTextEntity(range: (text as NSString).length ..< (text as NSString).length + (reactionText as NSString).length, type: .CustomEmoji(stickerPack: nil, fileId: fileId))) - text.append(reactionText) - case .stars: - break - } - } - } - previousIndex = range.range.upperBound - } - if nsText.length > previousIndex { - text.append(nsText.substring(with: NSRange(location: previousIndex, length: nsText.length - previousIndex))) - } - case .empty: - let rawText = self.presentationData.strings.Channel_AdminLog_ReactionsDisabled(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)] - } else if index == 1 { - return [.Bold] - } - 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .changeTheme(_, updatedValue): - var peers = SimpleDictionary() - 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] = [] - - let rawText: PresentationStrings.FormattedString - if let updatedValue = updatedValue { - rawText = self.presentationData.strings.Channel_AdminLog_MessageChangedThemeSet(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", updatedValue) - } else { - rawText = self.presentationData.strings.Channel_AdminLog_MessageChangedThemeRemove(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)] - } else if index == 1 { - return [.Bold] - } - 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .participantJoinByRequest(invite, approvedBy): - var peers = SimpleDictionary() - var author: Peer? - var approver: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer - } - if let peer = self.entry.peers[approvedBy] { - approver = peer - peers[approvedBy] = approver - } - - var text: String = "" - var entities: [MessageTextEntity] = [] - - let rawText: PresentationStrings.FormattedString - switch invite { - case let .link(link, _, _, _, _, _, _, _, _, _, _, _, _): - rawText = self.presentationData.strings.Channel_AdminLog_JoinedViaRequest(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", link.replacingOccurrences(of: "https://", with: ""), approver.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") - case .publicJoinRequest: - rawText = self.presentationData.strings.Channel_AdminLog_JoinedViaPublicRequest(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", approver.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)] - } else if index == 1 { - return [.Bold] - } else if index == 2, let approver = approver { - return [.TextMention(peerId: approver.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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .toggleCopyProtection(value): - var peers = SimpleDictionary() - 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 value { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageToggleNoForwardsOn(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)] - } - return [] - }, to: &text, entities: &entities) - } else { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageToggleNoForwardsOff(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)] - } - 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .sendMessage(message): - switch self.id.contentIndex { - case .header: - var peers = SimpleDictionary() - var author: Peer? - if self.entry.event.peerId == PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(136817688)) { - author = message.effectiveAuthor - } else if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer - } - var text: String = "" - var entities: [MessageTextEntity] = [] - - let textFormat = self.presentationData.strings.Channel_AdminLog_MessageSent - appendAttributedText(text: textFormat(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)] - } - return [] - }, to: &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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case .content: - var peers = SimpleDictionary() - var attributes: [MessageAttribute] = [] - for attribute in message.attributes { - if let attribute = attribute as? TextEntitiesMessageAttribute { - attributes.append(attribute) - } - if let attribute = attribute as? ReplyMessageAttribute { - attributes.append(attribute) - } - } - for attribute in attributes { - for peerId in attribute.associatedPeerIds { - if let peer = self.entry.peers[peerId] { - peers[peer.id] = peer - } - } - } - if let peer = self.entry.peers[message.id.peerId] { - peers[peer.id] = peer - } - 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: message.threadId, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: message.associatedMessages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: message.associatedThreadInfo, associatedStories: [:]) - return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: 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: [], customTags: [], forwardInfo: nil, author: message.author, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: message.associatedMessages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: message.associatedThreadInfo, associatedStories: [:]) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: filterOriginalMessageFlags(message), read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil), additionalContent: nil) } - case let .createTopic(info): - var peers = SimpleDictionary() - 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] = [] - - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicCreated(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", info.title), 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .deleteTopic(info): - var peers = SimpleDictionary() - 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] = [] + case let .linkedPeerUpdated(previous, updated): + var peers = SimpleDictionary() + 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] = [] - let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicDeleted(authorTitle, info.title), 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .editTopic(prevInfo, newInfo): - var peers = SimpleDictionary() - 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] = [] - - let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" - if prevInfo.isHidden != newInfo.isHidden { - appendAttributedText(text: newInfo.isHidden ? self.presentationData.strings.Channel_AdminLog_TopicHidden(authorTitle, newInfo.info.title) : self.presentationData.strings.Channel_AdminLog_TopicUnhidden(authorTitle, newInfo.info.title), generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } - return [] - }, to: &text, entities: &entities) - } else if prevInfo.isClosed != newInfo.isClosed { - appendAttributedText(text: newInfo.isClosed ? self.presentationData.strings.Channel_AdminLog_TopicClosed(authorTitle, newInfo.info.title) : self.presentationData.strings.Channel_AdminLog_TopicReopened(authorTitle, newInfo.info.title), generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } - return [] - }, to: &text, entities: &entities) - } else if prevInfo.info.title != newInfo.info.title && prevInfo.info.icon != newInfo.info.icon { - if let fileId = newInfo.info.icon { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicRenamedWithIcon(authorTitle, prevInfo.info.title, newInfo.info.title, "."), generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } else if index == 3 { - return [.CustomEmoji(stickerPack: nil, fileId: fileId)] - } - return [] - }, to: &text, entities: &entities) - } else { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicRenamedWithRemovedIcon(authorTitle, prevInfo.info.title, newInfo.info.title), generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } - return [] - }, to: &text, entities: &entities) - } - } else if prevInfo.info.icon != newInfo.info.icon { - if let fileId = newInfo.info.icon { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicChangedIcon(authorTitle, newInfo.info.title, "."), generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } else if index == 2 { - return [.CustomEmoji(stickerPack: nil, fileId: fileId)] - } - return [] - }, to: &text, entities: &entities) - } else { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicRemovedIcon(authorTitle, newInfo.info.title), generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } - return [] - }, to: &text, entities: &entities) - } - } else { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicRenamed(authorTitle, prevInfo.info.title, newInfo.info.title), 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .pinTopic(prevInfo, newInfo): - var peers = SimpleDictionary() - 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] = [] - - let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" - - if let newInfo = newInfo { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicPinned(authorTitle, newInfo.title), generateEntities: { index in - if index == 0, let author = author { - return [.TextMention(peerId: author.id)] - } - return [] - }, to: &text, entities: &entities) - } else if let prevInfo = prevInfo { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicUnpinned(authorTitle, prevInfo.title), generateEntities: { index in + if let updated = updated { + if let peer = peer as? TelegramChannel, case .group = peer.info { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedLinkedChannel(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", EnginePeer(updated).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 [.TextMention(peerId: updated.id)] } return [] }, to: &text, entities: &entities) } else { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicUnpinned(authorTitle, ""), generateEntities: { index in + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedLinkedGroup(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", EnginePeer(updated).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 [.TextMention(peerId: updated.id)] + } + return [] + }, to: &text, entities: &entities) + } + } else { + if let peer = peer as? TelegramChannel, case .group = peer.info { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedUnlinkedChannel(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", previous.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, let previous = previous { + return [.TextMention(peerId: previous.id)] + } + return [] + }, to: &text, entities: &entities) + } else { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedUnlinkedGroup(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", previous.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 == 0, let previous = previous { + return [.TextMention(peerId: previous.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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .changeGeoLocation(_, updated): + var peers = SimpleDictionary() + 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 updated = updated { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageChangedGroupGeoLocation(updated.address.replacingOccurrences(of: "\n", with: ", ")), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } + return [] + }, to: &text, entities: &entities) + + let mediaMap = TelegramMediaMap(latitude: updated.latitude, longitude: updated.longitude, heading: nil, accuracyRadius: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, 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, 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + } + case let .updateSlowmode(_, newValue): + var peers = SimpleDictionary() + 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 newValue = newValue { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_SetSlowmode(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", shortTimeIntervalString(strings: self.presentationData.strings, value: newValue)), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } + return [] + }, to: &text, entities: &entities) + } else { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_DisabledSlowmode(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)] + } + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case .startGroupCall, .endGroupCall: + var peers = SimpleDictionary() + 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] = [] + + let rawText: PresentationStrings.FormattedString + if case .startGroupCall = self.entry.event.action { + if let peer = peer as? TelegramChannel, case .broadcast = peer.info { + rawText = self.presentationData.strings.Channel_AdminLog_StartedLiveStream(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") + } else { + rawText = self.presentationData.strings.Channel_AdminLog_StartedVoiceChat(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") + } + } else { + if let peer = peer as? TelegramChannel, case .broadcast = peer.info { + rawText = self.presentationData.strings.Channel_AdminLog_EndedLiveStream(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") + } else { + rawText = self.presentationData.strings.Channel_AdminLog_EndedVoiceChat(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, 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .groupCallUpdateParticipantMuteStatus(participantId, isMuted): + var peers = SimpleDictionary() + var author: Peer? + var participant: Peer? + if let peer = self.entry.peers[self.entry.event.peerId] { + author = peer + peers[peer.id] = peer + } + if let participantPeer = self.entry.peers[participantId] { + participant = participantPeer + peers[peer.id] = participantPeer + } + + var text: String = "" + var entities: [MessageTextEntity] = [] + + let rawText: PresentationStrings.FormattedString + if isMuted { + rawText = self.presentationData.strings.Channel_AdminLog_MutedParticipant(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", participant.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") + } else { + rawText = self.presentationData.strings.Channel_AdminLog_UnmutedMutedParticipant(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", participant.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, 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .updateGroupCallSettings(joinMuted): + var peers = SimpleDictionary() + 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] = [] + + let rawText: PresentationStrings.FormattedString + if joinMuted { + rawText = self.presentationData.strings.Channel_AdminLog_MutedNewMembers(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") + } else { + rawText = self.presentationData.strings.Channel_AdminLog_AllowedNewMembersToSpeak(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, 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .groupCallUpdateParticipantVolume(participantId, volume): + var peers = SimpleDictionary() + var author: Peer? + var participant: Peer? + if let peer = self.entry.peers[self.entry.event.peerId] { + author = peer + peers[peer.id] = peer + } + if let participantPeer = self.entry.peers[participantId] { + participant = participantPeer + peers[peer.id] = participantPeer + } + + var text: String = "" + var entities: [MessageTextEntity] = [] + + let rawText: PresentationStrings.FormattedString = self.presentationData.strings.Channel_AdminLog_UpdatedParticipantVolume(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", participant.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", "\(volume / 100)%") + + appendAttributedText(text: rawText, generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } else if index == 1 { + return [.Bold] + } + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .deleteExportedInvitation(invite): + var peers = SimpleDictionary() + 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] = [] + + let rawText: PresentationStrings.FormattedString = self.presentationData.strings.Channel_AdminLog_DeletedInviteLink(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", invite.link?.replacingOccurrences(of: "https://", with: "") ?? "") + + appendAttributedText(text: rawText, generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } else if index == 1 { + return [.Bold] + } + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .revokeExportedInvitation(invite): + var peers = SimpleDictionary() + 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] = [] + + let rawText: PresentationStrings.FormattedString = self.presentationData.strings.Channel_AdminLog_RevokedInviteLink(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", invite.link?.replacingOccurrences(of: "https://", with: "") ?? "") + + appendAttributedText(text: rawText, generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } else if index == 1 { + return [.Bold] + } + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .editExportedInvitation(_, updatedInvite): + var peers = SimpleDictionary() + 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] = [] + + let rawText: PresentationStrings.FormattedString = self.presentationData.strings.Channel_AdminLog_EditedInviteLink(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", updatedInvite.link?.replacingOccurrences(of: "https://", with: "") ?? "") + + appendAttributedText(text: rawText, generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } else if index == 1 { + return [.Bold] + } + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .participantJoinedViaInvite(invite, joinedViaFolderLink): + var peers = SimpleDictionary() + 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] = [] + + let rawText: PresentationStrings.FormattedString + if joinedViaFolderLink { + rawText = self.presentationData.strings.Channel_AdminLog_JoinedViaFolderInviteLink(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", invite.link?.replacingOccurrences(of: "https://", with: "") ?? "") + } else { + rawText = self.presentationData.strings.Channel_AdminLog_JoinedViaInviteLink(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", invite.link?.replacingOccurrences(of: "https://", with: "") ?? "") + } + + appendAttributedText(text: rawText, generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } else if index == 1 { + return [.Bold] + } + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .changeHistoryTTL(_, updatedValue): + var peers = SimpleDictionary() + 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] = [] + + let rawText: PresentationStrings.FormattedString + if let updatedValue = updatedValue { + rawText = self.presentationData.strings.Channel_AdminLog_MessageChangedAutoremoveTimeoutSet(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", timeIntervalString(strings: self.presentationData.strings, value: updatedValue)) + } else { + rawText = self.presentationData.strings.Channel_AdminLog_MessageChangedAutoremoveTimeoutRemove(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)] + } else if index == 1 { + return [.Bold] + } + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .changeAvailableReactions(_, updatedValue): + var peers = SimpleDictionary() + 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] = [] + + switch updatedValue { + case .all: + let rawText = self.presentationData.strings.Channel_AdminLog_ReactionsEnabled(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)] + } else if index == 1 { + return [.Bold] + } + return [] + }, to: &text, entities: &entities) + case let .limited(reactions): + let authorTitle = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" + let rawText = self.presentationData.strings.Channel_AdminLog_AllowedReactionsUpdated(authorTitle, "") + var previousIndex = 0 + let nsText = rawText.string as NSString + for range in rawText.ranges.sorted(by: { $0.range.lowerBound < $1.range.lowerBound }) { + if range.range.lowerBound > previousIndex { + text.append(nsText.substring(with: NSRange(location: previousIndex, length: range.range.lowerBound - previousIndex))) + } + if range.index == 0 { + if let author { + entities.append(MessageTextEntity(range: (text as NSString).length ..< (text as NSString).length + (authorTitle as NSString).length, type: .TextMention(peerId: author.id))) + } + text.append(authorTitle) + } else if range.index == 1 { + for reaction in reactions { + let reactionText: String + switch reaction { + case let .builtin(value): + reactionText = value + text.append(reactionText) + case let .custom(fileId): + reactionText = "." + entities.append(MessageTextEntity(range: (text as NSString).length ..< (text as NSString).length + (reactionText as NSString).length, type: .CustomEmoji(stickerPack: nil, fileId: fileId))) + text.append(reactionText) + case .stars: + break + } + } + } + previousIndex = range.range.upperBound + } + if nsText.length > previousIndex { + text.append(nsText.substring(with: NSRange(location: previousIndex, length: nsText.length - previousIndex))) + } + case .empty: + let rawText = self.presentationData.strings.Channel_AdminLog_ReactionsDisabled(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)] + } else if index == 1 { + return [.Bold] + } + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .changeTheme(_, updatedValue): + var peers = SimpleDictionary() + 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] = [] + + let rawText: PresentationStrings.FormattedString + if let updatedValue = updatedValue { + rawText = self.presentationData.strings.Channel_AdminLog_MessageChangedThemeSet(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", updatedValue) + } else { + rawText = self.presentationData.strings.Channel_AdminLog_MessageChangedThemeRemove(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)] + } else if index == 1 { + return [.Bold] + } + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .participantJoinByRequest(invite, approvedBy): + var peers = SimpleDictionary() + var author: Peer? + var approver: Peer? + if let peer = self.entry.peers[self.entry.event.peerId] { + author = peer + peers[peer.id] = peer + } + if let peer = self.entry.peers[approvedBy] { + approver = peer + peers[approvedBy] = approver + } + + var text: String = "" + var entities: [MessageTextEntity] = [] + + let rawText: PresentationStrings.FormattedString + switch invite { + case let .link(link, _, _, _, _, _, _, _, _, _, _, _, _): + rawText = self.presentationData.strings.Channel_AdminLog_JoinedViaRequest(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", link.replacingOccurrences(of: "https://", with: ""), approver.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "") + case .publicJoinRequest: + rawText = self.presentationData.strings.Channel_AdminLog_JoinedViaPublicRequest(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", approver.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)] + } else if index == 1 { + return [.Bold] + } else if index == 2, let approver = approver { + return [.TextMention(peerId: approver.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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .toggleCopyProtection(value): + var peers = SimpleDictionary() + 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 value { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageToggleNoForwardsOn(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)] + } + return [] + }, to: &text, entities: &entities) + } else { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageToggleNoForwardsOff(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)] + } + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .sendMessage(message): + switch self.id.contentIndex { + case .header: + var peers = SimpleDictionary() + var author: Peer? + if self.entry.event.peerId == PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(136817688)) { + author = message.effectiveAuthor + } else if let peer = self.entry.peers[self.entry.event.peerId] { + author = peer + peers[peer.id] = peer + } + var text: String = "" + var entities: [MessageTextEntity] = [] + + let textFormat = self.presentationData.strings.Channel_AdminLog_MessageSent + appendAttributedText(text: textFormat(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)] + } + return [] + }, to: &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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case .content: + var peers = SimpleDictionary() + var attributes: [MessageAttribute] = [] + for attribute in message.attributes { + if let attribute = attribute as? TextEntitiesMessageAttribute { + attributes.append(attribute) + } + if let attribute = attribute as? ReplyMessageAttribute { + attributes.append(attribute) + } + } + for attribute in attributes { + for peerId in attribute.associatedPeerIds { + if let peer = self.entry.peers[peerId] { + peers[peer.id] = peer + } + } + } + if let peer = self.entry.peers[message.id.peerId] { + peers[peer.id] = peer + } + 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: message.threadId, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: message.associatedMessages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: message.associatedThreadInfo, associatedStories: [:]) + return ChatMessageItemImpl(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadPeerId: nil, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + } + case let .createTopic(info): + var peers = SimpleDictionary() + 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] = [] + + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicCreated(author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "", info.title), 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .deleteTopic(info): + var peers = SimpleDictionary() + 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] = [] + + let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicDeleted(authorTitle, info.title), 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .editTopic(prevInfo, newInfo): + var peers = SimpleDictionary() + 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] = [] + + let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" + if prevInfo.isHidden != newInfo.isHidden { + appendAttributedText(text: newInfo.isHidden ? self.presentationData.strings.Channel_AdminLog_TopicHidden(authorTitle, newInfo.info.title) : self.presentationData.strings.Channel_AdminLog_TopicUnhidden(authorTitle, newInfo.info.title), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } + return [] + }, to: &text, entities: &entities) + } else if prevInfo.isClosed != newInfo.isClosed { + appendAttributedText(text: newInfo.isClosed ? self.presentationData.strings.Channel_AdminLog_TopicClosed(authorTitle, newInfo.info.title) : self.presentationData.strings.Channel_AdminLog_TopicReopened(authorTitle, newInfo.info.title), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } + return [] + }, to: &text, entities: &entities) + } else if prevInfo.info.title != newInfo.info.title && prevInfo.info.icon != newInfo.info.icon { + if let fileId = newInfo.info.icon { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicRenamedWithIcon(authorTitle, prevInfo.info.title, newInfo.info.title, "."), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } else if index == 3 { + return [.CustomEmoji(stickerPack: nil, fileId: fileId)] + } + return [] + }, to: &text, entities: &entities) + } else { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicRenamedWithRemovedIcon(authorTitle, prevInfo.info.title, newInfo.info.title), 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .toggleForum(isForum): - var peers = SimpleDictionary() - var author: Peer? - if let peer = self.entry.peers[self.entry.event.peerId] { - author = peer - peers[peer.id] = peer + } else if prevInfo.info.icon != newInfo.info.icon { + if let fileId = newInfo.info.icon { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicChangedIcon(authorTitle, newInfo.info.title, "."), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } else if index == 2 { + return [.CustomEmoji(stickerPack: nil, fileId: fileId)] + } + return [] + }, to: &text, entities: &entities) + } else { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicRemovedIcon(authorTitle, newInfo.info.title), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } + return [] + }, to: &text, entities: &entities) } - var text: String = "" - var entities: [MessageTextEntity] = [] - - let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" - appendAttributedText(text: isForum ? self.presentationData.strings.Channel_AdminLog_TopicsEnabled(authorTitle) : self.presentationData.strings.Channel_AdminLog_TopicsDisabled(authorTitle), generateEntities: { index in + } else { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicRenamed(authorTitle, prevInfo.info.title, newInfo.info.title), 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .toggleAntiSpam(isEnabled): - var peers = SimpleDictionary() - 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] = [] - - let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" - appendAttributedText(text: isEnabled ? self.presentationData.strings.Channel_AdminLog_AntiSpamEnabled(authorTitle) : self.presentationData.strings.Channel_AdminLog_AntiSpamDisabled(authorTitle), generateEntities: { index in + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .pinTopic(prevInfo, newInfo): + var peers = SimpleDictionary() + 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] = [] + + let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" + + if let newInfo = newInfo { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicPinned(authorTitle, newInfo.title), generateEntities: { index in if index == 0, let author = author { return [.TextMention(peerId: author.id)] } return [] }, to: &text, entities: &entities) + } else if let prevInfo = prevInfo { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicUnpinned(authorTitle, prevInfo.title), generateEntities: { index in + if index == 0, let author = author { + return [.TextMention(peerId: author.id)] + } + return [] + }, to: &text, entities: &entities) + } else { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_TopicUnpinned(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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .toggleForum(isForum): + var peers = SimpleDictionary() + 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] = [] + + let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" + appendAttributedText(text: isForum ? self.presentationData.strings.Channel_AdminLog_TopicsEnabled(authorTitle) : self.presentationData.strings.Channel_AdminLog_TopicsDisabled(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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .toggleAntiSpam(isEnabled): + var peers = SimpleDictionary() + 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] = [] + + let authorTitle: String = author.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? "" + appendAttributedText(text: isEnabled ? self.presentationData.strings.Channel_AdminLog_AntiSpamEnabled(authorTitle) : self.presentationData.strings.Channel_AdminLog_AntiSpamDisabled(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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) case let .changeNameColor(_, _, updatedColor, updatedIcon): var peers = SimpleDictionary() var author: Peer? @@ -2285,33 +2287,69 @@ 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) - case let .toggleAutoTranslation(value): - var peers = SimpleDictionary() - 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 value { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageToggleAutoTranslateOn(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)] - } - return [] - }, to: &text, entities: &entities) - } else { - appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageToggleAutoTranslateOff(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)] - } - 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .toggleAutoTranslation(value): + var peers = SimpleDictionary() + 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 value { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageToggleAutoTranslateOn(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)] + } + return [] + }, to: &text, entities: &entities) + } else { + appendAttributedText(text: self.presentationData.strings.Channel_AdminLog_MessageToggleAutoTranslateOff(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)] + } + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) + case let .editRank(peerId, _, newRank): + var peers = SimpleDictionary() + var author: Peer? + var subjectPeer: Peer? + if let peer = self.entry.peers[self.entry.event.peerId] { + author = peer + peers[peer.id] = peer + } + if peerId != self.entry.event.peerId, let peer = self.entry.peers[peerId] { + subjectPeer = peer + peers[peerId] = peer + } + + var text: String = "" + var entities: [MessageTextEntity] = [] + + if let subjectPeer { + appendAttributedText(text: subjectPeer.addressName == nil ? self.presentationData.strings.Channel_AdminLog_MessageRankNameNew(EnginePeer(subjectPeer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), newRank) : self.presentationData.strings.Channel_AdminLog_MessageRankUsernameNew(EnginePeer(subjectPeer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), "@" + subjectPeer.addressName!, newRank), generateEntities: { index in + var result: [MessageTextEntityType] = [] + if index == 0 { + result.append(.TextMention(peerId: subjectPeer.id)) + } else if index == 1 { + result.append(.Mention) + } else if index == 2 { + result.append(.Bold) + } + return result + }, to: &text, entities: &entities) + } else { + + } + + 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: [], customTags: [], 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: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: nil, defaultReaction: nil, areStarReactionsEnabled: false, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil)) } } } diff --git a/submodules/TelegramUI/Components/ChatParticipantRightsScreen/Sources/ChatParticipantRightsScreen.swift b/submodules/TelegramUI/Components/ChatParticipantRightsScreen/Sources/ChatParticipantRightsScreen.swift index 2cefec7ea3..0cf90a074a 100644 --- a/submodules/TelegramUI/Components/ChatParticipantRightsScreen/Sources/ChatParticipantRightsScreen.swift +++ b/submodules/TelegramUI/Components/ChatParticipantRightsScreen/Sources/ChatParticipantRightsScreen.swift @@ -65,42 +65,23 @@ private final class ChatParticipantRightsContent: CombinedComponent { weak var controller: ViewController? - var memberId: EnginePeer.Id { - let memberId: EnginePeer.Id - switch subject { - case let .admin(_, peerIdValue): - memberId = peerIdValue - case let .member(_, peerIdValue, _): - memberId = peerIdValue - case .rank: - memberId = self.context.account.peerId - } - return memberId - } - init(context: AccountContext, subject: ChatParticipantRightsScreen.Subject) { self.context = context self.subject = subject super.init() - let peerId: EnginePeer.Id + let participantId: EnginePeer.Id switch subject { - case let .admin(_, peerIdValue): - peerId = peerIdValue - case let .member(_, peerIdValue, rankValue): - peerId = peerIdValue - self.rank = rankValue - self.initialRank = rankValue - case let .rank(_, peerIdValue, rankValue): - peerId = peerIdValue + case let .rank(_, participantIdValue, rankValue, _): + participantId = participantIdValue self.rank = rankValue self.initialRank = rankValue } self.disposable = (context.engine.data.subscribe( - TelegramEngine.EngineData.Item.Peer.Peer(id: peerId), - TelegramEngine.EngineData.Item.Peer.Presence(id: peerId) + TelegramEngine.EngineData.Item.Peer.Peer(id: participantId), + TelegramEngine.EngineData.Item.Peer.Presence(id: participantId) ) |> deliverOnMainQueue).start(next: { [weak self] peer, presence in guard let self else { @@ -117,28 +98,10 @@ private final class ChatParticipantRightsContent: CombinedComponent { } func complete() { - var rank = self.rank - if rank?.isEmpty == true { - rank = nil - } - let _ = self.context.peerChannelMemberCategoriesContextsManager.updateMemberRank(engine: self.context.engine, peerId: self.subject.peerId, memberId: self.memberId, rank: rank).start() - - let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - if let controller = self.controller, let navigationController = controller.navigationController as? NavigationController { - Queue.mainQueue().after(0.5) { - //TODO:localize - var title: String? - var text: String - if let rank { - title = "Tag added" - text = rank - } else { - text = "Tag removed" - } - let toastController = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: title, text: text, cancel: nil, destructive: false), appearance: .init(isNarrow: true), action: { _ in return true}) - (navigationController.topViewController as? ViewController)?.present(toastController, in: .current) - } + guard let controller = self.controller as? ChatParticipantRightsScreen else { + return } + controller.complete(rank: self.rank) } } @@ -149,7 +112,6 @@ private final class ChatParticipantRightsContent: CombinedComponent { static var body: Body { let closeButton = Child(GlassBarButtonComponent.self) let title = Child(MultilineTextComponent.self) - let peerSection = Child(ListSectionComponent.self) let rankSection = Child(ListSectionComponent.self) let doneButton = Child(ButtonComponent.self) @@ -192,10 +154,6 @@ private final class ChatParticipantRightsContent: CombinedComponent { let titleString: String switch component.subject { - case .member: - titleString = "Member" - case .admin: - titleString = "Admin" case .rank: titleString = "Member Tag" } @@ -215,67 +173,35 @@ private final class ChatParticipantRightsContent: CombinedComponent { contentHeight += 44.0 let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - let itemPresentationData = ItemListPresentationData(theme: theme, fontSize: presentationData.listsFontSize, strings: strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: environment.dateTimeFormat) let listItemParams = ListViewItemLayoutParams(width: context.availableSize.width - sideInset * 2.0, leftInset: 0.0, rightInset: 0.0, availableHeight: 10000.0, isStandalone: true) - switch component.subject { - case .admin, .member: - var peerSectionItems: [AnyComponentWithIdentity] = [] - peerSectionItems.append( - AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor( - itemGenerator: ItemListAvatarAndNameInfoItem(itemContext: .accountContext(component.context), presentationData: itemPresentationData, systemStyle: .glass, dateTimeFormat: environment.dateTimeFormat, mode: .generic, peer: state.peer, presence: state.presence, memberCount: nil, state: ItemListAvatarAndNameInfoItemState(), sectionId: 0, style: .blocks(withTopInset: false, withExtendedBottomInset: false), editingNameUpdated: { _ in - }, avatarTapped: { - }), - params: listItemParams - ))) - ) - - let peerSection = peerSection.update( - component: ListSectionComponent( - theme: theme, - style: .glass, - header: nil, - footer: nil, - items: peerSectionItems - ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: context.availableSize.height), - transition: context.transition - ) - context.add(peerSection - .position(CGPoint(x: context.availableSize.width / 2.0, y: contentHeight + peerSection.size.height / 2.0)) - ) - contentHeight += peerSection.size.height - contentHeight += 38.0 - default: - break - } - let peer: EnginePeer if let current = state.peer { peer = current } else { - peer = EnginePeer.user(TelegramUser(id: EnginePeer.Id(0), accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil, verificationIconFileId: nil)) + peer = EnginePeer.user(TelegramUser(id: component.subject.participantId, accessHash: nil, firstName: " ", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil, verificationIconFileId: nil)) } var rankPreviewPlaceholder = "" var rankFooterString = "" var rankRole: ChatRankInfoScreenRole = .member switch component.subject { - case .admin: - rankFooterString = "Add short tag next to \(peer.compactDisplayTitle)'s name." - rankPreviewPlaceholder = "admin" - rankRole = .admin - case .member: - rankFooterString = "Add short tag next to \(peer.compactDisplayTitle)'s name." - rankPreviewPlaceholder = "0️⃣" - case .rank: + case let .rank(_, _, _, role): if peer.id == component.context.account.peerId { rankFooterString = "Share your role, title, or how you're known in this group. Your tag is visible to all members." } else { rankFooterString = "Add short tag next to \(peer.compactDisplayTitle)'s name." } - rankPreviewPlaceholder = "0️⃣" + switch role { + case .creator: + rankPreviewPlaceholder = strings.Conversation_Owner + case .admin: + rankPreviewPlaceholder = strings.Conversation_Admin + case .member: + rankPreviewPlaceholder = "0️⃣" + } + rankRole = role } let rankValue = state.rank ?? "" @@ -363,7 +289,7 @@ private final class ChatParticipantRightsContent: CombinedComponent { let buttonTitle: String switch component.subject { - case let .rank(_, _, initialRank): + case let .rank(_, _, initialRank, _): if (initialRank ?? "").isEmpty && (state.rank ?? "").isEmpty { buttonTitle = "Add Later" } else if (initialRank ?? "").isEmpty && !(state.rank ?? "").isEmpty { @@ -373,8 +299,6 @@ private final class ChatParticipantRightsContent: CombinedComponent { } else { buttonTitle = "Edit Tag" } - default: - buttonTitle = "Save Changes" } let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0) @@ -509,20 +433,21 @@ private final class ChatParticipantRightsComponent: CombinedComponent { public class ChatParticipantRightsScreen: ViewControllerComponentContainer { public enum Subject { - case member(peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?) - case admin(peerId: EnginePeer.Id, participantId: EnginePeer.Id) - case rank(peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?) + case rank(peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?, role: ChatRankInfoScreenRole) var peerId: EnginePeer.Id { switch self { - case let .member(peerId, _, _): - return peerId - case let .admin(peerId, _): - return peerId - case let .rank(peerId, _, _): + case let .rank(peerId, _, _, _): return peerId } } + + var participantId: EnginePeer.Id { + switch self { + case let .rank(_, participantId, _, _): + return participantId + } + } } private let context: AccountContext @@ -559,6 +484,42 @@ public class ChatParticipantRightsScreen: ViewControllerComponentContainer { self.view.disablesInteractiveModalDismiss = true } + fileprivate func complete(rank: String?) { + var rank = rank + if rank?.isEmpty == true { + rank = nil + } + + if let rank, rank.count > rankMaxLength { + if let view = self.node.hostView.findTaggedView(tag: rankFieldTag) as? ListTextFieldItemComponent.View { + view.animateError() + HapticFeedback().error() + } + return + } + + let _ = self.context.peerChannelMemberCategoriesContextsManager.updateMemberRank(engine: self.context.engine, peerId: self.subject.peerId, memberId: self.subject.participantId, rank: rank).start() + + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + if let navigationController = self.navigationController as? NavigationController { + Queue.mainQueue().after(0.5) { + //TODO:localize + var title: String? + var text: String + if let rank { + title = "Tag added" + text = rank + } else { + text = "Tag removed" + } + let toastController = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: title, text: text, cancel: nil, destructive: false), appearance: .init(isNarrow: true), action: { _ in return true}) + (navigationController.topViewController as? ViewController)?.present(toastController, in: .current) + } + } + + self.dismissAnimated() + } + public override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) diff --git a/submodules/TelegramUI/Components/ListTextFieldItemComponent/Sources/ListTextFieldItemComponent.swift b/submodules/TelegramUI/Components/ListTextFieldItemComponent/Sources/ListTextFieldItemComponent.swift index 017ec26cbf..3866013073 100644 --- a/submodules/TelegramUI/Components/ListTextFieldItemComponent/Sources/ListTextFieldItemComponent.swift +++ b/submodules/TelegramUI/Components/ListTextFieldItemComponent/Sources/ListTextFieldItemComponent.swift @@ -123,6 +123,7 @@ public final class ListTextFieldItemComponent: Component { private let textField: TextField private let placeholder = ComponentView() private let clearButton = ComponentView() + private let counter = ComponentView() private var component: ListTextFieldItemComponent? private weak var state: EmptyComponentState? @@ -178,6 +179,10 @@ public final class ListTextFieldItemComponent: Component { return false } + public func animateError() { + self.textField.layer.addShakeAnimation() + } + public func activateInput() { self.textField.becomeFirstResponder() } @@ -294,6 +299,31 @@ public final class ListTextFieldItemComponent: Component { clearButtonView.isHidden = self.currentText.isEmpty || !self.textField.isFirstResponder } + let text = self.textField.text ?? "" + if let characterLimit = component.characterLimit, text.count > 0 { + let counterSize = self.counter.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: "\(characterLimit - text.count)", font: Font.with(size: 15.0, traits: .monospacedNumbers), textColor: text.count > characterLimit ? component.theme.list.itemDestructiveColor : component.theme.list.itemSecondaryTextColor)), + horizontalAlignment: .right + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0 - 30.0 - component.contentInsets.left - component.contentInsets.right, height: 100.0) + ) + let counterFrame = CGRect(origin: CGPoint(x: availableSize.width - sideInset - component.contentInsets.right - clearButtonSize.width - counterSize.width + 17.0, y: floor((contentHeight - counterSize.height) * 0.5)), size: counterSize) + if let counterView = self.counter.view { + if counterView.superview == nil { + counterView.layer.anchorPoint = CGPoint() + counterView.isUserInteractionEnabled = false + self.addSubview(counterView) + } + transition.setPosition(view: counterView, position: counterFrame.origin) + counterView.bounds = CGRect(origin: CGPoint(), size: counterFrame.size) + } + } else { + self.counter.view?.removeFromSuperview() + } + self.separatorInset = 16.0 + component.contentInsets.left return CGSize(width: availableSize.width, height: contentHeight) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index d02232d67f..d0436d81b6 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -4268,15 +4268,31 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro switch action { case .editRank: let rank: String? + let role: ChatRankInfoScreenRole switch member { case let .channelMember(participant, _): rank = participant.participant.rank - case let .legacyGroupMember(_, _, _, _, _, rankValue): + switch participant.participant { + case .creator: + role = .creator + case let .member(_, _, adminInfo, _, _, _): + role = adminInfo != nil ? .admin : .member + } + case let .legacyGroupMember(_, roleValue, _, _, _, rankValue): rank = rankValue + switch roleValue { + case .creator: + role = .creator + case .admin: + role = .admin + case .member: + role = .member + } default: rank = nil + role = .member } - let controller = self.context.sharedContext.makeChatCustomRankSetupScreen(context: self.context, peerId: peer.id, participantId: member.id, rank: rank) + let controller = self.context.sharedContext.makeChatCustomRankSetupScreen(context: self.context, peerId: peer.id, participantId: member.id, rank: rank, role: role) self.controller?.push(controller) case .promote: if case let .channelMember(channelMember, _) = member { diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift index 25a60b74cf..8959e21dda 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift @@ -174,8 +174,6 @@ final class ChannelAppearanceScreenComponent: Component { private let topOverscrollLayer = SimpleLayer() private let scrollView: ScrollView private let actionButton = ComponentView() - private let bottomPanelBackgroundView: BlurredBackgroundView - private let bottomPanelSeparator: SimpleLayer private let navigationTitle = ComponentView() @@ -242,9 +240,6 @@ final class ChannelAppearanceScreenComponent: Component { } self.scrollView.alwaysBounceVertical = true - self.bottomPanelBackgroundView = BlurredBackgroundView(color: .clear, enableBlur: true) - self.bottomPanelSeparator = SimpleLayer() - super.init(frame: frame) self.scrollView.delegate = self @@ -253,9 +248,6 @@ final class ChannelAppearanceScreenComponent: Component { self.scrollView.layer.addSublayer(self.topOverscrollLayer) self.addSubview(self.edgeEffectView) - - self.addSubview(self.bottomPanelBackgroundView) - self.layer.addSublayer(self.bottomPanelSeparator) } required init?(coder: NSCoder) { @@ -348,13 +340,7 @@ final class ChannelAppearanceScreenComponent: Component { transition.setAlpha(view: navigationTitleView, alpha: navigationAlpha) } - transition.setAlpha(view: self.edgeEffectView, alpha: navigationAlpha) - - let bottomNavigationAlphaDistance: CGFloat = 16.0 - let bottomNavigationAlpha: CGFloat = max(0.0, min(1.0, (self.scrollView.contentSize.height - self.scrollView.bounds.maxY) / bottomNavigationAlphaDistance)) - - transition.setAlpha(view: self.bottomPanelBackgroundView, alpha: bottomNavigationAlpha) - transition.setAlpha(layer: self.bottomPanelSeparator, alpha: bottomNavigationAlpha) + //transition.setAlpha(view: self.edgeEffectView, alpha: navigationAlpha) } private func resolveState() -> ResolvedState? { @@ -1007,11 +993,6 @@ final class ChannelAppearanceScreenComponent: Component { } self.requiredBoostSubject = requiredBoostSubject - let edgeEffectHeight: CGFloat = environment.navigationHeight + 8.0 - let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: edgeEffectHeight)) - transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame) - self.edgeEffectView.update(content: environment.theme.list.blocksBackgroundColor, blur: true, rect: edgeEffectFrame, edge: .top, edgeSize: min(30, edgeEffectFrame.height), transition: transition) - let headerColor: UIColor if let profileColor { let headerBackgroundColors = component.context.peerNameColors.getProfile(profileColor, dark: environment.theme.overallDarkAppearance, subject: .background) @@ -1843,13 +1824,10 @@ final class ChannelAppearanceScreenComponent: Component { transition.setAlpha(view: buttonView, alpha: 1.0) } - let bottomPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: buttonY - 8.0), size: CGSize(width: availableSize.width, height: availableSize.height - buttonY + 8.0)) - transition.setFrame(view: self.bottomPanelBackgroundView, frame: bottomPanelFrame) - self.bottomPanelBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) - self.bottomPanelBackgroundView.update(size: bottomPanelFrame.size, transition: transition.containedViewLayoutTransition) - - self.bottomPanelSeparator.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor - transition.setFrame(layer: self.bottomPanelSeparator, frame: CGRect(origin: CGPoint(x: bottomPanelFrame.minX, y: bottomPanelFrame.minY), size: CGSize(width: bottomPanelFrame.width, height: UIScreenPixel))) + let edgeEffectHeight: CGFloat = availableSize.height - buttonY + 36.0 + let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - edgeEffectHeight), size: CGSize(width: availableSize.width, height: edgeEffectHeight)) + transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame) + self.edgeEffectView.update(content: environment.theme.list.blocksBackgroundColor, alpha: 1.0, rect: edgeEffectFrame, edge: .bottom, edgeSize: edgeEffectFrame.height, transition: transition) let previousBounds = self.scrollView.bounds @@ -1860,10 +1838,6 @@ final class ChannelAppearanceScreenComponent: Component { if self.scrollView.contentSize != contentSize { self.scrollView.contentSize = contentSize } - let scrollInsets = UIEdgeInsets(top: environment.navigationHeight, left: 0.0, bottom: availableSize.height - bottomPanelFrame.minY, right: 0.0) - if self.scrollView.verticalScrollIndicatorInsets != scrollInsets { - self.scrollView.verticalScrollIndicatorInsets = scrollInsets - } if !previousBounds.isEmpty, !transition.animation.isImmediate { let bounds = self.scrollView.bounds diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 549654b028..c03db9ce2f 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -5275,19 +5275,46 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let self, let chatPeer = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel else { return } + var canChange = false + let chatParticipant = Promise() + if let defaultBannedRights = chatPeer.defaultBannedRights { + canChange = !defaultBannedRights.flags.contains(.banEditRank) + + if canChange { + chatParticipant.set(self.context.engine.peers.fetchChannelParticipant(peerId: chatPeer.id, participantId: self.context.account.peerId)) + } + } let controller = self.context.sharedContext.makeChatRankInfoScreen( context: self.context, chatPeer: EnginePeer(chatPeer), userPeer: peer, role: role, rank: rank, - canChange: false, //!(chatPeer.defaultBannedRights?.flags.contains(.editRank) ?? true), + canChange: canChange, completion: { [weak self] in guard let self else { return } - let controller = self.context.sharedContext.makeChatCustomRankSetupScreen(context: self.context, peerId: chatPeer.id, participantId: self.context.account.peerId, rank: nil) - self.push(controller) + let _ = (chatParticipant.get() + |> deliverOnMainQueue).start(next: { [weak self] participant in + guard let self else { + return + } + var rank: String? = nil + var role: ChatRankInfoScreenRole = .member + switch participant { + case let .creator(_, _, rankValue): + rank = rankValue + role = .creator + case let .member(_, _, adminInfo, _, rankValue, _): + rank = rankValue + role = adminInfo != nil ? .admin : .member + default: + break + } + let controller = self.context.sharedContext.makeChatCustomRankSetupScreen(context: self.context, peerId: chatPeer.id, participantId: self.context.account.peerId, rank: rank, role: role) + self.push(controller) + }) } ) self.push(controller) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 6132922684..37a34fde0a 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -4317,8 +4317,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { return PasskeysScreen(context: context, displaySkip: displaySkip, initialPasskeysData: nil, passkeysDataUpdated: { _ in }, completion: completion, cancel: dismiss) } - public func makeChatCustomRankSetupScreen(context: AccountContext, peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?) -> ViewController { - return ChatParticipantRightsScreen(context: context, subject: .rank(peerId: peerId, participantId: participantId, rank: rank)) + public func makeChatCustomRankSetupScreen(context: AccountContext, peerId: EnginePeer.Id, participantId: EnginePeer.Id, rank: String?, role: ChatRankInfoScreenRole) -> ViewController { + return ChatParticipantRightsScreen(context: context, subject: .rank(peerId: peerId, participantId: participantId, rank: rank, role: role)) } public func makePeerCopyProtectionInfoScreen(context: AccountContext, completion: @escaping () -> Void) -> ViewController {