mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-08 08:31:13 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
fb0d0cb90f
@ -5790,3 +5790,5 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
"ChatList.Search.Music_any" = "%@ audio files";
|
"ChatList.Search.Music_any" = "%@ audio files";
|
||||||
|
|
||||||
"Conversation.InputTextAnonymousPlaceholder" = "Send anonymously";
|
"Conversation.InputTextAnonymousPlaceholder" = "Send anonymously";
|
||||||
|
|
||||||
|
"DialogList.Replies" = "Replies";
|
||||||
|
@ -11,20 +11,27 @@ import TelegramPresentationData
|
|||||||
import TelegramUIPreferences
|
import TelegramUIPreferences
|
||||||
|
|
||||||
public final class ChatMessageItemAssociatedData: Equatable {
|
public final class ChatMessageItemAssociatedData: Equatable {
|
||||||
|
public enum ChannelDiscussionGroupStatus: Equatable {
|
||||||
|
case unknown
|
||||||
|
case known(PeerId?)
|
||||||
|
}
|
||||||
|
|
||||||
public let automaticDownloadPeerType: MediaAutoDownloadPeerType
|
public let automaticDownloadPeerType: MediaAutoDownloadPeerType
|
||||||
public let automaticDownloadNetworkType: MediaAutoDownloadNetworkType
|
public let automaticDownloadNetworkType: MediaAutoDownloadNetworkType
|
||||||
public let isRecentActions: Bool
|
public let isRecentActions: Bool
|
||||||
public let isScheduledMessages: Bool
|
public let isScheduledMessages: Bool
|
||||||
public let contactsPeerIds: Set<PeerId>
|
public let contactsPeerIds: Set<PeerId>
|
||||||
|
public let channelDiscussionGroup: ChannelDiscussionGroupStatus
|
||||||
public let animatedEmojiStickers: [String: [StickerPackItem]]
|
public let animatedEmojiStickers: [String: [StickerPackItem]]
|
||||||
public let forcedResourceStatus: FileMediaResourceStatus?
|
public let forcedResourceStatus: FileMediaResourceStatus?
|
||||||
|
|
||||||
public init(automaticDownloadPeerType: MediaAutoDownloadPeerType, automaticDownloadNetworkType: MediaAutoDownloadNetworkType, isRecentActions: Bool = false, isScheduledMessages: Bool = false, contactsPeerIds: Set<PeerId> = Set(), animatedEmojiStickers: [String: [StickerPackItem]] = [:], forcedResourceStatus: FileMediaResourceStatus? = nil) {
|
public init(automaticDownloadPeerType: MediaAutoDownloadPeerType, automaticDownloadNetworkType: MediaAutoDownloadNetworkType, isRecentActions: Bool = false, isScheduledMessages: Bool = false, contactsPeerIds: Set<PeerId> = Set(), channelDiscussionGroup: ChannelDiscussionGroupStatus = .unknown, animatedEmojiStickers: [String: [StickerPackItem]] = [:], forcedResourceStatus: FileMediaResourceStatus? = nil) {
|
||||||
self.automaticDownloadPeerType = automaticDownloadPeerType
|
self.automaticDownloadPeerType = automaticDownloadPeerType
|
||||||
self.automaticDownloadNetworkType = automaticDownloadNetworkType
|
self.automaticDownloadNetworkType = automaticDownloadNetworkType
|
||||||
self.isRecentActions = isRecentActions
|
self.isRecentActions = isRecentActions
|
||||||
self.isScheduledMessages = isScheduledMessages
|
self.isScheduledMessages = isScheduledMessages
|
||||||
self.contactsPeerIds = contactsPeerIds
|
self.contactsPeerIds = contactsPeerIds
|
||||||
|
self.channelDiscussionGroup = channelDiscussionGroup
|
||||||
self.animatedEmojiStickers = animatedEmojiStickers
|
self.animatedEmojiStickers = animatedEmojiStickers
|
||||||
self.forcedResourceStatus = forcedResourceStatus
|
self.forcedResourceStatus = forcedResourceStatus
|
||||||
}
|
}
|
||||||
@ -45,6 +52,9 @@ public final class ChatMessageItemAssociatedData: Equatable {
|
|||||||
if lhs.contactsPeerIds != rhs.contactsPeerIds {
|
if lhs.contactsPeerIds != rhs.contactsPeerIds {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.channelDiscussionGroup != rhs.channelDiscussionGroup {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.animatedEmojiStickers != rhs.animatedEmojiStickers {
|
if lhs.animatedEmojiStickers != rhs.animatedEmojiStickers {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import Emoji
|
|||||||
private let deletedIcon = UIImage(bundleImageName: "Avatar/DeletedIcon")?.precomposed()
|
private let deletedIcon = UIImage(bundleImageName: "Avatar/DeletedIcon")?.precomposed()
|
||||||
private let savedMessagesIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/SavedMessagesIcon"), color: .white)
|
private let savedMessagesIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/SavedMessagesIcon"), color: .white)
|
||||||
private let archivedChatsIcon = UIImage(bundleImageName: "Avatar/ArchiveAvatarIcon")?.precomposed()
|
private let archivedChatsIcon = UIImage(bundleImageName: "Avatar/ArchiveAvatarIcon")?.precomposed()
|
||||||
|
private let repliesIcon = generateTintedImage(image: UIImage(bundleImageName: "Avatar/RepliesMessagesIcon"), color: .white)
|
||||||
|
|
||||||
public func avatarPlaceholderFont(size: CGFloat) -> UIFont {
|
public func avatarPlaceholderFont(size: CGFloat) -> UIFont {
|
||||||
return Font.with(size: size, design: .round, traits: [.bold])
|
return Font.with(size: size, design: .round, traits: [.bold])
|
||||||
@ -100,6 +101,7 @@ private func ==(lhs: AvatarNodeState, rhs: AvatarNodeState) -> Bool {
|
|||||||
private enum AvatarNodeIcon: Equatable {
|
private enum AvatarNodeIcon: Equatable {
|
||||||
case none
|
case none
|
||||||
case savedMessagesIcon
|
case savedMessagesIcon
|
||||||
|
case repliesIcon
|
||||||
case archivedChatsIcon(hiddenByDefault: Bool)
|
case archivedChatsIcon(hiddenByDefault: Bool)
|
||||||
case editAvatarIcon
|
case editAvatarIcon
|
||||||
case deletedIcon
|
case deletedIcon
|
||||||
@ -109,6 +111,7 @@ public enum AvatarNodeImageOverride: Equatable {
|
|||||||
case none
|
case none
|
||||||
case image(TelegramMediaImageRepresentation)
|
case image(TelegramMediaImageRepresentation)
|
||||||
case savedMessagesIcon
|
case savedMessagesIcon
|
||||||
|
case repliesIcon
|
||||||
case archivedChatsIcon(hiddenByDefault: Bool)
|
case archivedChatsIcon(hiddenByDefault: Bool)
|
||||||
case editAvatarIcon
|
case editAvatarIcon
|
||||||
case deletedIcon
|
case deletedIcon
|
||||||
@ -308,6 +311,9 @@ public final class AvatarNode: ASDisplayNode {
|
|||||||
case .savedMessagesIcon:
|
case .savedMessagesIcon:
|
||||||
representation = nil
|
representation = nil
|
||||||
icon = .savedMessagesIcon
|
icon = .savedMessagesIcon
|
||||||
|
case .repliesIcon:
|
||||||
|
representation = nil
|
||||||
|
icon = .repliesIcon
|
||||||
case let .archivedChatsIcon(hiddenByDefault):
|
case let .archivedChatsIcon(hiddenByDefault):
|
||||||
representation = nil
|
representation = nil
|
||||||
icon = .archivedChatsIcon(hiddenByDefault: hiddenByDefault)
|
icon = .archivedChatsIcon(hiddenByDefault: hiddenByDefault)
|
||||||
@ -452,6 +458,8 @@ public final class AvatarNode: ASDisplayNode {
|
|||||||
colorsArray = grayscaleColors
|
colorsArray = grayscaleColors
|
||||||
} else if case .savedMessagesIcon = parameters.icon {
|
} else if case .savedMessagesIcon = parameters.icon {
|
||||||
colorsArray = savedMessagesColors
|
colorsArray = savedMessagesColors
|
||||||
|
} else if case .repliesIcon = parameters.icon {
|
||||||
|
colorsArray = savedMessagesColors
|
||||||
} else if case .editAvatarIcon = parameters.icon, let theme = parameters.theme {
|
} else if case .editAvatarIcon = parameters.icon, let theme = parameters.theme {
|
||||||
colorsArray = [theme.list.itemAccentColor.withAlphaComponent(0.1).cgColor, theme.list.itemAccentColor.withAlphaComponent(0.1).cgColor]
|
colorsArray = [theme.list.itemAccentColor.withAlphaComponent(0.1).cgColor, theme.list.itemAccentColor.withAlphaComponent(0.1).cgColor]
|
||||||
} else if case let .archivedChatsIcon(hiddenByDefault) = parameters.icon, let theme = parameters.theme {
|
} else if case let .archivedChatsIcon(hiddenByDefault) = parameters.icon, let theme = parameters.theme {
|
||||||
@ -506,6 +514,15 @@ public final class AvatarNode: ASDisplayNode {
|
|||||||
if let savedMessagesIcon = savedMessagesIcon {
|
if let savedMessagesIcon = savedMessagesIcon {
|
||||||
context.draw(savedMessagesIcon.cgImage!, in: CGRect(origin: CGPoint(x: floor((bounds.size.width - savedMessagesIcon.size.width) / 2.0), y: floor((bounds.size.height - savedMessagesIcon.size.height) / 2.0)), size: savedMessagesIcon.size))
|
context.draw(savedMessagesIcon.cgImage!, in: CGRect(origin: CGPoint(x: floor((bounds.size.width - savedMessagesIcon.size.width) / 2.0), y: floor((bounds.size.height - savedMessagesIcon.size.height) / 2.0)), size: savedMessagesIcon.size))
|
||||||
}
|
}
|
||||||
|
} else if case .repliesIcon = parameters.icon {
|
||||||
|
let factor = bounds.size.width / 60.0
|
||||||
|
context.translateBy(x: bounds.size.width / 2.0, y: bounds.size.height / 2.0)
|
||||||
|
context.scaleBy(x: factor, y: -factor)
|
||||||
|
context.translateBy(x: -bounds.size.width / 2.0, y: -bounds.size.height / 2.0)
|
||||||
|
|
||||||
|
if let repliesIcon = repliesIcon {
|
||||||
|
context.draw(repliesIcon.cgImage!, in: CGRect(origin: CGPoint(x: floor((bounds.size.width - repliesIcon.size.width) / 2.0), y: floor((bounds.size.height - repliesIcon.size.height) / 2.0)), size: repliesIcon.size))
|
||||||
|
}
|
||||||
} else if case .editAvatarIcon = parameters.icon, let theme = parameters.theme, !parameters.hasImage {
|
} else if case .editAvatarIcon = parameters.icon, let theme = parameters.theme, !parameters.hasImage {
|
||||||
context.translateBy(x: bounds.size.width / 2.0, y: bounds.size.height / 2.0)
|
context.translateBy(x: bounds.size.width / 2.0, y: bounds.size.height / 2.0)
|
||||||
context.scaleBy(x: 1.0, y: -1.0)
|
context.scaleBy(x: 1.0, y: -1.0)
|
||||||
|
@ -638,7 +638,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
if let peer = peer {
|
if let peer = peer {
|
||||||
var overrideImage: AvatarNodeImageOverride?
|
var overrideImage: AvatarNodeImageOverride?
|
||||||
if peer.id.isReplies {
|
if peer.id.isReplies {
|
||||||
overrideImage = .savedMessagesIcon
|
overrideImage = .repliesIcon
|
||||||
} else if peer.id == item.context.account.peerId && !displayAsMessage {
|
} else if peer.id == item.context.account.peerId && !displayAsMessage {
|
||||||
overrideImage = .savedMessagesIcon
|
overrideImage = .savedMessagesIcon
|
||||||
} else if peer.isDeleted {
|
} else if peer.isDeleted {
|
||||||
@ -1101,7 +1101,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
titleAttributedString = NSAttributedString(string: item.presentationData.strings.ChatList_ArchivedChatsTitle, font: titleFont, textColor: theme.titleColor)
|
titleAttributedString = NSAttributedString(string: item.presentationData.strings.ChatList_ArchivedChatsTitle, font: titleFont, textColor: theme.titleColor)
|
||||||
} else if itemPeer.chatMainPeer?.id == item.context.account.peerId {
|
} else if itemPeer.chatMainPeer?.id == item.context.account.peerId {
|
||||||
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: titleFont, textColor: theme.titleColor)
|
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: titleFont, textColor: theme.titleColor)
|
||||||
} else if let displayTitle = itemPeer.chatMainPeer?.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) {
|
} else if let id = itemPeer.chatMainPeer?.id, id.isReplies {
|
||||||
|
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Replies, font: titleFont, textColor: theme.titleColor)
|
||||||
|
} else if let displayTitle = itemPeer.chatMainPeer?.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) {
|
||||||
titleAttributedString = NSAttributedString(string: displayTitle, font: titleFont, textColor: item.index.messageIndex.id.peerId.namespace == Namespaces.Peer.SecretChat ? theme.secretTitleColor : theme.titleColor)
|
titleAttributedString = NSAttributedString(string: displayTitle, font: titleFont, textColor: item.index.messageIndex.id.peerId.namespace == Namespaces.Peer.SecretChat ? theme.secretTitleColor : theme.titleColor)
|
||||||
}
|
}
|
||||||
case .group:
|
case .group:
|
||||||
|
@ -556,6 +556,8 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||||||
if let user = peer as? TelegramUser {
|
if let user = peer as? TelegramUser {
|
||||||
if peer.id == item.context.account.peerId, case .generalSearch = item.peerMode {
|
if peer.id == item.context.account.peerId, case .generalSearch = item.peerMode {
|
||||||
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: titleBoldFont, textColor: textColor)
|
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: titleBoldFont, textColor: textColor)
|
||||||
|
} else if peer.id.isReplies {
|
||||||
|
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Replies, font: titleBoldFont, textColor: textColor)
|
||||||
} else if let firstName = user.firstName, let lastName = user.lastName, !firstName.isEmpty, !lastName.isEmpty {
|
} else if let firstName = user.firstName, let lastName = user.lastName, !firstName.isEmpty, !lastName.isEmpty {
|
||||||
let string = NSMutableAttributedString()
|
let string = NSMutableAttributedString()
|
||||||
switch item.displayOrder {
|
switch item.displayOrder {
|
||||||
@ -747,6 +749,8 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||||||
var overrideImage: AvatarNodeImageOverride?
|
var overrideImage: AvatarNodeImageOverride?
|
||||||
if peer.id == item.context.account.peerId, case .generalSearch = item.peerMode {
|
if peer.id == item.context.account.peerId, case .generalSearch = item.peerMode {
|
||||||
overrideImage = .savedMessagesIcon
|
overrideImage = .savedMessagesIcon
|
||||||
|
} else if peer.id.isReplies, case .generalSearch = item.peerMode {
|
||||||
|
overrideImage = .repliesIcon
|
||||||
} else if peer.isDeleted {
|
} else if peer.isDeleted {
|
||||||
overrideImage = .deletedIcon
|
overrideImage = .deletedIcon
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,8 @@ private final class DeleteChatPeerActionSheetItemNode: ActionSheetItemNode {
|
|||||||
|
|
||||||
if chatPeer.id == context.account.peerId {
|
if chatPeer.id == context.account.peerId {
|
||||||
self.avatarNode.setPeer(context: context, theme: (context.sharedContext.currentPresentationData.with { $0 }).theme, peer: peer, overrideImage: .savedMessagesIcon)
|
self.avatarNode.setPeer(context: context, theme: (context.sharedContext.currentPresentationData.with { $0 }).theme, peer: peer, overrideImage: .savedMessagesIcon)
|
||||||
|
} else if chatPeer.id.isReplies {
|
||||||
|
self.avatarNode.setPeer(context: context, theme: (context.sharedContext.currentPresentationData.with { $0 }).theme, peer: peer, overrideImage: .repliesIcon)
|
||||||
} else {
|
} else {
|
||||||
var overrideImage: AvatarNodeImageOverride?
|
var overrideImage: AvatarNodeImageOverride?
|
||||||
if chatPeer.isDeleted {
|
if chatPeer.isDeleted {
|
||||||
|
@ -42,6 +42,8 @@ final class HashtagSearchControllerNode: ASDisplayNode {
|
|||||||
var items: [String] = []
|
var items: [String] = []
|
||||||
if peer?.id == context.account.peerId {
|
if peer?.id == context.account.peerId {
|
||||||
items.append(presentationData.strings.Conversation_SavedMessages)
|
items.append(presentationData.strings.Conversation_SavedMessages)
|
||||||
|
} else if let id = peer?.id, id.isReplies {
|
||||||
|
items.append(presentationData.strings.DialogList_Replies)
|
||||||
} else {
|
} else {
|
||||||
items.append(peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) ?? "")
|
items.append(peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) ?? "")
|
||||||
}
|
}
|
||||||
|
@ -683,6 +683,8 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
|
|
||||||
if item.peer.id == item.context.account.peerId, case .threatSelfAsSaved = item.aliasHandling {
|
if item.peer.id == item.context.account.peerId, case .threatSelfAsSaved = item.aliasHandling {
|
||||||
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: currentBoldFont, textColor: titleColor)
|
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: currentBoldFont, textColor: titleColor)
|
||||||
|
} else if item.peer.id.isReplies {
|
||||||
|
titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Replies, font: currentBoldFont, textColor: titleColor)
|
||||||
} else if let user = item.peer as? TelegramUser {
|
} else if let user = item.peer as? TelegramUser {
|
||||||
if let firstName = user.firstName, let lastName = user.lastName, !firstName.isEmpty, !lastName.isEmpty {
|
if let firstName = user.firstName, let lastName = user.lastName, !firstName.isEmpty, !lastName.isEmpty {
|
||||||
let string = NSMutableAttributedString()
|
let string = NSMutableAttributedString()
|
||||||
@ -1059,6 +1061,8 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
|
|
||||||
if item.peer.id == item.context.account.peerId, case .threatSelfAsSaved = item.aliasHandling {
|
if item.peer.id == item.context.account.peerId, case .threatSelfAsSaved = item.aliasHandling {
|
||||||
strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: .savedMessagesIcon, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad)
|
strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: .savedMessagesIcon, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad)
|
||||||
|
} else if item.peer.id.isReplies {
|
||||||
|
strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: .repliesIcon, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad)
|
||||||
} else {
|
} else {
|
||||||
var overrideImage: AvatarNodeImageOverride?
|
var overrideImage: AvatarNodeImageOverride?
|
||||||
if item.peer.isDeleted {
|
if item.peer.isDeleted {
|
||||||
|
@ -637,7 +637,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
|
|||||||
} else if let initialParticipant = initialParticipant, case let .creator(_, maybeAdminRights, _) = initialParticipant, let adminRights = maybeAdminRights {
|
} else if let initialParticipant = initialParticipant, case let .creator(_, maybeAdminRights, _) = initialParticipant, let adminRights = maybeAdminRights {
|
||||||
currentRightsFlags = adminRights.rights.flags
|
currentRightsFlags = adminRights.rights.flags
|
||||||
} else {
|
} else {
|
||||||
currentRightsFlags = accountUserRightsFlags.subtracting(.canAddAdmins)
|
currentRightsFlags = accountUserRightsFlags.subtracting(.canAddAdmins).subtracting(.canBeAnonymous)
|
||||||
}
|
}
|
||||||
|
|
||||||
var index = 0
|
var index = 0
|
||||||
@ -667,7 +667,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s
|
|||||||
} else if let initialParticipant = initialParticipant, case let .member(_, _, maybeAdminRights, _, _) = initialParticipant, let adminRights = maybeAdminRights {
|
} else if let initialParticipant = initialParticipant, case let .member(_, _, maybeAdminRights, _, _) = initialParticipant, let adminRights = maybeAdminRights {
|
||||||
currentRightsFlags = adminRights.rights.flags
|
currentRightsFlags = adminRights.rights.flags
|
||||||
} else {
|
} else {
|
||||||
currentRightsFlags = accountUserRightsFlags.subtracting(.canAddAdmins)
|
currentRightsFlags = accountUserRightsFlags.subtracting(.canAddAdmins).subtracting(.canBeAnonymous)
|
||||||
}
|
}
|
||||||
|
|
||||||
var index = 0
|
var index = 0
|
||||||
|
@ -166,7 +166,7 @@ private func channelDiscussionGroupSetupControllerEntries(presentationData: Pres
|
|||||||
|
|
||||||
var entries: [ChannelDiscussionGroupSetupControllerEntry] = []
|
var entries: [ChannelDiscussionGroupSetupControllerEntry] = []
|
||||||
|
|
||||||
if let linkedDiscussionPeerId = cachedData.linkedDiscussionPeerId {
|
if case let .known(maybeLinkedDiscussionPeerId) = cachedData.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId {
|
||||||
if let group = view.peers[linkedDiscussionPeerId] {
|
if let group = view.peers[linkedDiscussionPeerId] {
|
||||||
if case .group = peer.info {
|
if case .group = peer.info {
|
||||||
entries.append(.header(presentationData.theme, presentationData.strings, group.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), true, presentationData.strings.Channel_DiscussionGroup_HeaderLabel))
|
entries.append(.header(presentationData.theme, presentationData.strings, group.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), true, presentationData.strings.Channel_DiscussionGroup_HeaderLabel))
|
||||||
@ -299,7 +299,7 @@ public func channelDiscussionGroupSetupController(context: AccountContext, peerI
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if groupId == cachedData.linkedDiscussionPeerId {
|
if case let .known(maybeLinkedDiscussionPeerId) = cachedData.linkedDiscussionPeerId, maybeLinkedDiscussionPeerId == groupId {
|
||||||
navigateToGroupImpl?(groupId)
|
navigateToGroupImpl?(groupId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -483,7 +483,7 @@ public func channelDiscussionGroupSetupController(context: AccountContext, peerI
|
|||||||
let applyPeerId: PeerId
|
let applyPeerId: PeerId
|
||||||
if case .broadcast = peer.info {
|
if case .broadcast = peer.info {
|
||||||
applyPeerId = peerId
|
applyPeerId = peerId
|
||||||
} else if let linkedDiscussionPeerId = cachedData.linkedDiscussionPeerId {
|
} else if case let .known(maybeLinkedDiscussionPeerId) = cachedData.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId {
|
||||||
applyPeerId = linkedDiscussionPeerId
|
applyPeerId = linkedDiscussionPeerId
|
||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
|
@ -497,7 +497,7 @@ private func channelInfoEntries(account: Account, presentationData: Presentation
|
|||||||
|
|
||||||
let discussionGroupTitle: String
|
let discussionGroupTitle: String
|
||||||
if let cachedData = view.cachedData as? CachedChannelData {
|
if let cachedData = view.cachedData as? CachedChannelData {
|
||||||
if let linkedDiscussionPeerId = cachedData.linkedDiscussionPeerId, let peer = view.peers[linkedDiscussionPeerId] {
|
if case let .known(maybeLinkedDiscussionPeerId) = cachedData.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = view.peers[linkedDiscussionPeerId] {
|
||||||
if let addressName = peer.addressName, !addressName.isEmpty {
|
if let addressName = peer.addressName, !addressName.isEmpty {
|
||||||
discussionGroupTitle = "@\(addressName)"
|
discussionGroupTitle = "@\(addressName)"
|
||||||
} else {
|
} else {
|
||||||
@ -532,7 +532,7 @@ private func channelInfoEntries(account: Account, presentationData: Presentation
|
|||||||
if let _ = state.editingState, let adminRights = peer.adminRights, !adminRights.isEmpty {
|
if let _ = state.editingState, let adminRights = peer.adminRights, !adminRights.isEmpty {
|
||||||
let discussionGroupTitle: String?
|
let discussionGroupTitle: String?
|
||||||
if let cachedData = view.cachedData as? CachedChannelData {
|
if let cachedData = view.cachedData as? CachedChannelData {
|
||||||
if let linkedDiscussionPeerId = cachedData.linkedDiscussionPeerId, let peer = view.peers[linkedDiscussionPeerId] {
|
if case let .known(maybeLinkedDiscussionPeerId) = cachedData.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = view.peers[linkedDiscussionPeerId] {
|
||||||
if let addressName = peer.addressName, !addressName.isEmpty {
|
if let addressName = peer.addressName, !addressName.isEmpty {
|
||||||
discussionGroupTitle = "@\(addressName)"
|
discussionGroupTitle = "@\(addressName)"
|
||||||
} else {
|
} else {
|
||||||
@ -951,7 +951,7 @@ public func channelInfoController(context: AccountContext, peerId: PeerId) -> Vi
|
|||||||
if canEditChannel {
|
if canEditChannel {
|
||||||
hasSomethingToEdit = true
|
hasSomethingToEdit = true
|
||||||
} else if let adminRights = peer.adminRights, !adminRights.isEmpty {
|
} else if let adminRights = peer.adminRights, !adminRights.isEmpty {
|
||||||
if let cachedData = view.cachedData as? CachedChannelData, let _ = cachedData.linkedDiscussionPeerId {
|
if let cachedData = view.cachedData as? CachedChannelData, case let .known(maybeLinkedDiscussionPeerId) = cachedData.linkedDiscussionPeerId, let _ = maybeLinkedDiscussionPeerId {
|
||||||
hasSomethingToEdit = true
|
hasSomethingToEdit = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -896,7 +896,7 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa
|
|||||||
} else {
|
} else {
|
||||||
if cachedChannelData.flags.contains(.canChangeUsername) {
|
if cachedChannelData.flags.contains(.canChangeUsername) {
|
||||||
entries.append(GroupInfoEntry.groupTypeSetup(presentationData.theme, presentationData.strings.GroupInfo_GroupType, isPublic ? presentationData.strings.Channel_Setup_TypePublic : presentationData.strings.Channel_Setup_TypePrivate))
|
entries.append(GroupInfoEntry.groupTypeSetup(presentationData.theme, presentationData.strings.GroupInfo_GroupType, isPublic ? presentationData.strings.Channel_Setup_TypePublic : presentationData.strings.Channel_Setup_TypePrivate))
|
||||||
if let linkedDiscussionPeerId = cachedChannelData.linkedDiscussionPeerId, let peer = view.peers[linkedDiscussionPeerId] {
|
if case let .known(maybeLinkedDiscussionPeerId) = cachedChannelData.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = view.peers[linkedDiscussionPeerId] {
|
||||||
let peerTitle: String
|
let peerTitle: String
|
||||||
if let addressName = peer.addressName, !addressName.isEmpty {
|
if let addressName = peer.addressName, !addressName.isEmpty {
|
||||||
peerTitle = "@\(addressName)"
|
peerTitle = "@\(addressName)"
|
||||||
|
@ -948,6 +948,14 @@ public final class Transaction {
|
|||||||
return postbox.messageHistoryTagsTable.earlierIndices(tag: tag, peerId: peerId, namespace: namespace, index: nil, includeFrom: false, count: 1000)
|
return postbox.messageHistoryTagsTable.earlierIndices(tag: tag, peerId: peerId, namespace: namespace, index: nil, includeFrom: false, count: 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func getMessagesWithThreadId(peerId: PeerId, namespace: MessageId.Namespace, threadId: Int64, from: MessageIndex, includeFrom: Bool, to: MessageIndex, limit: Int) -> [Message] {
|
||||||
|
assert(!self.disposed)
|
||||||
|
guard let postbox = self.postbox else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, threadId: threadId, from: from, includeFrom: includeFrom, to: to, limit: limit).map(postbox.renderIntermediateMessage(_:))
|
||||||
|
}
|
||||||
|
|
||||||
public func scanMessages(peerId: PeerId, namespace: MessageId.Namespace, tag: MessageTags, _ f: (Message) -> Bool) {
|
public func scanMessages(peerId: PeerId, namespace: MessageId.Namespace, tag: MessageTags, _ f: (Message) -> Bool) {
|
||||||
assert(!self.disposed)
|
assert(!self.disposed)
|
||||||
self.postbox?.scanMessages(peerId: peerId, namespace: namespace, tag: tag, f)
|
self.postbox?.scanMessages(peerId: peerId, namespace: namespace, tag: tag, f)
|
||||||
|
@ -149,6 +149,9 @@ public final class SelectablePeerNode: ASDisplayNode {
|
|||||||
if peer.peerId == context.account.peerId {
|
if peer.peerId == context.account.peerId {
|
||||||
text = strings.DialogList_SavedMessages
|
text = strings.DialogList_SavedMessages
|
||||||
overrideImage = .savedMessagesIcon
|
overrideImage = .savedMessagesIcon
|
||||||
|
} else if peer.peerId.isReplies {
|
||||||
|
text = strings.DialogList_Replies
|
||||||
|
overrideImage = .repliesIcon
|
||||||
} else {
|
} else {
|
||||||
text = mainPeer.compactDisplayTitle
|
text = mainPeer.compactDisplayTitle
|
||||||
if mainPeer.isDeleted {
|
if mainPeer.isDeleted {
|
||||||
|
@ -150,6 +150,11 @@ public struct PeerGeoLocation: PostboxCoding, Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class CachedChannelData: CachedPeerData {
|
public final class CachedChannelData: CachedPeerData {
|
||||||
|
public enum LinkedDiscussionPeerId: Equatable {
|
||||||
|
case unknown
|
||||||
|
case known(PeerId?)
|
||||||
|
}
|
||||||
|
|
||||||
public let isNotAccessible: Bool
|
public let isNotAccessible: Bool
|
||||||
public let flags: CachedChannelFlags
|
public let flags: CachedChannelFlags
|
||||||
public let about: String?
|
public let about: String?
|
||||||
@ -161,7 +166,7 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
public let stickerPack: StickerPackCollectionInfo?
|
public let stickerPack: StickerPackCollectionInfo?
|
||||||
public let minAvailableMessageId: MessageId?
|
public let minAvailableMessageId: MessageId?
|
||||||
public let migrationReference: ChannelMigrationReference?
|
public let migrationReference: ChannelMigrationReference?
|
||||||
public let linkedDiscussionPeerId: PeerId?
|
public let linkedDiscussionPeerId: LinkedDiscussionPeerId
|
||||||
public let peerGeoLocation: PeerGeoLocation?
|
public let peerGeoLocation: PeerGeoLocation?
|
||||||
public let slowModeTimeout: Int32?
|
public let slowModeTimeout: Int32?
|
||||||
public let slowModeValidUntilTimestamp: Int32?
|
public let slowModeValidUntilTimestamp: Int32?
|
||||||
@ -190,7 +195,7 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
self.stickerPack = nil
|
self.stickerPack = nil
|
||||||
self.minAvailableMessageId = nil
|
self.minAvailableMessageId = nil
|
||||||
self.migrationReference = nil
|
self.migrationReference = nil
|
||||||
self.linkedDiscussionPeerId = nil
|
self.linkedDiscussionPeerId = .unknown
|
||||||
self.peerGeoLocation = nil
|
self.peerGeoLocation = nil
|
||||||
self.slowModeTimeout = nil
|
self.slowModeTimeout = nil
|
||||||
self.slowModeValidUntilTimestamp = nil
|
self.slowModeValidUntilTimestamp = nil
|
||||||
@ -200,7 +205,7 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
self.photo = nil
|
self.photo = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(isNotAccessible: Bool, flags: CachedChannelFlags, about: String?, participantsSummary: CachedChannelParticipantsSummary, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, stickerPack: StickerPackCollectionInfo?, minAvailableMessageId: MessageId?, migrationReference: ChannelMigrationReference?, linkedDiscussionPeerId: PeerId?, peerGeoLocation: PeerGeoLocation?, slowModeTimeout: Int32?, slowModeValidUntilTimestamp: Int32?, hasScheduledMessages: Bool, statsDatacenterId: Int32, invitedBy: PeerId?, photo: TelegramMediaImage?) {
|
public init(isNotAccessible: Bool, flags: CachedChannelFlags, about: String?, participantsSummary: CachedChannelParticipantsSummary, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, stickerPack: StickerPackCollectionInfo?, minAvailableMessageId: MessageId?, migrationReference: ChannelMigrationReference?, linkedDiscussionPeerId: LinkedDiscussionPeerId, peerGeoLocation: PeerGeoLocation?, slowModeTimeout: Int32?, slowModeValidUntilTimestamp: Int32?, hasScheduledMessages: Bool, statsDatacenterId: Int32, invitedBy: PeerId?, photo: TelegramMediaImage?) {
|
||||||
self.isNotAccessible = isNotAccessible
|
self.isNotAccessible = isNotAccessible
|
||||||
self.flags = flags
|
self.flags = flags
|
||||||
self.about = about
|
self.about = about
|
||||||
@ -226,8 +231,10 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
peerIds.insert(botInfo.peerId)
|
peerIds.insert(botInfo.peerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let linkedDiscussionPeerId = linkedDiscussionPeerId {
|
if case let .known(linkedDiscussionPeerIdValue) = linkedDiscussionPeerId {
|
||||||
peerIds.insert(linkedDiscussionPeerId)
|
if let linkedDiscussionPeerIdValue = linkedDiscussionPeerIdValue {
|
||||||
|
peerIds.insert(linkedDiscussionPeerIdValue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let invitedBy = invitedBy {
|
if let invitedBy = invitedBy {
|
||||||
@ -288,7 +295,7 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedLinkedDiscussionPeerId(_ linkedDiscussionPeerId: PeerId?) -> CachedChannelData {
|
public func withUpdatedLinkedDiscussionPeerId(_ linkedDiscussionPeerId: LinkedDiscussionPeerId) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy, photo: self.photo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,9 +368,13 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let linkedDiscussionPeerId = decoder.decodeOptionalInt64ForKey("dgi") {
|
if let linkedDiscussionPeerId = decoder.decodeOptionalInt64ForKey("dgi") {
|
||||||
self.linkedDiscussionPeerId = PeerId(linkedDiscussionPeerId)
|
if linkedDiscussionPeerId == 0 {
|
||||||
|
self.linkedDiscussionPeerId = .known(nil)
|
||||||
|
} else {
|
||||||
|
self.linkedDiscussionPeerId = .known(PeerId(linkedDiscussionPeerId))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.linkedDiscussionPeerId = nil
|
self.linkedDiscussionPeerId = .unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
if let peerGeoLocation = decoder.decodeObjectForKey("pgl", decoder: { PeerGeoLocation(decoder: $0) }) as? PeerGeoLocation {
|
if let peerGeoLocation = decoder.decodeObjectForKey("pgl", decoder: { PeerGeoLocation(decoder: $0) }) as? PeerGeoLocation {
|
||||||
@ -385,8 +396,10 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
self.photo = nil
|
self.photo = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if let linkedDiscussionPeerId = self.linkedDiscussionPeerId {
|
if case let .known(linkedDiscussionPeerIdValue) = self.linkedDiscussionPeerId {
|
||||||
peerIds.insert(linkedDiscussionPeerId)
|
if let linkedDiscussionPeerIdValue = linkedDiscussionPeerIdValue {
|
||||||
|
peerIds.insert(linkedDiscussionPeerIdValue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.peerIds = peerIds
|
self.peerIds = peerIds
|
||||||
@ -446,10 +459,15 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
} else {
|
} else {
|
||||||
encoder.encodeNil(forKey: "mr")
|
encoder.encodeNil(forKey: "mr")
|
||||||
}
|
}
|
||||||
if let linkedDiscussionPeerId = self.linkedDiscussionPeerId {
|
switch self.linkedDiscussionPeerId {
|
||||||
encoder.encodeInt64(linkedDiscussionPeerId.toInt64(), forKey: "dgi")
|
case .unknown:
|
||||||
} else {
|
|
||||||
encoder.encodeNil(forKey: "dgi")
|
encoder.encodeNil(forKey: "dgi")
|
||||||
|
case let .known(value):
|
||||||
|
if let value = value {
|
||||||
|
encoder.encodeInt64(value.toInt64(), forKey: "dgi")
|
||||||
|
} else {
|
||||||
|
encoder.encodeInt64(0, forKey: "dgi")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let peerGeoLocation = self.peerGeoLocation {
|
if let peerGeoLocation = self.peerGeoLocation {
|
||||||
encoder.encodeObject(peerGeoLocation, forKey: "pgl")
|
encoder.encodeObject(peerGeoLocation, forKey: "pgl")
|
||||||
|
@ -138,9 +138,49 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
|
|||||||
}
|
}
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> mapToSignal { _ -> Signal<IndexSet, NoError> in
|
|> mapToSignal { _ -> Signal<IndexSet, NoError> in
|
||||||
return postbox.loadedPeerWithId(peerId)
|
return postbox.transaction { transaction -> (Peer?, Int32) in
|
||||||
|> take(1)
|
var hash: Int32 = 0
|
||||||
|> mapToSignal { peer in
|
switch space {
|
||||||
|
case .everywhere:
|
||||||
|
if let threadId = threadId {
|
||||||
|
let offsetId: Int32
|
||||||
|
let addOffset: Int32
|
||||||
|
let selectedLimit = count
|
||||||
|
let maxId: Int32
|
||||||
|
let minId: Int32
|
||||||
|
|
||||||
|
switch direction {
|
||||||
|
case let .range(start, end):
|
||||||
|
if start.id <= end.id {
|
||||||
|
offsetId = start.id <= 1 ? 1 : (start.id - 1)
|
||||||
|
addOffset = Int32(-selectedLimit)
|
||||||
|
maxId = end.id
|
||||||
|
minId = start.id - 1
|
||||||
|
} else {
|
||||||
|
offsetId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||||
|
addOffset = 0
|
||||||
|
maxId = start.id == Int32.max ? start.id : (start.id + 1)
|
||||||
|
minId = end.id
|
||||||
|
}
|
||||||
|
case let .aroundId(id):
|
||||||
|
offsetId = id.id
|
||||||
|
addOffset = Int32(-selectedLimit / 2)
|
||||||
|
maxId = Int32.max
|
||||||
|
minId = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
//request = source.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: threadId.id, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return (transaction.getPeer(peerId), hash)
|
||||||
|
}
|
||||||
|
|> mapToSignal { (peer, hash) -> Signal<IndexSet, NoError> in
|
||||||
|
guard let peer = peer else {
|
||||||
|
return .single(IndexSet())
|
||||||
|
}
|
||||||
if let inputPeer = forceApiInputPeer(peer) {
|
if let inputPeer = forceApiInputPeer(peer) {
|
||||||
print("fetchMessageHistoryHole for \(peer.id) \(peer.debugDisplayTitle) \(direction) space \(space)")
|
print("fetchMessageHistoryHole for \(peer.id) \(peer.debugDisplayTitle) \(direction) space \(space)")
|
||||||
Logger.shared.log("fetchMessageHistoryHole", "fetch for \(peer.id) \(peer.debugDisplayTitle) \(direction) space \(space)")
|
Logger.shared.log("fetchMessageHistoryHole", "fetch for \(peer.id) \(peer.debugDisplayTitle) \(direction) space \(space)")
|
||||||
@ -197,7 +237,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
|
|||||||
minMaxRange = 1 ... (Int32.max - 1)
|
minMaxRange = 1 ... (Int32.max - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
request = source.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: threadId.id, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0))
|
request = source.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: threadId.id, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: hash))
|
||||||
} else {
|
} else {
|
||||||
let offsetId: Int32
|
let offsetId: Int32
|
||||||
let addOffset: Int32
|
let addOffset: Int32
|
||||||
|
@ -72,30 +72,30 @@ public func updateGroupDiscussionForChannel(network: Network, postbox: Postbox,
|
|||||||
if result {
|
if result {
|
||||||
return postbox.transaction { transaction in
|
return postbox.transaction { transaction in
|
||||||
if let channelId = channelId {
|
if let channelId = channelId {
|
||||||
var previousGroupId: PeerId?
|
var previousGroupId: CachedChannelData.LinkedDiscussionPeerId?
|
||||||
transaction.updatePeerCachedData(peerIds: Set([channelId]), update: { (_, current) -> CachedPeerData? in
|
transaction.updatePeerCachedData(peerIds: Set([channelId]), update: { (_, current) -> CachedPeerData? in
|
||||||
let current: CachedChannelData = current as? CachedChannelData ?? CachedChannelData()
|
let current: CachedChannelData = current as? CachedChannelData ?? CachedChannelData()
|
||||||
previousGroupId = current.linkedDiscussionPeerId
|
previousGroupId = current.linkedDiscussionPeerId
|
||||||
return current.withUpdatedLinkedDiscussionPeerId(groupId)
|
return current.withUpdatedLinkedDiscussionPeerId(.known(groupId))
|
||||||
})
|
})
|
||||||
if let previousGroupId = previousGroupId, previousGroupId != groupId {
|
if case let .known(maybeValue) = previousGroupId, let value = maybeValue, value != groupId {
|
||||||
transaction.updatePeerCachedData(peerIds: Set([previousGroupId]), update: { (_, current) -> CachedPeerData? in
|
transaction.updatePeerCachedData(peerIds: Set([value]), update: { (_, current) -> CachedPeerData? in
|
||||||
let cachedData = (current as? CachedChannelData ?? CachedChannelData())
|
let cachedData = (current as? CachedChannelData ?? CachedChannelData())
|
||||||
return cachedData.withUpdatedLinkedDiscussionPeerId(nil)
|
return cachedData.withUpdatedLinkedDiscussionPeerId(.known(nil))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let groupId = groupId {
|
if let groupId = groupId {
|
||||||
var previousChannelId: PeerId?
|
var previousChannelId: CachedChannelData.LinkedDiscussionPeerId?
|
||||||
transaction.updatePeerCachedData(peerIds: Set([groupId]), update: { (_, current) -> CachedPeerData? in
|
transaction.updatePeerCachedData(peerIds: Set([groupId]), update: { (_, current) -> CachedPeerData? in
|
||||||
let current: CachedChannelData = current as? CachedChannelData ?? CachedChannelData()
|
let current: CachedChannelData = current as? CachedChannelData ?? CachedChannelData()
|
||||||
previousChannelId = current.linkedDiscussionPeerId
|
previousChannelId = current.linkedDiscussionPeerId
|
||||||
return current.withUpdatedLinkedDiscussionPeerId(channelId)
|
return current.withUpdatedLinkedDiscussionPeerId(.known(channelId))
|
||||||
})
|
})
|
||||||
if let previousChannelId = previousChannelId, previousChannelId != channelId {
|
if case let .known(maybeValue) = previousChannelId, let value = maybeValue, value != channelId {
|
||||||
transaction.updatePeerCachedData(peerIds: Set([previousChannelId]), update: { (_, current) -> CachedPeerData? in
|
transaction.updatePeerCachedData(peerIds: Set([value]), update: { (_, current) -> CachedPeerData? in
|
||||||
let cachedData = (current as? CachedChannelData ?? CachedChannelData())
|
let cachedData = (current as? CachedChannelData ?? CachedChannelData())
|
||||||
return cachedData.withUpdatedLinkedDiscussionPeerId(nil)
|
return cachedData.withUpdatedLinkedDiscussionPeerId(.known(nil))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -498,7 +498,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||||||
.withUpdatedStickerPack(stickerPack)
|
.withUpdatedStickerPack(stickerPack)
|
||||||
.withUpdatedMinAvailableMessageId(minAvailableMessageId)
|
.withUpdatedMinAvailableMessageId(minAvailableMessageId)
|
||||||
.withUpdatedMigrationReference(migrationReference)
|
.withUpdatedMigrationReference(migrationReference)
|
||||||
.withUpdatedLinkedDiscussionPeerId(linkedDiscussionPeerId)
|
.withUpdatedLinkedDiscussionPeerId(.known(linkedDiscussionPeerId))
|
||||||
.withUpdatedPeerGeoLocation(peerGeoLocation)
|
.withUpdatedPeerGeoLocation(peerGeoLocation)
|
||||||
.withUpdatedSlowModeTimeout(slowmodeSeconds)
|
.withUpdatedSlowModeTimeout(slowmodeSeconds)
|
||||||
.withUpdatedSlowModeValidUntilTimestamp(slowmodeNextSendDate)
|
.withUpdatedSlowModeValidUntilTimestamp(slowmodeNextSendDate)
|
||||||
|
@ -74,20 +74,14 @@ private func updateMessageThreadStatsInternal(transaction: Transaction, threadMe
|
|||||||
let countDifference = Int32(difference)
|
let countDifference = Int32(difference)
|
||||||
|
|
||||||
var attributes = currentMessage.attributes
|
var attributes = currentMessage.attributes
|
||||||
var updated = false
|
|
||||||
loop: for j in 0 ..< attributes.count {
|
loop: for j in 0 ..< attributes.count {
|
||||||
if let attribute = attributes[j] as? ReplyThreadMessageAttribute {
|
if let attribute = attributes[j] as? ReplyThreadMessageAttribute {
|
||||||
let count = max(0, attribute.count + countDifference)
|
let count = max(0, attribute.count + countDifference)
|
||||||
attributes[j] = ReplyThreadMessageAttribute(count: count, latestUsers: mergeLatestUsers(current: attribute.latestUsers, added: addedMessagePeers, isGroup: isGroup, isEmpty: count == 0), commentsPeerId: attribute.commentsPeerId)
|
attributes[j] = ReplyThreadMessageAttribute(count: count, latestUsers: mergeLatestUsers(current: attribute.latestUsers, added: addedMessagePeers, isGroup: isGroup, isEmpty: count == 0), commentsPeerId: attribute.commentsPeerId)
|
||||||
updated = true
|
|
||||||
} else if let attribute = attributes[j] as? SourceReferenceMessageAttribute {
|
} else if let attribute = attributes[j] as? SourceReferenceMessageAttribute {
|
||||||
channelThreadMessageId = attribute.messageId
|
channelThreadMessageId = attribute.messageId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !updated && isGroup {
|
|
||||||
let count = max(0, countDifference)
|
|
||||||
attributes.append(ReplyThreadMessageAttribute(count: count, latestUsers: mergeLatestUsers(current: [], added: addedMessagePeers, isGroup: isGroup, isEmpty: count == 0), commentsPeerId: nil))
|
|
||||||
}
|
|
||||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -262,4 +262,5 @@ public enum PresentationResourceParameterKey: Hashable {
|
|||||||
|
|
||||||
case chatMessageCommentsIcon(incoming: Bool)
|
case chatMessageCommentsIcon(incoming: Bool)
|
||||||
case chatMessageCommentsArrowIcon(incoming: Bool)
|
case chatMessageCommentsArrowIcon(incoming: Bool)
|
||||||
|
case chatMessageRepliesIcon(incoming: Bool)
|
||||||
}
|
}
|
||||||
|
@ -1099,6 +1099,14 @@ public struct PresentationResourcesChat {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func chatMessageRepliesIcon(_ theme: PresentationTheme, incoming: Bool) -> UIImage? {
|
||||||
|
return theme.image(PresentationResourceParameterKey.chatMessageRepliesIcon(incoming: incoming), { theme in
|
||||||
|
let messageTheme = incoming ? theme.chat.message.incoming : theme.chat.message.outgoing
|
||||||
|
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BubbleReplies"), color: messageTheme.accentTextColor)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
public static func chatMessageCommentsArrowIcon(_ theme: PresentationTheme, incoming: Bool) -> UIImage? {
|
public static func chatMessageCommentsArrowIcon(_ theme: PresentationTheme, incoming: Bool) -> UIImage? {
|
||||||
return theme.image(PresentationResourceParameterKey.chatMessageCommentsArrowIcon(incoming: incoming), { theme in
|
return theme.image(PresentationResourceParameterKey.chatMessageCommentsArrowIcon(incoming: incoming), { theme in
|
||||||
let messageTheme = incoming ? theme.chat.message.incoming : theme.chat.message.outgoing
|
let messageTheme = incoming ? theme.chat.message.incoming : theme.chat.message.outgoing
|
||||||
|
12
submodules/TelegramUI/Images.xcassets/Avatar/RepliesMessagesIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Avatar/RepliesMessagesIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "repliesavatar.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Avatar/RepliesMessagesIcon.imageset/repliesavatar.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Avatar/RepliesMessagesIcon.imageset/repliesavatar.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Chat/Message/BubbleReplies.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Message/BubbleReplies.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Ic_viewinchat.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Chat/Message/BubbleReplies.imageset/Ic_viewinchat.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Message/BubbleReplies.imageset/Ic_viewinchat.pdf
vendored
Normal file
Binary file not shown.
Binary file not shown.
@ -182,7 +182,7 @@ final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
|||||||
self.badgeBackground.image = PresentationResourcesChatList.badgeBackgroundActive(interfaceState.theme, diameter: 20.0)
|
self.badgeBackground.image = PresentationResourcesChatList.badgeBackgroundActive(interfaceState.theme, diameter: 20.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if previousState?.peerDiscussionId != interfaceState.peerDiscussionId {
|
/*if previousState?.peerDiscussionId != interfaceState.peerDiscussionId {
|
||||||
let signal: Signal<Int?, NoError>
|
let signal: Signal<Int?, NoError>
|
||||||
if let peerDiscussionId = interfaceState.peerDiscussionId, let context = self.context {
|
if let peerDiscussionId = interfaceState.peerDiscussionId, let context = self.context {
|
||||||
let key = PostboxViewKey.unreadCounts(items: [.peer(peerDiscussionId)])
|
let key = PostboxViewKey.unreadCounts(items: [.peer(peerDiscussionId)])
|
||||||
@ -226,9 +226,9 @@ final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
|||||||
strongSelf.badgeText.isHidden = false
|
strongSelf.badgeText.isHidden = false
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}*/
|
||||||
|
|
||||||
if let peer = interfaceState.renderedPeer?.peer, previousState?.renderedPeer?.peer == nil || !peer.isEqual(previousState!.renderedPeer!.peer!) || previousState?.theme !== interfaceState.theme || previousState?.strings !== interfaceState.strings || previousState?.peerIsMuted != interfaceState.peerIsMuted || previousState?.peerDiscussionId != interfaceState.peerDiscussionId {
|
if let peer = interfaceState.renderedPeer?.peer, previousState?.renderedPeer?.peer == nil || !peer.isEqual(previousState!.renderedPeer!.peer!) || previousState?.theme !== interfaceState.theme || previousState?.strings !== interfaceState.strings || previousState?.peerIsMuted != interfaceState.peerIsMuted /*|| previousState?.peerDiscussionId != interfaceState.peerDiscussionId*/ {
|
||||||
if let action = actionForPeer(peer: peer, isMuted: interfaceState.peerIsMuted) {
|
if let action = actionForPeer(peer: peer, isMuted: interfaceState.peerIsMuted) {
|
||||||
self.action = action
|
self.action = action
|
||||||
let (title, color) = titleAndColorForAction(action, theme: interfaceState.theme, strings: interfaceState.strings)
|
let (title, color) = titleAndColorForAction(action, theme: interfaceState.theme, strings: interfaceState.strings)
|
||||||
@ -237,12 +237,12 @@ final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
|||||||
self.action = nil
|
self.action = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if interfaceState.peerDiscussionId != nil {
|
/*if interfaceState.peerDiscussionId != nil {
|
||||||
self.discussButtonText.attributedText = NSAttributedString(string: interfaceState.strings.Channel_DiscussionGroup_HeaderLabel, font: Font.regular(17.0), textColor: interfaceState.theme.chat.inputPanel.panelControlAccentColor)
|
self.discussButtonText.attributedText = NSAttributedString(string: interfaceState.strings.Channel_DiscussionGroup_HeaderLabel, font: Font.regular(17.0), textColor: interfaceState.theme.chat.inputPanel.panelControlAccentColor)
|
||||||
self.discussButton.isHidden = false
|
self.discussButton.isHidden = false
|
||||||
} else {
|
} else {*/
|
||||||
self.discussButton.isHidden = true
|
self.discussButton.isHidden = true
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2426,6 +2426,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
let imageOverride: AvatarNodeImageOverride?
|
let imageOverride: AvatarNodeImageOverride?
|
||||||
if strongSelf.context.account.peerId == peer.id {
|
if strongSelf.context.account.peerId == peer.id {
|
||||||
imageOverride = .savedMessagesIcon
|
imageOverride = .savedMessagesIcon
|
||||||
|
} else if peer.id.isReplies {
|
||||||
|
imageOverride = .repliesIcon
|
||||||
} else if peer.isDeleted {
|
} else if peer.isDeleted {
|
||||||
imageOverride = .deletedIcon
|
imageOverride = .deletedIcon
|
||||||
} else {
|
} else {
|
||||||
@ -2489,7 +2491,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
var peerGeoLocation: PeerGeoLocation?
|
var peerGeoLocation: PeerGeoLocation?
|
||||||
if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData {
|
if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData {
|
||||||
if case .broadcast = peer.info {
|
if case .broadcast = peer.info {
|
||||||
peerDiscussionId = cachedData.linkedDiscussionPeerId
|
if case let .known(value) = cachedData.linkedDiscussionPeerId {
|
||||||
|
peerDiscussionId = value
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
peerGeoLocation = cachedData.peerGeoLocation
|
peerGeoLocation = cachedData.peerGeoLocation
|
||||||
}
|
}
|
||||||
@ -2692,7 +2696,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
var peerGeoLocation: PeerGeoLocation?
|
var peerGeoLocation: PeerGeoLocation?
|
||||||
if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData {
|
if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData {
|
||||||
if case .broadcast = peer.info {
|
if case .broadcast = peer.info {
|
||||||
peerDiscussionId = cachedData.linkedDiscussionPeerId
|
if case let .known(value) = cachedData.linkedDiscussionPeerId {
|
||||||
|
peerDiscussionId = value
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
peerGeoLocation = cachedData.peerGeoLocation
|
peerGeoLocation = cachedData.peerGeoLocation
|
||||||
}
|
}
|
||||||
@ -3218,7 +3224,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
guard let strongSelf = self, let combinedInitialData = combinedInitialData else {
|
guard let strongSelf = self, let combinedInitialData = combinedInitialData else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let interfaceState = combinedInitialData.initialData?.chatInterfaceState as? ChatInterfaceState {
|
if var interfaceState = combinedInitialData.initialData?.chatInterfaceState as? ChatInterfaceState {
|
||||||
|
switch strongSelf.chatLocation {
|
||||||
|
case .peer:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
interfaceState = ChatInterfaceState()
|
||||||
|
}
|
||||||
|
|
||||||
var pinnedMessageId: MessageId?
|
var pinnedMessageId: MessageId?
|
||||||
var peerIsBlocked: Bool = false
|
var peerIsBlocked: Bool = false
|
||||||
var callsAvailable: Bool = true
|
var callsAvailable: Bool = true
|
||||||
|
@ -343,6 +343,7 @@ private final class ChatHistoryTransactionOpaqueState {
|
|||||||
private func extractAssociatedData(chatLocation: ChatLocation, view: MessageHistoryView, automaticDownloadNetworkType: MediaAutoDownloadNetworkType, animatedEmojiStickers: [String: [StickerPackItem]], isScheduledMessages: Bool) -> ChatMessageItemAssociatedData {
|
private func extractAssociatedData(chatLocation: ChatLocation, view: MessageHistoryView, automaticDownloadNetworkType: MediaAutoDownloadNetworkType, animatedEmojiStickers: [String: [StickerPackItem]], isScheduledMessages: Bool) -> ChatMessageItemAssociatedData {
|
||||||
var automaticMediaDownloadPeerType: MediaAutoDownloadPeerType = .channel
|
var automaticMediaDownloadPeerType: MediaAutoDownloadPeerType = .channel
|
||||||
var contactsPeerIds: Set<PeerId> = Set()
|
var contactsPeerIds: Set<PeerId> = Set()
|
||||||
|
var channelDiscussionGroup: ChatMessageItemAssociatedData.ChannelDiscussionGroupStatus = .unknown
|
||||||
if case let .peer(peerId) = chatLocation {
|
if case let .peer(peerId) = chatLocation {
|
||||||
if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.SecretChat {
|
if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.SecretChat {
|
||||||
var isContact = false
|
var isContact = false
|
||||||
@ -367,7 +368,15 @@ private func extractAssociatedData(chatLocation: ChatLocation, view: MessageHist
|
|||||||
if let channel = value as? TelegramChannel, case .group = channel.info {
|
if let channel = value as? TelegramChannel, case .group = channel.info {
|
||||||
automaticMediaDownloadPeerType = .group
|
automaticMediaDownloadPeerType = .group
|
||||||
}
|
}
|
||||||
break
|
} else if case let .cachedPeerData(dataPeerId, cachedData) = entry, dataPeerId == peerId {
|
||||||
|
if let cachedData = cachedData as? CachedChannelData {
|
||||||
|
switch cachedData.linkedDiscussionPeerId {
|
||||||
|
case let .known(value):
|
||||||
|
channelDiscussionGroup = .known(value)
|
||||||
|
case .unknown:
|
||||||
|
channelDiscussionGroup = .unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if automaticMediaDownloadPeerType == .group {
|
if automaticMediaDownloadPeerType == .group {
|
||||||
@ -379,7 +388,7 @@ private func extractAssociatedData(chatLocation: ChatLocation, view: MessageHist
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let associatedData = ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadNetworkType: automaticDownloadNetworkType, isRecentActions: false, isScheduledMessages: isScheduledMessages, contactsPeerIds: contactsPeerIds, animatedEmojiStickers: animatedEmojiStickers)
|
let associatedData = ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadNetworkType: automaticDownloadNetworkType, isRecentActions: false, isScheduledMessages: isScheduledMessages, contactsPeerIds: contactsPeerIds, channelDiscussionGroup: channelDiscussionGroup, animatedEmojiStickers: animatedEmojiStickers)
|
||||||
return associatedData
|
return associatedData
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1051,6 +1060,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
if message.id.namespace == Namespaces.Message.Cloud {
|
if message.id.namespace == Namespaces.Message.Cloud {
|
||||||
messageIdsWithViewCount.append(message.id)
|
messageIdsWithViewCount.append(message.id)
|
||||||
}
|
}
|
||||||
|
} else if attribute is ReplyThreadMessageAttribute {
|
||||||
|
if message.id.namespace == Namespaces.Message.Cloud {
|
||||||
|
messageIdsWithViewCount.append(message.id)
|
||||||
|
}
|
||||||
} else if let attribute = attribute as? ConsumableContentMessageAttribute, !attribute.consumed {
|
} else if let attribute = attribute as? ConsumableContentMessageAttribute, !attribute.consumed {
|
||||||
hasUnconsumedContent = true
|
hasUnconsumedContent = true
|
||||||
} else if let _ = attribute as? ContentRequiresValidationMessageAttribute {
|
} else if let _ = attribute as? ContentRequiresValidationMessageAttribute {
|
||||||
@ -1095,6 +1108,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
if message.id.namespace == Namespaces.Message.Cloud {
|
if message.id.namespace == Namespaces.Message.Cloud {
|
||||||
messageIdsWithViewCount.append(message.id)
|
messageIdsWithViewCount.append(message.id)
|
||||||
}
|
}
|
||||||
|
} else if attribute is ReplyThreadMessageAttribute {
|
||||||
|
if message.id.namespace == Namespaces.Message.Cloud {
|
||||||
|
messageIdsWithViewCount.append(message.id)
|
||||||
|
}
|
||||||
} else if let attribute = attribute as? ConsumableContentMessageAttribute, !attribute.consumed {
|
} else if let attribute = attribute as? ConsumableContentMessageAttribute, !attribute.consumed {
|
||||||
hasUnconsumedContent = true
|
hasUnconsumedContent = true
|
||||||
}
|
}
|
||||||
|
@ -371,27 +371,32 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
|||||||
return transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration) as? LimitsConfiguration ?? LimitsConfiguration.defaultValue
|
return transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration) as? LimitsConfiguration ?? LimitsConfiguration.defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
let dataSignal: Signal<(MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia]), NoError> = combineLatest(
|
let cachedData = context.account.postbox.transaction { transaction -> CachedPeerData? in
|
||||||
|
return transaction.getPeerCachedData(peerId: messages[0].id.peerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
let dataSignal: Signal<(MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?), NoError> = combineLatest(
|
||||||
loadLimits,
|
loadLimits,
|
||||||
loadStickerSaveStatusSignal,
|
loadStickerSaveStatusSignal,
|
||||||
loadResourceStatusSignal,
|
loadResourceStatusSignal,
|
||||||
context.sharedContext.chatAvailableMessageActions(postbox: context.account.postbox, accountPeerId: context.account.peerId, messageIds: Set(messages.map { $0.id })),
|
context.sharedContext.chatAvailableMessageActions(postbox: context.account.postbox, accountPeerId: context.account.peerId, messageIds: Set(messages.map { $0.id })),
|
||||||
context.account.pendingUpdateMessageManager.updatingMessageMedia
|
context.account.pendingUpdateMessageManager.updatingMessageMedia
|
||||||
|> take(1)
|
|> take(1),
|
||||||
|
cachedData
|
||||||
)
|
)
|
||||||
|> map { limitsConfiguration, stickerSaveStatus, resourceStatus, messageActions, updatingMessageMedia -> (MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia]) in
|
|> map { limitsConfiguration, stickerSaveStatus, resourceStatus, messageActions, updatingMessageMedia, cachedData -> (MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?) in
|
||||||
var canEdit = false
|
var canEdit = false
|
||||||
if !isAction {
|
if !isAction {
|
||||||
let message = messages[0]
|
let message = messages[0]
|
||||||
canEdit = canEditMessage(context: context, limitsConfiguration: limitsConfiguration, message: message)
|
canEdit = canEditMessage(context: context, limitsConfiguration: limitsConfiguration, message: message)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (MessageContextMenuData(starStatus: stickerSaveStatus, canReply: canReply, canPin: canPin, canEdit: canEdit, canSelect: canSelect, resourceStatus: resourceStatus, messageActions: messageActions), updatingMessageMedia)
|
return (MessageContextMenuData(starStatus: stickerSaveStatus, canReply: canReply, canPin: canPin, canEdit: canEdit, canSelect: canSelect, resourceStatus: resourceStatus, messageActions: messageActions), updatingMessageMedia, cachedData)
|
||||||
}
|
}
|
||||||
|
|
||||||
return dataSignal
|
return dataSignal
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> map { data, updatingMessageMedia -> [ContextMenuItem] in
|
|> map { data, updatingMessageMedia, cachedData -> [ContextMenuItem] in
|
||||||
var actions: [ContextMenuItem] = []
|
var actions: [ContextMenuItem] = []
|
||||||
|
|
||||||
if let starStatus = data.starStatus {
|
if let starStatus = data.starStatus {
|
||||||
@ -576,7 +581,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
|||||||
|
|
||||||
var threadId: Int64?
|
var threadId: Int64?
|
||||||
var threadMessageCount: Int = 0
|
var threadMessageCount: Int = 0
|
||||||
if case .peer = chatPresentationInterfaceState.chatLocation {
|
if case .peer = chatPresentationInterfaceState.chatLocation, let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, case .group = channel.info, let cachedData = cachedData as? CachedChannelData, case let .known(maybeValue) = cachedData.linkedDiscussionPeerId, let _ = maybeValue {
|
||||||
if let value = messages[0].threadId {
|
if let value = messages[0].threadId {
|
||||||
threadId = value
|
threadId = value
|
||||||
} else {
|
} else {
|
||||||
|
@ -922,7 +922,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside)
|
updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside)
|
||||||
}
|
}
|
||||||
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, message: item.message, account: item.context.account)
|
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, message: item.message, account: item.context.account)
|
||||||
updatedShareButtonNode.frame = CGRect(origin: CGPoint(x: updatedImageFrame.maxX + 8.0, y: updatedImageFrame.maxY - buttonSize.height), size: buttonSize)
|
updatedShareButtonNode.frame = CGRect(origin: CGPoint(x: updatedImageFrame.maxX + 8.0, y: updatedImageFrame.maxY - buttonSize.height - 4.0), size: buttonSize)
|
||||||
} else if let shareButtonNode = strongSelf.shareButtonNode {
|
} else if let shareButtonNode = strongSelf.shareButtonNode {
|
||||||
shareButtonNode.removeFromSupernode()
|
shareButtonNode.removeFromSupernode()
|
||||||
strongSelf.shareButtonNode = nil
|
strongSelf.shareButtonNode = nil
|
||||||
|
@ -41,9 +41,14 @@ enum ChatMessageBubbleMergeStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum ChatMessageBubbleRelativePosition {
|
enum ChatMessageBubbleRelativePosition {
|
||||||
|
enum NeighbourType {
|
||||||
|
case media
|
||||||
|
case freeform
|
||||||
|
}
|
||||||
|
|
||||||
case None(ChatMessageBubbleMergeStatus)
|
case None(ChatMessageBubbleMergeStatus)
|
||||||
case BubbleNeighbour
|
case BubbleNeighbour
|
||||||
case Neighbour(Bool)
|
case Neighbour(Bool, NeighbourType)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ChatMessageBubbleContentMosaicNeighbor {
|
enum ChatMessageBubbleContentMosaicNeighbor {
|
||||||
|
@ -46,11 +46,11 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(
|
|||||||
|
|
||||||
inner: for media in message.media {
|
inner: for media in message.media {
|
||||||
if let _ = media as? TelegramMediaImage {
|
if let _ = media as? TelegramMediaImage {
|
||||||
result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, false))
|
result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, true))
|
||||||
} else if let file = media as? TelegramMediaFile {
|
} else if let file = media as? TelegramMediaFile {
|
||||||
let isVideo = file.isVideo || (file.isAnimated && file.dimensions != nil)
|
let isVideo = file.isVideo || (file.isAnimated && file.dimensions != nil)
|
||||||
if isVideo {
|
if isVideo {
|
||||||
result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, false))
|
result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, true))
|
||||||
} else {
|
} else {
|
||||||
result.append((message, ChatMessageFileBubbleContentNode.self, itemAttributes, false))
|
result.append((message, ChatMessageFileBubbleContentNode.self, itemAttributes, false))
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(
|
|||||||
result.append((message, ChatMessageActionBubbleContentNode.self, itemAttributes, false))
|
result.append((message, ChatMessageActionBubbleContentNode.self, itemAttributes, false))
|
||||||
}
|
}
|
||||||
} else if let _ = media as? TelegramMediaMap {
|
} else if let _ = media as? TelegramMediaMap {
|
||||||
result.append((message, ChatMessageMapBubbleContentNode.self, itemAttributes, false))
|
result.append((message, ChatMessageMapBubbleContentNode.self, itemAttributes, true))
|
||||||
} else if let _ = media as? TelegramMediaGame {
|
} else if let _ = media as? TelegramMediaGame {
|
||||||
skipText = true
|
skipText = true
|
||||||
result.append((message, ChatMessageGameBubbleContentNode.self, itemAttributes, false))
|
result.append((message, ChatMessageGameBubbleContentNode.self, itemAttributes, false))
|
||||||
@ -134,8 +134,36 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let firstMessage = item.content.firstMessage
|
let firstMessage = item.content.firstMessage
|
||||||
if !isAction, let channel = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
|
if !isAction {
|
||||||
result.append((firstMessage, ChatMessageCommentFooterContentNode.self, ChatMessageEntryAttributes(), true))
|
var hasDiscussion = false
|
||||||
|
if let channel = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
|
||||||
|
hasDiscussion = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasDiscussion {
|
||||||
|
var canComment = false
|
||||||
|
if firstMessage.id.namespace == Namespaces.Message.Local {
|
||||||
|
canComment = true
|
||||||
|
} else {
|
||||||
|
for attribute in firstMessage.attributes {
|
||||||
|
if let attribute = attribute as? ReplyThreadMessageAttribute, let commentsPeerId = attribute.commentsPeerId {
|
||||||
|
switch item.associatedData.channelDiscussionGroup {
|
||||||
|
case .unknown:
|
||||||
|
canComment = true
|
||||||
|
case let .known(groupId):
|
||||||
|
canComment = groupId == commentsPeerId
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if canComment {
|
||||||
|
result.append((firstMessage, ChatMessageCommentFooterContentNode.self, ChatMessageEntryAttributes(), false))
|
||||||
|
}
|
||||||
|
} else if firstMessage.id.peerId.isReplies {
|
||||||
|
result.append((firstMessage, ChatMessageCommentFooterContentNode.self, ChatMessageEntryAttributes(), false))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -877,10 +905,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
var needShareButton = false
|
var needShareButton = false
|
||||||
if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
|
if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
|
||||||
needShareButton = false
|
needShareButton = false
|
||||||
} else if item.message.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) {
|
} else if item.message.id.peerId == item.context.account.peerId {
|
||||||
if let _ = sourceReference {
|
if let _ = sourceReference {
|
||||||
needShareButton = true
|
needShareButton = true
|
||||||
}
|
}
|
||||||
|
} else if item.message.id.peerId.isReplies {
|
||||||
|
needShareButton = false
|
||||||
} else if item.message.effectivelyIncoming(item.context.account.peerId) {
|
} else if item.message.effectivelyIncoming(item.context.account.peerId) {
|
||||||
if let _ = sourceReference {
|
if let _ = sourceReference {
|
||||||
needShareButton = true
|
needShareButton = true
|
||||||
@ -1074,8 +1104,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
let topPosition: ChatMessageBubbleRelativePosition
|
let topPosition: ChatMessageBubbleRelativePosition
|
||||||
let bottomPosition: ChatMessageBubbleRelativePosition
|
let bottomPosition: ChatMessageBubbleRelativePosition
|
||||||
|
|
||||||
topPosition = .Neighbour(false)
|
if index != 0 && contentPropertiesAndPrepareLayouts[index - 1].3 {
|
||||||
bottomPosition = .Neighbour(false)
|
topPosition = .Neighbour(true, .freeform)
|
||||||
|
} else {
|
||||||
|
topPosition = .Neighbour(false, .freeform)
|
||||||
|
}
|
||||||
|
bottomPosition = .Neighbour(false, .freeform)
|
||||||
|
|
||||||
let prepareContentPosition: ChatMessageBubblePreparePosition
|
let prepareContentPosition: ChatMessageBubblePreparePosition
|
||||||
if let mosaicRange = mosaicRange, mosaicRange.contains(index) {
|
if let mosaicRange = mosaicRange, mosaicRange.contains(index) {
|
||||||
@ -1148,7 +1182,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
} else {
|
} else {
|
||||||
if inlineBotNameString == nil && (ignoreForward || firstMessage.forwardInfo == nil) && replyMessage == nil {
|
if inlineBotNameString == nil && (ignoreForward || firstMessage.forwardInfo == nil) && replyMessage == nil {
|
||||||
if let first = contentPropertiesAndLayouts.first, first.1.hidesSimpleAuthorHeader {
|
if let first = contentPropertiesAndLayouts.first, first.1.hidesSimpleAuthorHeader {
|
||||||
initialDisplayHeader = false
|
if let author = firstMessage.author as? TelegramChannel, case .group = author.info, author.id == firstMessage.id.peerId, !incoming {
|
||||||
|
} else {
|
||||||
|
initialDisplayHeader = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1208,7 +1245,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
|
|
||||||
let firstNodeTopPosition: ChatMessageBubbleRelativePosition
|
let firstNodeTopPosition: ChatMessageBubbleRelativePosition
|
||||||
if displayHeader {
|
if displayHeader {
|
||||||
firstNodeTopPosition = .Neighbour(false)
|
firstNodeTopPosition = .Neighbour(false, .freeform)
|
||||||
} else {
|
} else {
|
||||||
firstNodeTopPosition = .None(topNodeMergeStatus)
|
firstNodeTopPosition = .None(topNodeMergeStatus)
|
||||||
}
|
}
|
||||||
@ -1502,7 +1539,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
if mosaicRange.upperBound - 1 == contentNodeCount - 1 {
|
if mosaicRange.upperBound - 1 == contentNodeCount - 1 {
|
||||||
lastMosaicBottomPosition = lastNodeTopPosition
|
lastMosaicBottomPosition = lastNodeTopPosition
|
||||||
} else {
|
} else {
|
||||||
lastMosaicBottomPosition = .Neighbour(false)
|
lastMosaicBottomPosition = .Neighbour(false, .freeform)
|
||||||
}
|
}
|
||||||
|
|
||||||
if position.contains(.bottom), case .Neighbour = lastMosaicBottomPosition {
|
if position.contains(.bottom), case .Neighbour = lastMosaicBottomPosition {
|
||||||
@ -1567,22 +1604,22 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
topPosition = firstNodeTopPosition
|
topPosition = firstNodeTopPosition
|
||||||
|
} else if i == contentNodeCount - 1 && i != 0 && contentPropertiesAndLayouts[i - 1].3 {
|
||||||
|
topPosition = .Neighbour(true, .freeform)
|
||||||
} else {
|
} else {
|
||||||
topPosition = .Neighbour(false)
|
topPosition = .Neighbour(false, .freeform)
|
||||||
}
|
}
|
||||||
|
|
||||||
if i == contentNodeCount - 1 {
|
if i == contentNodeCount - 1 {
|
||||||
bottomPosition = lastNodeTopPosition
|
bottomPosition = lastNodeTopPosition
|
||||||
} else if i == contentNodeCount - 2 && contentPropertiesAndLayouts[contentNodeCount - 1].3 {
|
|
||||||
bottomPosition = .Neighbour(true)
|
|
||||||
} else {
|
} else {
|
||||||
bottomPosition = .Neighbour(false)
|
bottomPosition = .Neighbour(false, .freeform)
|
||||||
}
|
}
|
||||||
|
|
||||||
contentPosition = .linear(top: topPosition, bottom: bottomPosition)
|
contentPosition = .linear(top: topPosition, bottom: bottomPosition)
|
||||||
case .mosaic:
|
case .mosaic:
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
contentPosition = .linear(top: .Neighbour(false), bottom: .Neighbour(false))
|
contentPosition = .linear(top: .Neighbour(false, .freeform), bottom: .Neighbour(false, .freeform))
|
||||||
}
|
}
|
||||||
let (contentNodeWidth, contentNodeFinalize) = contentNodeLayout(CGSize(width: maximumNodeWidth, height: CGFloat.greatestFiniteMagnitude), contentPosition)
|
let (contentNodeWidth, contentNodeFinalize) = contentNodeLayout(CGSize(width: maximumNodeWidth, height: CGFloat.greatestFiniteMagnitude), contentPosition)
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
@ -81,25 +81,35 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
|
|||||||
guard let item = self.item else {
|
guard let item = self.item else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item.controllerInteraction.openMessageReplies(item.message.id)
|
if item.message.id.peerId.isReplies {
|
||||||
|
for attribute in item.message.attributes {
|
||||||
|
if let attribute = attribute as? SourceReferenceMessageAttribute {
|
||||||
|
item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
item.controllerInteraction.openMessageReplies(item.message.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (ChatMessageBubbleContentProperties, CGSize?, CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void))) {
|
override func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (ChatMessageBubbleContentProperties, CGSize?, CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void))) {
|
||||||
let textLayout = TextNode.asyncLayout(self.textNode)
|
let textLayout = TextNode.asyncLayout(self.textNode)
|
||||||
|
|
||||||
return { item, layoutConstants, _, _, constrainedSize in
|
return { item, layoutConstants, preparePosition, _, constrainedSize in
|
||||||
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)
|
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)
|
||||||
|
|
||||||
|
let displaySeparator: Bool
|
||||||
|
if case let .linear(top, _) = preparePosition, case .Neighbour(true, _) = top {
|
||||||
|
displaySeparator = false
|
||||||
|
} else {
|
||||||
|
displaySeparator = true
|
||||||
|
}
|
||||||
|
|
||||||
return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in
|
return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in
|
||||||
let incoming = item.message.effectivelyIncoming(item.context.account.peerId)
|
let incoming = item.message.effectivelyIncoming(item.context.account.peerId)
|
||||||
|
|
||||||
var maxTextWidth = CGFloat.greatestFiniteMagnitude
|
let maxTextWidth = CGFloat.greatestFiniteMagnitude
|
||||||
for media in item.message.media {
|
|
||||||
if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, content.type == "telegram_background" || content.type == "telegram_theme" {
|
|
||||||
maxTextWidth = layoutConstants.wallpapers.maxTextWidth
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let horizontalInset = layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right
|
let horizontalInset = layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right
|
||||||
|
|
||||||
@ -117,7 +127,9 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
|
|||||||
//TODO:localize
|
//TODO:localize
|
||||||
let rawText: String
|
let rawText: String
|
||||||
|
|
||||||
if dateReplies > 0 {
|
if item.message.id.peerId.isReplies {
|
||||||
|
rawText = "View Reply"
|
||||||
|
} else if dateReplies > 0 {
|
||||||
if dateReplies == 1 {
|
if dateReplies == 1 {
|
||||||
rawText = "1 Comment"
|
rawText = "1 Comment"
|
||||||
} else {
|
} else {
|
||||||
@ -132,12 +144,12 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
|
|||||||
|
|
||||||
var textLeftInset: CGFloat = 0.0
|
var textLeftInset: CGFloat = 0.0
|
||||||
if replyPeers.isEmpty {
|
if replyPeers.isEmpty {
|
||||||
textLeftInset = 32.0
|
textLeftInset = 41.0
|
||||||
} else {
|
} else {
|
||||||
textLeftInset = 8.0 + imageSize * min(1.0, CGFloat(replyPeers.count)) + imageSpacing * max(0.0, min(2.0, CGFloat(replyPeers.count - 1)))
|
textLeftInset = 15.0 + imageSize * min(1.0, CGFloat(replyPeers.count)) + (imageSpacing) * max(0.0, min(2.0, CGFloat(replyPeers.count - 1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
let textConstrainedSize = CGSize(width: min(maxTextWidth, constrainedSize.width - horizontalInset - textLeftInset - 20.0), height: constrainedSize.height)
|
let textConstrainedSize = CGSize(width: min(maxTextWidth, constrainedSize.width - horizontalInset - textLeftInset - 28.0), height: constrainedSize.height)
|
||||||
|
|
||||||
let attributedText: NSAttributedString
|
let attributedText: NSAttributedString
|
||||||
|
|
||||||
@ -154,14 +166,22 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
|
|||||||
var textFrame = CGRect(origin: CGPoint(x: -textInsets.left + textLeftInset, y: -textInsets.top + 5.0), size: textLayout.size)
|
var textFrame = CGRect(origin: CGPoint(x: -textInsets.left + textLeftInset, y: -textInsets.top + 5.0), size: textLayout.size)
|
||||||
var textFrameWithoutInsets = CGRect(origin: CGPoint(x: textFrame.origin.x + textInsets.left, y: textFrame.origin.y + textInsets.top), size: CGSize(width: textFrame.width - textInsets.left - textInsets.right, height: textFrame.height - textInsets.top - textInsets.bottom))
|
var textFrameWithoutInsets = CGRect(origin: CGPoint(x: textFrame.origin.x + textInsets.left, y: textFrame.origin.y + textInsets.top), size: CGSize(width: textFrame.width - textInsets.left - textInsets.right, height: textFrame.height - textInsets.top - textInsets.bottom))
|
||||||
|
|
||||||
textFrame = textFrame.offsetBy(dx: layoutConstants.text.bubbleInsets.left, dy: layoutConstants.text.bubbleInsets.top - 2.0)
|
textFrame = textFrame.offsetBy(dx: layoutConstants.text.bubbleInsets.left, dy: layoutConstants.text.bubbleInsets.top - 5.0 + UIScreenPixel)
|
||||||
textFrameWithoutInsets = textFrameWithoutInsets.offsetBy(dx: layoutConstants.text.bubbleInsets.left, dy: layoutConstants.text.bubbleInsets.top)
|
textFrameWithoutInsets = textFrameWithoutInsets.offsetBy(dx: layoutConstants.text.bubbleInsets.left, dy: layoutConstants.text.bubbleInsets.top)
|
||||||
|
|
||||||
var suggestedBoundingWidth: CGFloat
|
var suggestedBoundingWidth: CGFloat
|
||||||
suggestedBoundingWidth = textFrameWithoutInsets.width
|
suggestedBoundingWidth = textFrameWithoutInsets.width
|
||||||
suggestedBoundingWidth += layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right + textLeftInset + 20.0
|
suggestedBoundingWidth += layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right + textLeftInset + 28.0
|
||||||
|
|
||||||
let iconImage = PresentationResourcesChat.chatMessageCommentsIcon(item.presentationData.theme.theme, incoming: incoming)
|
let iconImage: UIImage?
|
||||||
|
let iconOffset: CGPoint
|
||||||
|
if item.message.id.peerId.isReplies {
|
||||||
|
iconImage = PresentationResourcesChat.chatMessageRepliesIcon(item.presentationData.theme.theme, incoming: incoming)
|
||||||
|
iconOffset = CGPoint(x: -4.0, y: -4.0)
|
||||||
|
} else {
|
||||||
|
iconImage = PresentationResourcesChat.chatMessageCommentsIcon(item.presentationData.theme.theme, incoming: incoming)
|
||||||
|
iconOffset = CGPoint()
|
||||||
|
}
|
||||||
let arrowImage = PresentationResourcesChat.chatMessageCommentsArrowIcon(item.presentationData.theme.theme, incoming: incoming)
|
let arrowImage = PresentationResourcesChat.chatMessageCommentsArrowIcon(item.presentationData.theme.theme, incoming: incoming)
|
||||||
|
|
||||||
return (suggestedBoundingWidth, { boundingWidth in
|
return (suggestedBoundingWidth, { boundingWidth in
|
||||||
@ -169,7 +189,7 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
|
|||||||
|
|
||||||
boundingSize = textFrameWithoutInsets.size
|
boundingSize = textFrameWithoutInsets.size
|
||||||
boundingSize.width += layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right
|
boundingSize.width += layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right
|
||||||
boundingSize.height = 42.0
|
boundingSize.height = 40.0
|
||||||
|
|
||||||
return (boundingSize, { [weak self] animation, synchronousLoad in
|
return (boundingSize, { [weak self] animation, synchronousLoad in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
@ -205,23 +225,24 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
|
|||||||
|
|
||||||
if let iconImage = iconImage {
|
if let iconImage = iconImage {
|
||||||
strongSelf.iconNode.image = iconImage
|
strongSelf.iconNode.image = iconImage
|
||||||
strongSelf.iconNode.frame = CGRect(origin: CGPoint(x: 10.0, y: 7.0), size: iconImage.size)
|
strongSelf.iconNode.frame = CGRect(origin: CGPoint(x: 15.0 + iconOffset.x, y: 6.0 + iconOffset.y), size: iconImage.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let arrowImage = arrowImage {
|
if let arrowImage = arrowImage {
|
||||||
strongSelf.arrowNode.image = arrowImage
|
strongSelf.arrowNode.image = arrowImage
|
||||||
strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: boundingWidth - 27.0, y: 8.0), size: arrowImage.size)
|
strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: boundingWidth - 33.0, y: 6.0), size: arrowImage.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.iconNode.isHidden = !replyPeers.isEmpty
|
strongSelf.iconNode.isHidden = !replyPeers.isEmpty
|
||||||
|
|
||||||
let avatarsFrame = CGRect(origin: CGPoint(x: 10.0, y: 5.0), size: CGSize(width: imageSize * 3.0, height: imageSize))
|
let avatarsFrame = CGRect(origin: CGPoint(x: 13.0, y: 3.0), size: CGSize(width: imageSize * 3.0, height: imageSize))
|
||||||
strongSelf.avatarsNode.frame = avatarsFrame
|
strongSelf.avatarsNode.frame = avatarsFrame
|
||||||
strongSelf.avatarsNode.updateLayout(size: avatarsFrame.size)
|
strongSelf.avatarsNode.updateLayout(size: avatarsFrame.size)
|
||||||
strongSelf.avatarsNode.update(context: item.context, peers: replyPeers, synchronousLoad: synchronousLoad, imageSize: imageSize, imageSpacing: imageSpacing, borderWidth: 2.0)
|
strongSelf.avatarsNode.update(context: item.context, peers: replyPeers, synchronousLoad: synchronousLoad, imageSize: imageSize, imageSpacing: imageSpacing, borderWidth: 2.0 - UIScreenPixel)
|
||||||
|
|
||||||
strongSelf.separatorNode.backgroundColor = messageTheme.polls.separator
|
strongSelf.separatorNode.backgroundColor = messageTheme.polls.separator
|
||||||
strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -1.0), size: CGSize(width: boundingWidth, height: UIScreenPixel))
|
strongSelf.separatorNode.isHidden = !displaySeparator
|
||||||
|
strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: layoutConstants.bubble.strokeInsets.left, y: -3.0), size: CGSize(width: boundingWidth - layoutConstants.bubble.strokeInsets.left - layoutConstants.bubble.strokeInsets.right, height: UIScreenPixel))
|
||||||
|
|
||||||
strongSelf.buttonNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: boundingWidth, height: boundingSize.height))
|
strongSelf.buttonNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: boundingWidth, height: boundingSize.height))
|
||||||
}
|
}
|
||||||
|
@ -131,7 +131,7 @@ private func messagesShouldBeMerged(accountPeerId: PeerId, _ lhs: Message, _ rhs
|
|||||||
}
|
}
|
||||||
|
|
||||||
var sameAuthor = false
|
var sameAuthor = false
|
||||||
if lhsEffectiveAuthor?.id == rhsEffectiveAuthor?.id {
|
if lhsEffectiveAuthor?.id == rhsEffectiveAuthor?.id && lhs.effectivelyIncoming(accountPeerId) == rhs.effectivelyIncoming(accountPeerId) {
|
||||||
sameAuthor = true
|
sameAuthor = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
if activeLiveBroadcastingTimeout != nil || selectedMedia?.venue != nil {
|
if activeLiveBroadcastingTimeout != nil || selectedMedia?.venue != nil {
|
||||||
var relativePosition = position
|
var relativePosition = position
|
||||||
if case let .linear(top, _) = position {
|
if case let .linear(top, _) = position {
|
||||||
relativePosition = .linear(top: top, bottom: .Neighbour(false))
|
relativePosition = .linear(top: top, bottom: .Neighbour(false, .freeform))
|
||||||
}
|
}
|
||||||
imageCorners = chatMessageBubbleImageContentCorners(relativeContentPosition: relativePosition, normalRadius: layoutConstants.image.defaultCornerRadius, mergedRadius: layoutConstants.image.mergedCornerRadius, mergedWithAnotherContentRadius: layoutConstants.image.contentMergedCornerRadius, layoutConstants: layoutConstants, chatPresentationData: item.presentationData)
|
imageCorners = chatMessageBubbleImageContentCorners(relativeContentPosition: relativePosition, normalRadius: layoutConstants.image.defaultCornerRadius, mergedRadius: layoutConstants.image.mergedCornerRadius, mergedWithAnotherContentRadius: layoutConstants.image.contentMergedCornerRadius, layoutConstants: layoutConstants, chatPresentationData: item.presentationData)
|
||||||
|
|
||||||
|
@ -516,7 +516,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside)
|
updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside)
|
||||||
}
|
}
|
||||||
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, message: item.message, account: item.context.account)
|
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, message: item.message, account: item.context.account)
|
||||||
var shareButtonFrame = CGRect(origin: CGPoint(x: updatedImageFrame.maxX + 8.0, y: updatedImageFrame.maxY - 10.0 - buttonSize.height), size: buttonSize)
|
var shareButtonFrame = CGRect(origin: CGPoint(x: updatedImageFrame.maxX + 6.0, y: updatedImageFrame.maxY - 10.0 - buttonSize.height - 4.0), size: buttonSize)
|
||||||
if isEmoji && incoming {
|
if isEmoji && incoming {
|
||||||
shareButtonFrame.origin.x = dateAndStatusFrame.maxX + 8.0
|
shareButtonFrame.origin.x = dateAndStatusFrame.maxX + 8.0
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
case let .linear(_, neighbor):
|
case let .linear(_, neighbor):
|
||||||
if case .None = neighbor {
|
if case .None = neighbor {
|
||||||
displayStatus = true
|
displayStatus = true
|
||||||
} else if case .Neighbour(true) = neighbor {
|
} else if case .Neighbour(true, _) = neighbor {
|
||||||
displayStatus = true
|
displayStatus = true
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -18,6 +18,8 @@ import CounterContollerTitleView
|
|||||||
private func peerTokenTitle(accountPeerId: PeerId, peer: Peer, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) -> String {
|
private func peerTokenTitle(accountPeerId: PeerId, peer: Peer, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) -> String {
|
||||||
if peer.id == accountPeerId {
|
if peer.id == accountPeerId {
|
||||||
return strings.DialogList_SavedMessages
|
return strings.DialogList_SavedMessages
|
||||||
|
} else if peer.id.isReplies {
|
||||||
|
return strings.DialogList_Replies
|
||||||
} else {
|
} else {
|
||||||
return peer.displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
return peer.displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
||||||
}
|
}
|
||||||
|
@ -638,7 +638,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
}
|
}
|
||||||
|
|
||||||
var discussionPeer: Peer?
|
var discussionPeer: Peer?
|
||||||
if let linkedDiscussionPeerId = (peerView.cachedData as? CachedChannelData)?.linkedDiscussionPeerId, let peer = peerView.peers[linkedDiscussionPeerId] {
|
if case let .known(maybeLinkedDiscussionPeerId) = (peerView.cachedData as? CachedChannelData)?.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = peerView.peers[linkedDiscussionPeerId] {
|
||||||
discussionPeer = peer
|
discussionPeer = peer
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -767,7 +767,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
}
|
}
|
||||||
|
|
||||||
var discussionPeer: Peer?
|
var discussionPeer: Peer?
|
||||||
if let linkedDiscussionPeerId = (peerView.cachedData as? CachedChannelData)?.linkedDiscussionPeerId, let peer = peerView.peers[linkedDiscussionPeerId] {
|
if case let .known(maybeLinkedDiscussionPeerId) = (peerView.cachedData as? CachedChannelData)?.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = peerView.peers[linkedDiscussionPeerId] {
|
||||||
discussionPeer = peer
|
discussionPeer = peer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2851,6 +2851,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
if let peer = peer {
|
if let peer = peer {
|
||||||
if peer.id == self.context.account.peerId && !self.isSettings {
|
if peer.id == self.context.account.peerId && !self.isSettings {
|
||||||
titleString = NSAttributedString(string: presentationData.strings.Conversation_SavedMessages, font: Font.medium(24.0), textColor: presentationData.theme.list.itemPrimaryTextColor)
|
titleString = NSAttributedString(string: presentationData.strings.Conversation_SavedMessages, font: Font.medium(24.0), textColor: presentationData.theme.list.itemPrimaryTextColor)
|
||||||
|
} else if peer.id == self.context.account.peerId && !self.isSettings {
|
||||||
|
titleString = NSAttributedString(string: presentationData.strings.DialogList_Replies, font: Font.medium(24.0), textColor: presentationData.theme.list.itemPrimaryTextColor)
|
||||||
} else {
|
} else {
|
||||||
titleString = NSAttributedString(string: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), font: Font.medium(24.0), textColor: presentationData.theme.list.itemPrimaryTextColor)
|
titleString = NSAttributedString(string: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), font: Font.medium(24.0), textColor: presentationData.theme.list.itemPrimaryTextColor)
|
||||||
}
|
}
|
||||||
@ -3299,14 +3301,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
if buttonKeys.count > 3 {
|
if buttonKeys.count > 3 {
|
||||||
if self.isOpenedFromChat {
|
if self.isOpenedFromChat {
|
||||||
switch buttonKey {
|
switch buttonKey {
|
||||||
case .message, .search, .videoCall:
|
case .message, .search, .mute:
|
||||||
hiddenWhileExpanded = true
|
hiddenWhileExpanded = true
|
||||||
default:
|
default:
|
||||||
hiddenWhileExpanded = false
|
hiddenWhileExpanded = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch buttonKey {
|
switch buttonKey {
|
||||||
case .mute, .search, .videoCall:
|
case .mute, .search, .mute:
|
||||||
hiddenWhileExpanded = true
|
hiddenWhileExpanded = true
|
||||||
default:
|
default:
|
||||||
hiddenWhileExpanded = false
|
hiddenWhileExpanded = false
|
||||||
|
@ -2826,7 +2826,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
case .discussion:
|
case .discussion:
|
||||||
if let cachedData = self.data?.cachedData as? CachedChannelData, let linkedDiscussionPeerId = cachedData.linkedDiscussionPeerId {
|
if let cachedData = self.data?.cachedData as? CachedChannelData, case let .known(maybeLinkedDiscussionPeerId) = cachedData.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId {
|
||||||
if let navigationController = controller.navigationController as? NavigationController {
|
if let navigationController = controller.navigationController as? NavigationController {
|
||||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(linkedDiscussionPeerId)))
|
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(linkedDiscussionPeerId)))
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user