mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
c4c05851a7
@ -1067,6 +1067,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
|
|
||||||
if case let .channel(channel) = peer, channel.flags.contains(.isMonoforum) {
|
if case let .channel(channel) = peer, channel.flags.contains(.isMonoforum) {
|
||||||
openAsInlineForum = false
|
openAsInlineForum = false
|
||||||
|
} else if case let .channel(channel) = peer, channel.flags.contains(.displayForumAsTabs) {
|
||||||
|
openAsInlineForum = false
|
||||||
} else {
|
} else {
|
||||||
if let cachedData = cachedDataView.cachedPeerData as? CachedChannelData, case let .known(viewForumAsMessages) = cachedData.viewForumAsMessages, viewForumAsMessages {
|
if let cachedData = cachedDataView.cachedPeerData as? CachedChannelData, case let .known(viewForumAsMessages) = cachedData.viewForumAsMessages, viewForumAsMessages {
|
||||||
openAsInlineForum = false
|
openAsInlineForum = false
|
||||||
@ -1519,7 +1521,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
if let threadId = threadId {
|
if let threadId = threadId {
|
||||||
let source: ContextContentSource
|
let source: ContextContentSource
|
||||||
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .replyThread(message: ChatReplyThreadMessage(
|
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .replyThread(message: ChatReplyThreadMessage(
|
||||||
peerId: peer.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: !channel.isMonoForum, isMonoforumPost: channel.isMonoForum, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
|
peerId: peer.peerId, threadId: threadId, channelMessageId: nil, isChannelPost: false, isForumPost: channel.isForumOrMonoForum, isMonoforumPost: channel.isMonoForum, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false
|
||||||
)), subject: nil, botStart: nil, mode: .standard(.previewing), params: nil)
|
)), subject: nil, botStart: nil, mode: .standard(.previewing), params: nil)
|
||||||
chatController.canReadHistory.set(false)
|
chatController.canReadHistory.set(false)
|
||||||
source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController))
|
source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController))
|
||||||
|
@ -463,6 +463,7 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour {
|
|||||||
|
|
||||||
public static let toggleUnread = Actions(rawValue: 1 << 0)
|
public static let toggleUnread = Actions(rawValue: 1 << 0)
|
||||||
public static let delete = Actions(rawValue: 1 << 1)
|
public static let delete = Actions(rawValue: 1 << 1)
|
||||||
|
public static let togglePinned = Actions(rawValue: 1 << 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
case custom(Actions)
|
case custom(Actions)
|
||||||
@ -994,6 +995,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
final class TopicItemNode: ASDisplayNode {
|
final class TopicItemNode: ASDisplayNode {
|
||||||
let topicTitleNode: TextNode
|
let topicTitleNode: TextNode
|
||||||
let titleTopicIconView: ComponentHostView<Empty>?
|
let titleTopicIconView: ComponentHostView<Empty>?
|
||||||
|
var titleTopicAvatarNode: AvatarNode?
|
||||||
var titleTopicIconComponent: EmojiStatusComponent?
|
var titleTopicIconComponent: EmojiStatusComponent?
|
||||||
|
|
||||||
var visibilityStatus: Bool = false {
|
var visibilityStatus: Bool = false {
|
||||||
@ -1011,30 +1013,34 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private init(topicTitleNode: TextNode, titleTopicIconView: ComponentHostView<Empty>?, titleTopicIconComponent: EmojiStatusComponent?) {
|
private init(topicTitleNode: TextNode, titleTopicIconView: ComponentHostView<Empty>?, titleTopicAvatarNode: AvatarNode?, titleTopicIconComponent: EmojiStatusComponent?) {
|
||||||
self.topicTitleNode = topicTitleNode
|
self.topicTitleNode = topicTitleNode
|
||||||
self.titleTopicIconView = titleTopicIconView
|
self.titleTopicIconView = titleTopicIconView
|
||||||
|
self.titleTopicAvatarNode = titleTopicAvatarNode
|
||||||
self.titleTopicIconComponent = titleTopicIconComponent
|
self.titleTopicIconComponent = titleTopicIconComponent
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.addSubnode(self.topicTitleNode)
|
self.addSubnode(self.topicTitleNode)
|
||||||
|
if let titleTopicAvatarNode = self.titleTopicAvatarNode {
|
||||||
|
self.view.addSubview(titleTopicAvatarNode.view)
|
||||||
|
}
|
||||||
if let titleTopicIconView = self.titleTopicIconView {
|
if let titleTopicIconView = self.titleTopicIconView {
|
||||||
self.view.addSubview(titleTopicIconView)
|
self.view.addSubview(titleTopicIconView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func asyncLayout(_ currentNode: TopicItemNode?) -> (_ constrainedWidth: CGFloat, _ context: AccountContext, _ theme: PresentationTheme, _ threadId: Int64, _ title: NSAttributedString, _ iconId: Int64?, _ iconColor: Int32?) -> (CGSize, () -> TopicItemNode) {
|
static func asyncLayout(_ currentNode: TopicItemNode?) -> (_ constrainedWidth: CGFloat, _ context: AccountContext, _ theme: PresentationTheme, _ threadId: Int64, _ threadPeer: EnginePeer?, _ title: NSAttributedString, _ iconId: Int64?, _ iconColor: Int32?) -> (CGSize, () -> TopicItemNode) {
|
||||||
let makeTopicTitleLayout = TextNode.asyncLayout(currentNode?.topicTitleNode)
|
let makeTopicTitleLayout = TextNode.asyncLayout(currentNode?.topicTitleNode)
|
||||||
|
|
||||||
return { constrainedWidth, context, theme, threadId, title, iconId, iconColor in
|
return { constrainedWidth, context, theme, threadId, threadPeer, title, iconId, iconColor in
|
||||||
let remainingWidth = max(1.0, constrainedWidth - (((iconId == nil && iconColor == nil) ? 1.0 : 18.0) + 2.0))
|
let remainingWidth = max(1.0, constrainedWidth - (((iconId == nil && iconColor == nil && threadPeer == nil) ? 1.0 : 18.0) + 2.0))
|
||||||
|
|
||||||
let topicTitleArguments = TextNodeLayoutArguments(attributedString: title, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: remainingWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 1.0, bottom: 2.0, right: 1.0))
|
let topicTitleArguments = TextNodeLayoutArguments(attributedString: title, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: remainingWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 1.0, bottom: 2.0, right: 1.0))
|
||||||
|
|
||||||
let topicTitleLayout = makeTopicTitleLayout(topicTitleArguments)
|
let topicTitleLayout = makeTopicTitleLayout(topicTitleArguments)
|
||||||
|
|
||||||
return (CGSize(width: ((iconId == nil && iconColor == nil) ? 1.0 : 18.0) + 2.0 + topicTitleLayout.0.size.width, height: topicTitleLayout.0.size.height), {
|
return (CGSize(width: ((iconId == nil && iconColor == nil && threadPeer == nil) ? 1.0 : 18.0) + 2.0 + topicTitleLayout.0.size.width, height: topicTitleLayout.0.size.height), {
|
||||||
let topicTitleNode = topicTitleLayout.1()
|
let topicTitleNode = topicTitleLayout.1()
|
||||||
|
|
||||||
let titleTopicIconContent: EmojiStatusComponent.Content?
|
let titleTopicIconContent: EmojiStatusComponent.Content?
|
||||||
@ -1068,7 +1074,16 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let targetNode = currentNode ?? TopicItemNode(topicTitleNode: topicTitleNode, titleTopicIconView: titleTopicIconView, titleTopicIconComponent: titleTopicIconComponent)
|
var titleTopicAvatarNode: AvatarNode?
|
||||||
|
if let _ = threadPeer {
|
||||||
|
if let current = currentNode?.titleTopicAvatarNode {
|
||||||
|
titleTopicAvatarNode = current
|
||||||
|
} else {
|
||||||
|
titleTopicAvatarNode = AvatarNode(font: avatarPlaceholderFont(size: 8.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let targetNode = currentNode ?? TopicItemNode(topicTitleNode: topicTitleNode, titleTopicIconView: titleTopicIconView, titleTopicAvatarNode: titleTopicAvatarNode, titleTopicIconComponent: titleTopicIconComponent)
|
||||||
|
|
||||||
targetNode.titleTopicIconComponent = titleTopicIconComponent
|
targetNode.titleTopicIconComponent = titleTopicIconComponent
|
||||||
|
|
||||||
@ -1081,6 +1096,18 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
)
|
)
|
||||||
titleTopicIconView.frame = CGRect(origin: CGPoint(x: 0.0, y: 2.0), size: iconSize)
|
titleTopicIconView.frame = CGRect(origin: CGPoint(x: 0.0, y: 2.0), size: iconSize)
|
||||||
|
|
||||||
|
topicTitleNode.frame = CGRect(origin: CGPoint(x: 18.0 + 2.0, y: 0.0), size: topicTitleLayout.0.size)
|
||||||
|
} else if let titleTopicAvatarNode, let threadPeer {
|
||||||
|
let iconSize = CGSize(width: 18.0, height: 18.0)
|
||||||
|
|
||||||
|
titleTopicAvatarNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 2.0), size: iconSize)
|
||||||
|
titleTopicAvatarNode.updateSize(size: iconSize)
|
||||||
|
if threadPeer.smallProfileImage != nil {
|
||||||
|
titleTopicAvatarNode.setPeerV2(context: context, theme: theme, peer: threadPeer, overrideImage: nil, emptyColor: theme.list.mediaPlaceholderColor, clipStyle: .round, synchronousLoad: false, displayDimensions: iconSize)
|
||||||
|
} else {
|
||||||
|
titleTopicAvatarNode.setPeer(context: context, theme: theme, peer: threadPeer, overrideImage: nil, emptyColor: theme.list.mediaPlaceholderColor, clipStyle: .round, synchronousLoad: false, displayDimensions: iconSize)
|
||||||
|
}
|
||||||
|
|
||||||
topicTitleNode.frame = CGRect(origin: CGPoint(x: 18.0 + 2.0, y: 0.0), size: topicTitleLayout.0.size)
|
topicTitleNode.frame = CGRect(origin: CGPoint(x: 18.0 + 2.0, y: 0.0), size: topicTitleLayout.0.size)
|
||||||
} else {
|
} else {
|
||||||
topicTitleNode.frame = CGRect(origin: CGPoint(x: 1.0, y: 0.0), size: topicTitleLayout.0.size)
|
topicTitleNode.frame = CGRect(origin: CGPoint(x: 1.0, y: 0.0), size: topicTitleLayout.0.size)
|
||||||
@ -1144,9 +1171,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func asyncLayout() -> (_ context: AccountContext, _ constrainedWidth: CGFloat, _ theme: PresentationTheme, _ authorTitle: NSAttributedString?, _ topics: [(id: Int64, title: NSAttributedString, iconId: Int64?, iconColor: Int32?)]) -> (CGSize, () -> CGRect?) {
|
func asyncLayout() -> (_ context: AccountContext, _ constrainedWidth: CGFloat, _ theme: PresentationTheme, _ authorTitle: NSAttributedString?, _ topics: [(id: Int64, threadPeer: EnginePeer?, title: NSAttributedString, iconId: Int64?, iconColor: Int32?)]) -> (CGSize, () -> CGRect?) {
|
||||||
let makeAuthorLayout = TextNode.asyncLayout(self.authorNode)
|
let makeAuthorLayout = TextNode.asyncLayout(self.authorNode)
|
||||||
var makeExistingTopicLayouts: [Int64: (_ constrainedWidth: CGFloat, _ context: AccountContext, _ theme: PresentationTheme, _ threadId: Int64, _ title: NSAttributedString, _ iconId: Int64?, _ iconColor: Int32?) -> (CGSize, () -> TopicItemNode)] = [:]
|
var makeExistingTopicLayouts: [Int64: (_ constrainedWidth: CGFloat, _ context: AccountContext, _ theme: PresentationTheme, _ threadId: Int64, _ threadPeer: EnginePeer?, _ title: NSAttributedString, _ iconId: Int64?, _ iconColor: Int32?) -> (CGSize, () -> TopicItemNode)] = [:]
|
||||||
for (topicId, topicNode) in self.topicNodes {
|
for (topicId, topicNode) in self.topicNodes {
|
||||||
makeExistingTopicLayouts[topicId] = TopicItemNode.asyncLayout(topicNode)
|
makeExistingTopicLayouts[topicId] = TopicItemNode.asyncLayout(topicNode)
|
||||||
}
|
}
|
||||||
@ -1178,7 +1205,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let makeTopicLayout = makeExistingTopicLayouts[topic.id] ?? TopicItemNode.asyncLayout(nil)
|
let makeTopicLayout = makeExistingTopicLayouts[topic.id] ?? TopicItemNode.asyncLayout(nil)
|
||||||
let (topicSize, topicApply) = makeTopicLayout(remainingWidth, context, theme, topic.id, topic.title, topic.iconId, topic.iconColor)
|
let (topicSize, topicApply) = makeTopicLayout(remainingWidth, context, theme, topic.id, topic.threadPeer, topic.title, topic.iconId, topic.iconColor)
|
||||||
topicsSizeAndApply.append((topic.id, topicSize, topicApply))
|
topicsSizeAndApply.append((topic.id, topicSize, topicApply))
|
||||||
|
|
||||||
remainingWidth -= topicSize.width + 4.0
|
remainingWidth -= topicSize.width + 4.0
|
||||||
@ -3337,17 +3364,17 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
let isSearching = item.interaction.searchTextHighightState != nil
|
let isSearching = item.interaction.searchTextHighightState != nil
|
||||||
|
|
||||||
var isFirstForumThreadSelectable = false
|
var isFirstForumThreadSelectable = false
|
||||||
var forumThreads: [(id: Int64, title: NSAttributedString, iconId: Int64?, iconColor: Int32?)] = []
|
var forumThreads: [(id: Int64, threadPeer: EnginePeer?, title: NSAttributedString, iconId: Int64?, iconColor: Int32?)] = []
|
||||||
if case .savedMessagesChats = item.chatListLocation {
|
if case .savedMessagesChats = item.chatListLocation {
|
||||||
} else if case let .peer(peer) = item.content, case let .channel(channel) = peer.peer.peer, channel.flags.contains(.isMonoforum) {
|
} else if case let .peer(peer) = item.content, case let .channel(channel) = peer.peer.peer, channel.flags.contains(.isMonoforum) {
|
||||||
if forumThread != nil || !topForumTopicItems.isEmpty {
|
if forumThread != nil || !topForumTopicItems.isEmpty {
|
||||||
if let forumThread {
|
if let forumThread {
|
||||||
isFirstForumThreadSelectable = forumThread.isUnread
|
isFirstForumThreadSelectable = forumThread.isUnread
|
||||||
forumThreads.append((id: forumThread.id, title: NSAttributedString(string: forumThread.threadPeer?.compactDisplayTitle ?? " ", font: textFont, textColor: forumThread.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: nil, iconColor: nil))
|
forumThreads.append((id: forumThread.id, threadPeer: forumThread.threadPeer, title: NSAttributedString(string: forumThread.threadPeer?.compactDisplayTitle ?? " ", font: textFont, textColor: forumThread.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: nil, iconColor: nil))
|
||||||
}
|
}
|
||||||
for topicItem in topForumTopicItems {
|
for topicItem in topForumTopicItems {
|
||||||
if forumThread?.id != topicItem.id {
|
if forumThread?.id != topicItem.id {
|
||||||
forumThreads.append((id: topicItem.id, title: NSAttributedString(string: topicItem.threadPeer?.compactDisplayTitle ?? " ", font: textFont, textColor: topicItem.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: nil, iconColor: nil))
|
forumThreads.append((id: topicItem.id, threadPeer: topicItem.threadPeer, title: NSAttributedString(string: topicItem.threadPeer?.compactDisplayTitle ?? " ", font: textFont, textColor: topicItem.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: nil, iconColor: nil))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3364,13 +3391,13 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
} else if forumThread != nil || !topForumTopicItems.isEmpty {
|
} else if forumThread != nil || !topForumTopicItems.isEmpty {
|
||||||
if let forumThread = forumThread {
|
if let forumThread = forumThread {
|
||||||
isFirstForumThreadSelectable = forumThread.isUnread
|
isFirstForumThreadSelectable = forumThread.isUnread
|
||||||
forumThreads.append((id: forumThread.id, title: NSAttributedString(string: forumThread.title, font: textFont, textColor: forumThread.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: forumThread.iconId, iconColor: forumThread.iconColor))
|
forumThreads.append((id: forumThread.id, threadPeer: forumThread.threadPeer, title: NSAttributedString(string: forumThread.title, font: textFont, textColor: forumThread.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: forumThread.iconId, iconColor: forumThread.iconColor))
|
||||||
}
|
}
|
||||||
for topicItem in topForumTopicItems {
|
for topicItem in topForumTopicItems {
|
||||||
if case let .peer(peer) = item.content, peer.peer.peerId.id._internalGetInt64Value() == topicItem.id {
|
if case let .peer(peer) = item.content, peer.peer.peerId.id._internalGetInt64Value() == topicItem.id {
|
||||||
|
|
||||||
} else if forumThread?.id != topicItem.id {
|
} else if forumThread?.id != topicItem.id {
|
||||||
forumThreads.append((id: topicItem.id, title: NSAttributedString(string: topicItem.title, font: textFont, textColor: topicItem.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: topicItem.iconFileId, iconColor: topicItem.iconColor))
|
forumThreads.append((id: topicItem.id, threadPeer: topicItem.threadPeer, title: NSAttributedString(string: topicItem.title, font: textFont, textColor: topicItem.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: topicItem.iconFileId, iconColor: topicItem.iconColor))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
case enableDebugDataDisplay(Bool)
|
case enableDebugDataDisplay(Bool)
|
||||||
case rippleEffect(Bool)
|
case rippleEffect(Bool)
|
||||||
case browserExperiment(Bool)
|
case browserExperiment(Bool)
|
||||||
case localTranscription(Bool)
|
case allForumsHaveTabs(Bool)
|
||||||
case enableReactionOverrides(Bool)
|
case enableReactionOverrides(Bool)
|
||||||
case compressedEmojiCache(Bool)
|
case compressedEmojiCache(Bool)
|
||||||
case storiesJpegExperiment(Bool)
|
case storiesJpegExperiment(Bool)
|
||||||
@ -133,7 +133,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return DebugControllerSection.web.rawValue
|
return DebugControllerSection.web.rawValue
|
||||||
case .keepChatNavigationStack, .skipReadHistory, .dustEffect, .crashOnSlowQueries, .crashOnMemoryPressure:
|
case .keepChatNavigationStack, .skipReadHistory, .dustEffect, .crashOnSlowQueries, .crashOnMemoryPressure:
|
||||||
return DebugControllerSection.experiments.rawValue
|
return DebugControllerSection.experiments.rawValue
|
||||||
case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .compressedEmojiCache, .storiesJpegExperiment, .checkSerializedData, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .rippleEffect, .browserExperiment, .localTranscription, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2, .experimentalCallMute, .playerV2, .devRequests, .fakeAds, .enableLocalTranslation:
|
case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .compressedEmojiCache, .storiesJpegExperiment, .checkSerializedData, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .rippleEffect, .browserExperiment, .allForumsHaveTabs, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2, .experimentalCallMute, .playerV2, .devRequests, .fakeAds, .enableLocalTranslation:
|
||||||
return DebugControllerSection.experiments.rawValue
|
return DebugControllerSection.experiments.rawValue
|
||||||
case .logTranslationRecognition, .resetTranslationStates:
|
case .logTranslationRecognition, .resetTranslationStates:
|
||||||
return DebugControllerSection.translation.rawValue
|
return DebugControllerSection.translation.rawValue
|
||||||
@ -226,7 +226,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return 39
|
return 39
|
||||||
case .browserExperiment:
|
case .browserExperiment:
|
||||||
return 40
|
return 40
|
||||||
case .localTranscription:
|
case .allForumsHaveTabs:
|
||||||
return 41
|
return 41
|
||||||
case .enableReactionOverrides:
|
case .enableReactionOverrides:
|
||||||
return 42
|
return 42
|
||||||
@ -1264,12 +1264,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
})
|
})
|
||||||
}).start()
|
}).start()
|
||||||
})
|
})
|
||||||
case let .localTranscription(value):
|
case let .allForumsHaveTabs(value):
|
||||||
return ItemListSwitchItem(presentationData: presentationData, title: "Local Transcription", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
return ItemListSwitchItem(presentationData: presentationData, title: "Forum Tabs Debug", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||||
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
||||||
settings.localTranscription = value
|
settings.allForumsHaveTabs = value
|
||||||
return PreferencesEntry(settings)
|
return PreferencesEntry(settings)
|
||||||
})
|
})
|
||||||
}).start()
|
}).start()
|
||||||
@ -1526,7 +1526,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
|||||||
entries.append(.browserExperiment(experimentalSettings.browserExperiment))
|
entries.append(.browserExperiment(experimentalSettings.browserExperiment))
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
entries.append(.localTranscription(experimentalSettings.localTranscription))
|
entries.append(.allForumsHaveTabs(experimentalSettings.allForumsHaveTabs))
|
||||||
if case .internal = sharedContext.applicationBindings.appBuildType {
|
if case .internal = sharedContext.applicationBindings.appBuildType {
|
||||||
entries.append(.enableReactionOverrides(experimentalSettings.enableReactionOverrides))
|
entries.append(.enableReactionOverrides(experimentalSettings.enableReactionOverrides))
|
||||||
}
|
}
|
||||||
|
@ -862,6 +862,10 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func resetScrolledToItem() {
|
||||||
|
self.scrolledToItem = nil
|
||||||
|
}
|
||||||
|
|
||||||
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
|
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
|
||||||
if let shouldStopScrolling = self.shouldStopScrolling, shouldStopScrolling(velocity.y) {
|
if let shouldStopScrolling = self.shouldStopScrolling, shouldStopScrolling(velocity.y) {
|
||||||
targetContentOffset.pointee.y = scrollView.contentOffset.y
|
targetContentOffset.pointee.y = scrollView.contentOffset.y
|
||||||
|
@ -65,7 +65,11 @@ private func mappedChatListFilterPredicate(postbox: PostboxImpl, currentTransact
|
|||||||
if let cachedPeerData = postbox.cachedPeerDataTable.get(peer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
if let cachedPeerData = postbox.cachedPeerDataTable.get(peer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
||||||
displayAsRegularChat = true
|
displayAsRegularChat = true
|
||||||
}
|
}
|
||||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value && !displayAsRegularChat {
|
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value
|
||||||
|
if !isThreadBased {
|
||||||
|
displayAsRegularChat = true
|
||||||
|
}
|
||||||
|
if isThreadBased && !displayAsRegularChat {
|
||||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.effectiveUnreadCount ?? 0) > 0
|
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.effectiveUnreadCount ?? 0) > 0
|
||||||
} else {
|
} else {
|
||||||
isUnread = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId)?.isUnread ?? false
|
isUnread = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId)?.isUnread ?? false
|
||||||
@ -435,13 +439,17 @@ private final class ChatListViewSpaceState {
|
|||||||
if let cachedPeerData = postbox.cachedPeerDataTable.get(peer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
if let cachedPeerData = postbox.cachedPeerDataTable.get(peer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
||||||
displayAsRegularChat = true
|
displayAsRegularChat = true
|
||||||
}
|
}
|
||||||
|
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value
|
||||||
|
if !isThreadBased {
|
||||||
|
displayAsRegularChat = true
|
||||||
|
}
|
||||||
|
|
||||||
let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettingsValue, peer: peer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(notificationsPeerId))
|
let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettingsValue, peer: peer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(notificationsPeerId))
|
||||||
|
|
||||||
let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peer.id, threadId: nil, calculation: filterPredicate.messageTagSummary)
|
let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peer.id, threadId: nil, calculation: filterPredicate.messageTagSummary)
|
||||||
|
|
||||||
var isUnread: Bool
|
var isUnread: Bool
|
||||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value && !displayAsRegularChat {
|
if isThreadBased && !displayAsRegularChat {
|
||||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.effectiveUnreadCount ?? 0) > 0
|
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.effectiveUnreadCount ?? 0) > 0
|
||||||
} else {
|
} else {
|
||||||
isUnread = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId)?.isUnread ?? false
|
isUnread = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId)?.isUnread ?? false
|
||||||
@ -569,9 +577,13 @@ private final class ChatListViewSpaceState {
|
|||||||
if let cachedPeerData = postbox.cachedPeerDataTable.get(entryPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
if let cachedPeerData = postbox.cachedPeerDataTable.get(entryPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
||||||
displayAsRegularChat = true
|
displayAsRegularChat = true
|
||||||
}
|
}
|
||||||
|
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer).value
|
||||||
|
if !isThreadBased {
|
||||||
|
displayAsRegularChat = true
|
||||||
|
}
|
||||||
|
|
||||||
var isUnread: Bool
|
var isUnread: Bool
|
||||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer).value && !displayAsRegularChat {
|
if isThreadBased && !displayAsRegularChat {
|
||||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: entryPeer.id)?.effectiveUnreadCount ?? 0) > 0
|
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: entryPeer.id)?.effectiveUnreadCount ?? 0) > 0
|
||||||
} else {
|
} else {
|
||||||
isUnread = postbox.readStateTable.getCombinedState(entryPeer.id)?.isUnread ?? false
|
isUnread = postbox.readStateTable.getCombinedState(entryPeer.id)?.isUnread ?? false
|
||||||
@ -618,9 +630,13 @@ private final class ChatListViewSpaceState {
|
|||||||
if let cachedPeerData = postbox.cachedPeerDataTable.get(mainPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
if let cachedPeerData = postbox.cachedPeerDataTable.get(mainPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
||||||
displayAsRegularChat = true
|
displayAsRegularChat = true
|
||||||
}
|
}
|
||||||
|
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer).value
|
||||||
|
if !isThreadBased {
|
||||||
|
displayAsRegularChat = true
|
||||||
|
}
|
||||||
|
|
||||||
var isUnread: Bool
|
var isUnread: Bool
|
||||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer).value && !displayAsRegularChat {
|
if isThreadBased && !displayAsRegularChat {
|
||||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0) > 0
|
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0) > 0
|
||||||
} else {
|
} else {
|
||||||
isUnread = postbox.readStateTable.getCombinedState(peerId)?.isUnread ?? false
|
isUnread = postbox.readStateTable.getCombinedState(peerId)?.isUnread ?? false
|
||||||
@ -800,9 +816,13 @@ private final class ChatListViewSpaceState {
|
|||||||
if let cachedPeerData = postbox.cachedPeerDataTable.get(entryPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
if let cachedPeerData = postbox.cachedPeerDataTable.get(entryPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
||||||
displayAsRegularChat = true
|
displayAsRegularChat = true
|
||||||
}
|
}
|
||||||
|
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer).value
|
||||||
|
if !isThreadBased {
|
||||||
|
displayAsRegularChat = true
|
||||||
|
}
|
||||||
|
|
||||||
var isUnread: Bool
|
var isUnread: Bool
|
||||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer).value && !displayAsRegularChat {
|
if isThreadBased && !displayAsRegularChat {
|
||||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: entryPeer.id)?.effectiveUnreadCount ?? 0) > 0
|
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: entryPeer.id)?.effectiveUnreadCount ?? 0) > 0
|
||||||
} else {
|
} else {
|
||||||
isUnread = postbox.readStateTable.getCombinedState(entryPeer.id)?.isUnread ?? false
|
isUnread = postbox.readStateTable.getCombinedState(entryPeer.id)?.isUnread ?? false
|
||||||
@ -858,9 +878,13 @@ private final class ChatListViewSpaceState {
|
|||||||
if let cachedPeerData = postbox.cachedPeerDataTable.get(mainPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
if let cachedPeerData = postbox.cachedPeerDataTable.get(mainPeer.id), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
||||||
displayAsRegularChat = true
|
displayAsRegularChat = true
|
||||||
}
|
}
|
||||||
|
let isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer).value
|
||||||
|
if !isThreadBased {
|
||||||
|
displayAsRegularChat = true
|
||||||
|
}
|
||||||
|
|
||||||
var isUnread: Bool
|
var isUnread: Bool
|
||||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer).value && !displayAsRegularChat {
|
if isThreadBased && !displayAsRegularChat {
|
||||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0) > 0
|
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0) > 0
|
||||||
} else {
|
} else {
|
||||||
isUnread = postbox.readStateTable.getCombinedState(peerId)?.isUnread ?? false
|
isUnread = postbox.readStateTable.getCombinedState(peerId)?.isUnread ?? false
|
||||||
@ -952,10 +976,19 @@ private final class ChatListViewSpaceState {
|
|||||||
if let cachedPeerData = postbox.cachedPeerDataTable.get(entryData.index.messageIndex.id.peerId), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
if let cachedPeerData = postbox.cachedPeerDataTable.get(entryData.index.messageIndex.id.peerId), postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedPeerData) {
|
||||||
displayAsRegularChat = true
|
displayAsRegularChat = true
|
||||||
}
|
}
|
||||||
|
let isThreadBased: Bool
|
||||||
|
if let peer = postbox.peerTable.get(entryData.index.messageIndex.id.peerId) {
|
||||||
|
isThreadBased = postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value
|
||||||
|
} else {
|
||||||
|
isThreadBased = false
|
||||||
|
}
|
||||||
|
if !isThreadBased {
|
||||||
|
displayAsRegularChat = true
|
||||||
|
}
|
||||||
|
|
||||||
var updatedReadState = entryData.readState
|
var updatedReadState = entryData.readState
|
||||||
if let peer = postbox.peerTable.get(entryData.index.messageIndex.id.peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer).value, !displayAsRegularChat {
|
if isThreadBased, !displayAsRegularChat {
|
||||||
let summary = postbox.peerThreadsSummaryTable.get(peerId: peer.id)
|
let summary = postbox.peerThreadsSummaryTable.get(peerId: entryData.index.messageIndex.id.peerId)
|
||||||
|
|
||||||
var count: Int32 = 0
|
var count: Int32 = 0
|
||||||
var isMuted: Bool = false
|
var isMuted: Bool = false
|
||||||
@ -1605,6 +1638,9 @@ struct ChatListViewState {
|
|||||||
autoremoveTimeout = postbox.seedConfiguration.decodeAutoremoveTimeout(cachedData)
|
autoremoveTimeout = postbox.seedConfiguration.decodeAutoremoveTimeout(cachedData)
|
||||||
displayAsRegularChat = postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedData)
|
displayAsRegularChat = postbox.seedConfiguration.decodeDisplayPeerAsRegularChat(cachedData)
|
||||||
}
|
}
|
||||||
|
if !isThreadBased {
|
||||||
|
displayAsRegularChat = true
|
||||||
|
}
|
||||||
|
|
||||||
var topForumTopics: [ChatListForumTopicData] = []
|
var topForumTopics: [ChatListForumTopicData] = []
|
||||||
let readState: ChatListViewReadState?
|
let readState: ChatListViewReadState?
|
||||||
|
@ -247,7 +247,7 @@ func _internal_createForumChannelTopic(account: Account, peerId: PeerId, title:
|
|||||||
}
|
}
|
||||||
|> castError(CreateForumChannelTopicError.self)
|
|> castError(CreateForumChannelTopicError.self)
|
||||||
|> mapToSignal { _ -> Signal<Int64, CreateForumChannelTopicError> in
|
|> mapToSignal { _ -> Signal<Int64, CreateForumChannelTopicError> in
|
||||||
return resolveForumThreads(accountPeerId: account.peerId, postbox: account.postbox, source: .network(account.network), ids: [PeerAndBoundThreadId(peerId: peerId, threadId: topicId)])
|
return resolveForumThreads(accountPeerId: account.peerId, postbox: account.postbox, source: .network(account.network), additionalPeers: AccumulatedPeers(), ids: [PeerAndBoundThreadId(peerId: peerId, threadId: topicId)])
|
||||||
|> castError(CreateForumChannelTopicError.self)
|
|> castError(CreateForumChannelTopicError.self)
|
||||||
|> map { _ -> Int64 in
|
|> map { _ -> Int64 in
|
||||||
return topicId
|
return topicId
|
||||||
@ -277,7 +277,7 @@ func _internal_fetchForumChannelTopic(account: Account, peerId: PeerId, threadId
|
|||||||
if let info = info {
|
if let info = info {
|
||||||
return .single(.result(info))
|
return .single(.result(info))
|
||||||
} else {
|
} else {
|
||||||
return .single(.progress) |> then(resolveForumThreads(accountPeerId: account.peerId, postbox: account.postbox, source: .network(account.network), ids: [PeerAndBoundThreadId(peerId: peerId, threadId: threadId)])
|
return .single(.progress) |> then(resolveForumThreads(accountPeerId: account.peerId, postbox: account.postbox, source: .network(account.network), additionalPeers: AccumulatedPeers(), ids: [PeerAndBoundThreadId(peerId: peerId, threadId: threadId)])
|
||||||
|> mapToSignal { _ -> Signal<FetchForumChannelTopicResult, NoError> in
|
|> mapToSignal { _ -> Signal<FetchForumChannelTopicResult, NoError> in
|
||||||
return account.postbox.transaction { transaction -> FetchForumChannelTopicResult in
|
return account.postbox.transaction { transaction -> FetchForumChannelTopicResult in
|
||||||
if let data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.data.get(MessageHistoryThreadData.self) {
|
if let data = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.data.get(MessageHistoryThreadData.self) {
|
||||||
|
@ -2019,7 +2019,16 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM
|
|||||||
}
|
}
|
||||||
|
|
||||||
if peer.flags.contains(.isMonoforum) {
|
if peer.flags.contains(.isMonoforum) {
|
||||||
let signal = source.request(Api.functions.messages.getSavedDialogsByID(flags: 1 << 1, parentPeer: inputPeer, ids: threadIds.compactMap { transaction.getPeer(PeerId($0)).flatMap(apiInputPeer(_:)) }))
|
let signal = source.request(Api.functions.messages.getSavedDialogsByID(flags: 1 << 1, parentPeer: inputPeer, ids: threadIds.compactMap { threadId in
|
||||||
|
let threadPeerId = PeerId(threadId)
|
||||||
|
if let threadPeer = state.peers[threadPeerId] {
|
||||||
|
return apiInputPeer(threadPeer)
|
||||||
|
} else if let threadPeer = transaction.getPeer(threadPeerId) {
|
||||||
|
return apiInputPeer(threadPeer)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}))
|
||||||
|> map { result -> (Peer, FetchedForumThreads)? in
|
|> map { result -> (Peer, FetchedForumThreads)? in
|
||||||
let result = FetchedForumThreads(savedDialogs: result)
|
let result = FetchedForumThreads(savedDialogs: result)
|
||||||
return (peer, result)
|
return (peer, result)
|
||||||
@ -2146,7 +2155,7 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchMessageHistoryHoleSource, ids: [PeerAndBoundThreadId]) -> Signal<Void, NoError> {
|
func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchMessageHistoryHoleSource, additionalPeers: AccumulatedPeers, ids: [PeerAndBoundThreadId]) -> Signal<Void, NoError> {
|
||||||
let forumThreadIds = Set(ids)
|
let forumThreadIds = Set(ids)
|
||||||
|
|
||||||
if forumThreadIds.isEmpty {
|
if forumThreadIds.isEmpty {
|
||||||
@ -2172,7 +2181,16 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM
|
|||||||
}
|
}
|
||||||
|
|
||||||
if peer.flags.contains(.isMonoforum) {
|
if peer.flags.contains(.isMonoforum) {
|
||||||
let signal = source.request(Api.functions.messages.getSavedDialogsByID(flags: 1 << 1, parentPeer: inputPeer, ids: threadIds.compactMap { transaction.getPeer(PeerId($0)).flatMap(apiInputPeer(_:)) }))
|
let signal = source.request(Api.functions.messages.getSavedDialogsByID(flags: 1 << 1, parentPeer: inputPeer, ids: threadIds.compactMap { threadId in
|
||||||
|
let threadPeerId = PeerId(threadId)
|
||||||
|
if let threadPeer = additionalPeers.get(threadPeerId) {
|
||||||
|
return apiInputPeer(threadPeer)
|
||||||
|
} else if let threadPeer = transaction.getPeer(threadPeerId) {
|
||||||
|
return apiInputPeer(threadPeer)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}))
|
||||||
|> map { result -> (Peer, FetchedForumThreads)? in
|
|> map { result -> (Peer, FetchedForumThreads)? in
|
||||||
let result = FetchedForumThreads(savedDialogs: result)
|
let result = FetchedForumThreads(savedDialogs: result)
|
||||||
return (peer, result)
|
return (peer, result)
|
||||||
@ -2329,7 +2347,16 @@ func resolveForumThreads(accountPeerId: PeerId, postbox: Postbox, source: FetchM
|
|||||||
}
|
}
|
||||||
|
|
||||||
if peer.flags.contains(.isMonoforum) {
|
if peer.flags.contains(.isMonoforum) {
|
||||||
let signal = source.request(Api.functions.messages.getSavedDialogsByID(flags: 1 << 1, parentPeer: inputPeer, ids: threadIds.compactMap { transaction.getPeer(PeerId($0)).flatMap(apiInputPeer(_:)) }))
|
let signal = source.request(Api.functions.messages.getSavedDialogsByID(flags: 1 << 1, parentPeer: inputPeer, ids: threadIds.compactMap { threadId in
|
||||||
|
let threadPeerId = PeerId(threadId)
|
||||||
|
if let threadPeer = fetchedChatList.peers.get(threadPeerId) {
|
||||||
|
return apiInputPeer(threadPeer)
|
||||||
|
} else if let threadPeer = transaction.getPeer(threadPeerId) {
|
||||||
|
return apiInputPeer(threadPeer)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}))
|
||||||
|> map { result -> (Peer, FetchedForumThreads)? in
|
|> map { result -> (Peer, FetchedForumThreads)? in
|
||||||
let result = FetchedForumThreads(savedDialogs: result)
|
let result = FetchedForumThreads(savedDialogs: result)
|
||||||
return (peer, result)
|
return (peer, result)
|
||||||
|
@ -225,7 +225,7 @@ func withResolvedAssociatedMessages<T>(postbox: Postbox, source: FetchMessageHis
|
|||||||
return resolveAssociatedStories(postbox: postbox, source: source, accountPeerId: accountPeerId, messages: storeMessages, additionalPeers: parsedPeers, result: Void())
|
return resolveAssociatedStories(postbox: postbox, source: source, accountPeerId: accountPeerId, messages: storeMessages, additionalPeers: parsedPeers, result: Void())
|
||||||
|> mapToSignal { _ -> Signal<T, NoError> in
|
|> mapToSignal { _ -> Signal<T, NoError> in
|
||||||
if resolveThreads && !threadIds.isEmpty {
|
if resolveThreads && !threadIds.isEmpty {
|
||||||
return resolveForumThreads(accountPeerId: accountPeerId, postbox: postbox, source: source, ids: Array(threadIds))
|
return resolveForumThreads(accountPeerId: accountPeerId, postbox: postbox, source: source, additionalPeers: parsedPeers, ids: Array(threadIds))
|
||||||
|> mapToSignal { _ -> Signal<T, NoError> in
|
|> mapToSignal { _ -> Signal<T, NoError> in
|
||||||
return postbox.transaction { transaction -> T in
|
return postbox.transaction { transaction -> T in
|
||||||
return f(transaction, parsedPeers, [])
|
return f(transaction, parsedPeers, [])
|
||||||
@ -325,7 +325,8 @@ func withResolvedAssociatedMessages<T>(postbox: Postbox, source: FetchMessageHis
|
|||||||
let combinedMessages = storeMessages + additionalMessages
|
let combinedMessages = storeMessages + additionalMessages
|
||||||
return resolveUnknownEmojiFiles(postbox: postbox, source: source, messages: combinedMessages, reactions: [], result: Void())
|
return resolveUnknownEmojiFiles(postbox: postbox, source: source, messages: combinedMessages, reactions: [], result: Void())
|
||||||
|> mapToSignal { _ -> Signal<T, NoError> in
|
|> mapToSignal { _ -> Signal<T, NoError> in
|
||||||
return resolveAssociatedStories(postbox: postbox, source: source, accountPeerId: accountPeerId, messages: storeMessages + additionalMessages, additionalPeers: parsedPeers.union(with: additionalPeers), result: Void())
|
let additionalPeers = parsedPeers.union(with: additionalPeers)
|
||||||
|
return resolveAssociatedStories(postbox: postbox, source: source, accountPeerId: accountPeerId, messages: storeMessages + additionalMessages, additionalPeers: additionalPeers, result: Void())
|
||||||
|> mapToSignal { _ -> Signal<T, NoError> in
|
|> mapToSignal { _ -> Signal<T, NoError> in
|
||||||
var threadIds = Set<PeerAndBoundThreadId>()
|
var threadIds = Set<PeerAndBoundThreadId>()
|
||||||
for message in combinedMessages {
|
for message in combinedMessages {
|
||||||
@ -335,7 +336,7 @@ func withResolvedAssociatedMessages<T>(postbox: Postbox, source: FetchMessageHis
|
|||||||
}
|
}
|
||||||
|
|
||||||
if resolveThreads && !threadIds.isEmpty {
|
if resolveThreads && !threadIds.isEmpty {
|
||||||
return resolveForumThreads(accountPeerId: accountPeerId, postbox: postbox, source: source, ids: Array(threadIds))
|
return resolveForumThreads(accountPeerId: accountPeerId, postbox: postbox, source: source, additionalPeers: additionalPeers, ids: Array(threadIds))
|
||||||
|> mapToSignal { _ -> Signal<T, NoError> in
|
|> mapToSignal { _ -> Signal<T, NoError> in
|
||||||
return postbox.transaction { transaction -> T in
|
return postbox.transaction { transaction -> T in
|
||||||
return f(transaction, parsedPeers, [])
|
return f(transaction, parsedPeers, [])
|
||||||
|
@ -76,7 +76,11 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = {
|
|||||||
peerSummaryIsThreadBased: { peer in
|
peerSummaryIsThreadBased: { peer in
|
||||||
if let channel = peer as? TelegramChannel {
|
if let channel = peer as? TelegramChannel {
|
||||||
if channel.flags.contains(.isForum) {
|
if channel.flags.contains(.isForum) {
|
||||||
return (true, false)
|
if channel.flags.contains(.displayForumAsTabs) {
|
||||||
|
return (false, false)
|
||||||
|
} else {
|
||||||
|
return (true, false)
|
||||||
|
}
|
||||||
} else if channel.flags.contains(.isMonoforum) {
|
} else if channel.flags.contains(.isMonoforum) {
|
||||||
return (true, true)
|
return (true, true)
|
||||||
} else {
|
} else {
|
||||||
|
@ -208,7 +208,9 @@ func _internal_togglePeerUnreadMarkInteractively(transaction: Transaction, netwo
|
|||||||
}
|
}
|
||||||
|
|
||||||
var displayAsRegularChat: Bool = false
|
var displayAsRegularChat: Bool = false
|
||||||
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData {
|
if let channel = peer as? TelegramChannel, channel.flags.contains(.displayForumAsTabs) {
|
||||||
|
displayAsRegularChat = true
|
||||||
|
} else if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData {
|
||||||
displayAsRegularChat = cachedData.viewForumAsMessages.knownValue ?? false
|
displayAsRegularChat = cachedData.viewForumAsMessages.knownValue ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,8 +125,12 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||||||
let compactAuthorName = message.author?.compactDisplayTitle ?? ""
|
let compactAuthorName = message.author?.compactDisplayTitle ?? ""
|
||||||
|
|
||||||
var isChannel = false
|
var isChannel = false
|
||||||
if message.id.peerId.namespace == Namespaces.Peer.CloudChannel, let peer = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
|
var isMonoforum = false
|
||||||
isChannel = true
|
if message.id.peerId.namespace == Namespaces.Peer.CloudChannel, let peer = message.peers[message.id.peerId] as? TelegramChannel {
|
||||||
|
if case .broadcast = peer.info {
|
||||||
|
isChannel = true
|
||||||
|
}
|
||||||
|
isMonoforum = peer.isMonoForum
|
||||||
}
|
}
|
||||||
|
|
||||||
switch action.action {
|
switch action.action {
|
||||||
@ -135,7 +139,12 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||||||
attributedString = NSAttributedString(string: strings.Notification_CreatedChannel, font: titleFont, textColor: primaryTextColor)
|
attributedString = NSAttributedString(string: strings.Notification_CreatedChannel, font: titleFont, textColor: primaryTextColor)
|
||||||
} else {
|
} else {
|
||||||
if forChatList {
|
if forChatList {
|
||||||
attributedString = NSAttributedString(string: strings.Notification_CreatedGroup, font: titleFont, textColor: primaryTextColor)
|
if isMonoforum {
|
||||||
|
//TODO:Localize
|
||||||
|
attributedString = NSAttributedString(string: "No messages here yet...", font: titleFont, textColor: primaryTextColor)
|
||||||
|
} else {
|
||||||
|
attributedString = NSAttributedString(string: strings.Notification_CreatedGroup, font: titleFont, textColor: primaryTextColor)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
attributedString = addAttributesToStringWithRanges(strings.Notification_CreatedChatWithTitle(authorName, title)._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))
|
attributedString = addAttributesToStringWithRanges(strings.Notification_CreatedChatWithTitle(authorName, title)._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ import LottieComponent
|
|||||||
import BundleIconComponent
|
import BundleIconComponent
|
||||||
|
|
||||||
private protocol ChatEmptyNodeContent {
|
private protocol ChatEmptyNodeContent {
|
||||||
func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize
|
func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize
|
||||||
}
|
}
|
||||||
|
|
||||||
private let titleFont = Font.semibold(15.0)
|
private let titleFont = Font.semibold(15.0)
|
||||||
@ -46,7 +46,7 @@ private final class ChatEmptyNodeRegularChatContent: ASDisplayNode, ChatEmptyNod
|
|||||||
self.addSubnode(self.textNode)
|
self.addSubnode(self.textNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||||
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
||||||
self.currentTheme = interfaceState.theme
|
self.currentTheme = interfaceState.theme
|
||||||
self.currentStrings = interfaceState.strings
|
self.currentStrings = interfaceState.strings
|
||||||
@ -155,7 +155,7 @@ public final class ChatEmptyNodeGreetingChatContent: ASDisplayNode, ChatEmptyNod
|
|||||||
let _ = self.interaction?.sendSticker(.standalone(media: stickerItem.stickerItem.file._parse()), false, self.view, self.stickerNode.bounds, nil, [])
|
let _ = self.interaction?.sendSticker(.standalone(media: stickerItem.stickerItem.file._parse()), false, self.view, self.stickerNode.bounds, nil, [])
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||||
let isFirstTime = self.currentTheme == nil
|
let isFirstTime = self.currentTheme == nil
|
||||||
|
|
||||||
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
||||||
@ -363,7 +363,7 @@ public final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNodeS
|
|||||||
let _ = self.interaction?.sendSticker(.standalone(media: stickerItem.stickerItem.file._parse()), false, self.view, self.stickerNode.bounds, nil, [])
|
let _ = self.interaction?.sendSticker(.standalone(media: stickerItem.stickerItem.file._parse()), false, self.view, self.stickerNode.bounds, nil, [])
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||||
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
||||||
self.currentTheme = interfaceState.theme
|
self.currentTheme = interfaceState.theme
|
||||||
self.currentStrings = interfaceState.strings
|
self.currentStrings = interfaceState.strings
|
||||||
@ -496,7 +496,7 @@ private final class ChatEmptyNodeSecretChatContent: ASDisplayNode, ChatEmptyNode
|
|||||||
self.addSubnode(self.subtitleNode)
|
self.addSubnode(self.subtitleNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||||
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
||||||
self.currentTheme = interfaceState.theme
|
self.currentTheme = interfaceState.theme
|
||||||
self.currentStrings = interfaceState.strings
|
self.currentStrings = interfaceState.strings
|
||||||
@ -630,7 +630,7 @@ private final class ChatEmptyNodeGroupChatContent: ASDisplayNode, ChatEmptyNodeC
|
|||||||
self.addSubnode(self.subtitleNode)
|
self.addSubnode(self.subtitleNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||||
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
||||||
self.currentTheme = interfaceState.theme
|
self.currentTheme = interfaceState.theme
|
||||||
self.currentStrings = interfaceState.strings
|
self.currentStrings = interfaceState.strings
|
||||||
@ -759,7 +759,7 @@ private final class ChatEmptyNodeCloudChatContent: ASDisplayNode, ChatEmptyNodeC
|
|||||||
self.shareBusinessLink?(businessLink.url)
|
self.shareBusinessLink?(businessLink.url)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||||
var maxWidth: CGFloat = size.width
|
var maxWidth: CGFloat = size.width
|
||||||
var centerText = false
|
var centerText = false
|
||||||
|
|
||||||
@ -1125,7 +1125,7 @@ public final class ChatEmptyNodeTopicChatContent: ASDisplayNode, ChatEmptyNodeCo
|
|||||||
self.addSubnode(self.textNode)
|
self.addSubnode(self.textNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||||
let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
|
let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
|
||||||
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
||||||
self.currentTheme = interfaceState.theme
|
self.currentTheme = interfaceState.theme
|
||||||
@ -1266,7 +1266,7 @@ public final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||||
let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
|
let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
|
||||||
|
|
||||||
let maxWidth = min(270.0, size.width)
|
let maxWidth = min(270.0, size.width)
|
||||||
@ -1778,7 +1778,7 @@ public final class ChatEmptyNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: Subject, loadingNode: ChatLoadingNode?, backgroundNode: WallpaperBackgroundNode?, size: CGSize, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) {
|
public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: Subject, loadingNode: ChatLoadingNode?, backgroundNode: WallpaperBackgroundNode?, size: CGSize, insets: UIEdgeInsets, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
self.wallpaperBackgroundNode = backgroundNode
|
self.wallpaperBackgroundNode = backgroundNode
|
||||||
|
|
||||||
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
|
||||||
@ -1816,7 +1816,7 @@ public final class ChatEmptyNode: ASDisplayNode {
|
|||||||
contentType = .secret
|
contentType = .secret
|
||||||
} else if let group = peer as? TelegramGroup, case .creator = group.role {
|
} else if let group = peer as? TelegramGroup, case .creator = group.role {
|
||||||
contentType = .group
|
contentType = .group
|
||||||
} else if let channel = peer as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isCreator) && !channel.flags.contains(.isGigagroup) {
|
} else if let channel = peer as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isCreator) && !channel.flags.contains(.isGigagroup) && !channel.isMonoForum {
|
||||||
contentType = .group
|
contentType = .group
|
||||||
} else if let _ = interfaceState.peerNearbyData {
|
} else if let _ = interfaceState.peerNearbyData {
|
||||||
contentType = .peerNearby
|
contentType = .peerNearby
|
||||||
@ -1835,6 +1835,8 @@ public final class ChatEmptyNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if let channel = peer as? TelegramChannel, channel.isMonoForum, let sendPaidMessageStars = interfaceState.sendPaidMessageStars {
|
||||||
|
contentType = .starsRequired(sendPaidMessageStars.value)
|
||||||
} else {
|
} else {
|
||||||
contentType = .regular
|
contentType = .regular
|
||||||
}
|
}
|
||||||
@ -1910,14 +1912,14 @@ public final class ChatEmptyNode: ASDisplayNode {
|
|||||||
|
|
||||||
var contentSize = CGSize()
|
var contentSize = CGSize()
|
||||||
if let contentNode = self.content?.1 {
|
if let contentNode = self.content?.1 {
|
||||||
contentSize = contentNode.updateLayout(interfaceState: interfaceState, subject: subject, size: displayRect.size, transition: contentTransition)
|
contentSize = contentNode.updateLayout(interfaceState: interfaceState, subject: subject, size: displayRect.size, leftInset: leftInset, rightInset: rightInset, transition: contentTransition)
|
||||||
|
|
||||||
if updateGreetingSticker {
|
if updateGreetingSticker {
|
||||||
self.context.prefetchManager?.prepareNextGreetingSticker()
|
self.context.prefetchManager?.prepareNextGreetingSticker()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let contentFrame = CGRect(origin: CGPoint(x: displayRect.minX + floor((displayRect.width - contentSize.width) / 2.0), y: displayRect.minY + floor((displayRect.height - contentSize.height) / 2.0)), size: contentSize)
|
let contentFrame = CGRect(origin: CGPoint(x: displayRect.minX + leftInset + floor((displayRect.width - leftInset - rightInset - contentSize.width) / 2.0), y: displayRect.minY + floor((displayRect.height - contentSize.height) / 2.0)), size: contentSize)
|
||||||
if let contentNode = self.content?.1 {
|
if let contentNode = self.content?.1 {
|
||||||
contentTransition.updateFrame(node: contentNode, frame: contentFrame)
|
contentTransition.updateFrame(node: contentNode, frame: contentFrame)
|
||||||
}
|
}
|
||||||
@ -1978,7 +1980,7 @@ public final class ChatEmptyNode: ASDisplayNode {
|
|||||||
wallpaperBackgroundNode: backgroundNode,
|
wallpaperBackgroundNode: backgroundNode,
|
||||||
constrainedSize: CGSize(width: size.width - insets.left - insets.right, height: 200.0)
|
constrainedSize: CGSize(width: size.width - insets.left - insets.right, height: 200.0)
|
||||||
)
|
)
|
||||||
let attachedDescriptionFrame = CGRect(origin: CGPoint(x: floor((size.width - attachedDescriptionSize.width) * 0.5), y: contentFrame.maxY + 4.0), size: attachedDescriptionSize)
|
let attachedDescriptionFrame = CGRect(origin: CGPoint(x: leftInset + floor((size.width - leftInset - rightInset - attachedDescriptionSize.width) * 0.5), y: contentFrame.maxY + 4.0), size: attachedDescriptionSize)
|
||||||
transition.updateFrame(node: attachedDescriptionNode, frame: attachedDescriptionFrame)
|
transition.updateFrame(node: attachedDescriptionNode, frame: attachedDescriptionFrame)
|
||||||
|
|
||||||
if let (rect, containerSize) = self.absolutePosition {
|
if let (rect, containerSize) = self.absolutePosition {
|
||||||
|
@ -126,7 +126,7 @@ public final class ChatLoadingPlaceholderMessageContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let avatarNode = self.avatarNode, let avatarBorderNode = self.avatarBorderNode {
|
if let avatarNode = self.avatarNode, let avatarBorderNode = self.avatarBorderNode {
|
||||||
let avatarFrame = CGRect(origin: CGPoint(x: 3.0, y: rect.maxY + 1.0 - avatarSize.height), size: avatarSize)
|
let avatarFrame = CGRect(origin: CGPoint(x: rect.minX + 3.0, y: rect.maxY + 1.0 - avatarSize.height), size: avatarSize)
|
||||||
|
|
||||||
transition.updateFrame(node: avatarNode, frame: avatarFrame)
|
transition.updateFrame(node: avatarNode, frame: avatarFrame)
|
||||||
transition.updateFrame(node: avatarBorderNode, frame: avatarFrame)
|
transition.updateFrame(node: avatarBorderNode, frame: avatarFrame)
|
||||||
@ -134,7 +134,7 @@ public final class ChatLoadingPlaceholderMessageContainer {
|
|||||||
avatarOffset += avatarSize.width - 1.0
|
avatarOffset += avatarSize.width - 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let bubbleFrame = CGRect(origin: CGPoint(x: 3.0 + avatarOffset, y: rect.origin.y), size: CGSize(width: rect.width, height: rect.height))
|
let bubbleFrame = CGRect(origin: CGPoint(x: rect.minX + 3.0 + avatarOffset, y: rect.origin.y), size: CGSize(width: rect.width, height: rect.height))
|
||||||
transition.updateFrame(node: self.bubbleNode, frame: bubbleFrame)
|
transition.updateFrame(node: self.bubbleNode, frame: bubbleFrame)
|
||||||
transition.updateFrame(node: self.bubbleBorderNode, frame: bubbleFrame)
|
transition.updateFrame(node: self.bubbleBorderNode, frame: bubbleFrame)
|
||||||
}
|
}
|
||||||
@ -484,7 +484,7 @@ public final class ChatLoadingPlaceholderNode: ASDisplayNode {
|
|||||||
|
|
||||||
for messageContainer in self.messageContainers {
|
for messageContainer in self.messageContainers {
|
||||||
let messageSize = dimensions[index % 14]
|
let messageSize = dimensions[index % 14]
|
||||||
messageContainer.update(size: bounds.size, hasAvatar: self.chatType != .channel && self.chatType != .user, rect: CGRect(origin: CGPoint(x: 0.0, y: bounds.size.height - insets.bottom - offset - messageSize.height), size: messageSize), transition: transition)
|
messageContainer.update(size: bounds.size, hasAvatar: self.chatType != .channel && self.chatType != .user, rect: CGRect(origin: CGPoint(x: insets.left, y: bounds.size.height - insets.bottom - offset - messageSize.height), size: messageSize), transition: transition)
|
||||||
offset += messageSize.height
|
offset += messageSize.height
|
||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
|
@ -1423,10 +1423,10 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
strongSelf.contextSourceNode.contentNode.insertSubnode(strongSelf.textNode.textNode, aboveSubnode: strongSelf.imageNode)
|
strongSelf.contextSourceNode.contentNode.insertSubnode(strongSelf.textNode.textNode, aboveSubnode: strongSelf.imageNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.textNode.textNode.frame = imageFrame
|
animation.animator.updateFrame(layer: strongSelf.textNode.textNode.layer, frame: imageFrame, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.imageNode.frame = updatedContentFrame
|
animation.animator.updateFrame(layer: strongSelf.imageNode.layer, frame: updatedContentFrame, completion: nil)
|
||||||
|
|
||||||
strongSelf.contextSourceNode.contentRect = contextContentFrame
|
strongSelf.contextSourceNode.contentRect = contextContentFrame
|
||||||
strongSelf.containerNode.targetNodeForActivationProgressContentRect = strongSelf.contextSourceNode.contentRect
|
strongSelf.containerNode.targetNodeForActivationProgressContentRect = strongSelf.contextSourceNode.contentRect
|
||||||
@ -1458,11 +1458,13 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
let foregroundColor: UIColor = .clear// = bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.stickerPlaceholderColor, wallpaper: item.presentationData.theme.wallpaper)
|
let foregroundColor: UIColor = .clear// = bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.stickerPlaceholderColor, wallpaper: item.presentationData.theme.wallpaper)
|
||||||
let shimmeringColor = bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.stickerPlaceholderShimmerColor, wallpaper: item.presentationData.theme.wallpaper)
|
let shimmeringColor = bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.stickerPlaceholderShimmerColor, wallpaper: item.presentationData.theme.wallpaper)
|
||||||
strongSelf.placeholderNode.update(backgroundColor: nil, foregroundColor: foregroundColor, shimmeringColor: shimmeringColor, data: immediateThumbnailData, size: animationNodeFrame.size, enableEffect: item.context.sharedContext.energyUsageSettings.fullTranslucency, imageSize: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0))
|
strongSelf.placeholderNode.update(backgroundColor: nil, foregroundColor: foregroundColor, shimmeringColor: shimmeringColor, data: immediateThumbnailData, size: animationNodeFrame.size, enableEffect: item.context.sharedContext.energyUsageSettings.fullTranslucency, imageSize: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0))
|
||||||
strongSelf.placeholderNode.frame = animationNodeFrame
|
animation.animator.updateFrame(layer: strongSelf.placeholderNode.layer, frame: animationNodeFrame, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if strongSelf.animationNode?.supernode === strongSelf.contextSourceNode.contentNode {
|
if strongSelf.animationNode?.supernode === strongSelf.contextSourceNode.contentNode {
|
||||||
strongSelf.animationNode?.frame = animationNodeFrame
|
if let animationNode = strongSelf.animationNode {
|
||||||
|
animation.animator.updateFrame(layer: animationNode.layer, frame: animationNodeFrame, completion: nil)
|
||||||
|
}
|
||||||
if let animationNode = strongSelf.animationNode as? AnimatedStickerNode {
|
if let animationNode = strongSelf.animationNode as? AnimatedStickerNode {
|
||||||
animationNode.updateLayout(size: updatedContentFrame.insetBy(dx: imageInset, dy: imageInset).size)
|
animationNode.updateLayout(size: updatedContentFrame.insetBy(dx: imageInset, dy: imageInset).size)
|
||||||
|
|
||||||
@ -1494,7 +1496,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, controllerInteraction: item.controllerInteraction, chatLocation: item.chatLocation, subject: item.associatedData.subject, message: item.message, account: item.context.account)
|
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, controllerInteraction: item.controllerInteraction, chatLocation: item.chatLocation, subject: item.associatedData.subject, message: item.message, account: item.context.account)
|
||||||
updatedShareButtonNode.frame = CGRect(origin: CGPoint(x: !incoming ? updatedImageFrame.minX - buttonSize.width - 6.0 : updatedImageFrame.maxX + 8.0, y: updatedImageFrame.maxY - buttonSize.height - 4.0 + imageBottomPadding), size: buttonSize)
|
animation.animator.updateFrame(layer: updatedShareButtonNode.layer, frame: CGRect(origin: CGPoint(x: !incoming ? updatedImageFrame.minX - buttonSize.width - 6.0 : updatedImageFrame.maxX + 8.0, y: updatedImageFrame.maxY - buttonSize.height - 4.0 + imageBottomPadding), size: buttonSize), completion: nil)
|
||||||
} else if let shareButtonNode = strongSelf.shareButtonNode {
|
} else if let shareButtonNode = strongSelf.shareButtonNode {
|
||||||
shareButtonNode.removeFromSupernode()
|
shareButtonNode.removeFromSupernode()
|
||||||
strongSelf.shareButtonNode = nil
|
strongSelf.shareButtonNode = nil
|
||||||
|
@ -1926,7 +1926,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
inlineBotNameString = attribute.title
|
inlineBotNameString = attribute.title
|
||||||
}
|
}
|
||||||
} else if let attribute = attribute as? ReplyMessageAttribute {
|
} else if let attribute = attribute as? ReplyMessageAttribute {
|
||||||
if let threadId = firstMessage.threadId, Int32(clamping: threadId) == attribute.messageId.id {
|
if let threadId = firstMessage.threadId, Int32(clamping: threadId) == attribute.messageId.id, let channel = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, channel.isForumOrMonoForum {
|
||||||
} else {
|
} else {
|
||||||
replyMessage = firstMessage.associatedMessages[attribute.messageId]
|
replyMessage = firstMessage.associatedMessages[attribute.messageId]
|
||||||
}
|
}
|
||||||
@ -2429,6 +2429,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
var mediaInfoSizeApply: (CGSize, (Bool) -> ChatMessageStarsMediaInfoNode?) = (CGSize(), { _ in nil })
|
var mediaInfoSizeApply: (CGSize, (Bool) -> ChatMessageStarsMediaInfoNode?) = (CGSize(), { _ in nil })
|
||||||
|
|
||||||
var hasTitleAvatar = false
|
var hasTitleAvatar = false
|
||||||
|
var hasTitleTopicNavigation = false
|
||||||
|
|
||||||
if displayHeader {
|
if displayHeader {
|
||||||
let bubbleWidthInsets: CGFloat = mosaicRange == nil ? layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right : 0.0
|
let bubbleWidthInsets: CGFloat = mosaicRange == nil ? layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right : 0.0
|
||||||
@ -2439,6 +2440,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
|
|
||||||
if isSidePanelOpen && incoming {
|
if isSidePanelOpen && incoming {
|
||||||
hasTitleAvatar = true
|
hasTitleAvatar = true
|
||||||
|
hasTitleTopicNavigation = item.chatLocation.threadId == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let inlineBotNameColor = messageTheme.accentTextColor
|
let inlineBotNameColor = messageTheme.accentTextColor
|
||||||
@ -2544,7 +2546,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
var nameAvatarSpaceWidth: CGFloat = 0.0
|
var nameAvatarSpaceWidth: CGFloat = 0.0
|
||||||
if hasTitleAvatar {
|
if hasTitleAvatar {
|
||||||
headerSize.height += 12.0
|
headerSize.height += 12.0
|
||||||
nameAvatarSpaceWidth += 26.0 + 5.0 + 4.0 + 26.0
|
nameAvatarSpaceWidth += 26.0 + 5.0
|
||||||
|
if hasTitleTopicNavigation {
|
||||||
|
nameAvatarSpaceWidth += 4.0 + 26.0
|
||||||
|
}
|
||||||
nameNodeOriginY += 5.0
|
nameNodeOriginY += 5.0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3285,6 +3290,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
contentOrigin: contentOrigin,
|
contentOrigin: contentOrigin,
|
||||||
nameNodeOriginY: nameNodeOriginY + detachedContentNodesHeight + additionalTopHeight,
|
nameNodeOriginY: nameNodeOriginY + detachedContentNodesHeight + additionalTopHeight,
|
||||||
hasTitleAvatar: hasTitleAvatar,
|
hasTitleAvatar: hasTitleAvatar,
|
||||||
|
hasTitleTopicNavigation: hasTitleTopicNavigation,
|
||||||
authorNameColor: authorNameColor,
|
authorNameColor: authorNameColor,
|
||||||
layoutConstants: layoutConstants,
|
layoutConstants: layoutConstants,
|
||||||
currentCredibilityIcon: currentCredibilityIcon,
|
currentCredibilityIcon: currentCredibilityIcon,
|
||||||
@ -3348,6 +3354,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
contentOrigin: CGPoint,
|
contentOrigin: CGPoint,
|
||||||
nameNodeOriginY: CGFloat,
|
nameNodeOriginY: CGFloat,
|
||||||
hasTitleAvatar: Bool,
|
hasTitleAvatar: Bool,
|
||||||
|
hasTitleTopicNavigation: Bool,
|
||||||
authorNameColor: UIColor?,
|
authorNameColor: UIColor?,
|
||||||
layoutConstants: ChatMessageItemLayoutConstants,
|
layoutConstants: ChatMessageItemLayoutConstants,
|
||||||
currentCredibilityIcon: (EmojiStatusComponent.Content, UIColor?)?,
|
currentCredibilityIcon: (EmojiStatusComponent.Content, UIColor?)?,
|
||||||
@ -3538,21 +3545,6 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
strongSelf.clippingNode.addSubnode(nameAvatarNode)
|
strongSelf.clippingNode.addSubnode(nameAvatarNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
let nameNavigateButton: NameNavigateButton
|
|
||||||
if let current = strongSelf.nameNavigateButton {
|
|
||||||
nameNavigateButton = current
|
|
||||||
} else {
|
|
||||||
nameNavigateButton = NameNavigateButton(frame: CGRect())
|
|
||||||
strongSelf.nameNavigateButton = nameNavigateButton
|
|
||||||
strongSelf.clippingNode.view.addSubview(nameNavigateButton)
|
|
||||||
nameNavigateButton.action = { [weak strongSelf] in
|
|
||||||
guard let strongSelf, let item = strongSelf.item else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
item.controllerInteraction.updateChatLocationThread(item.content.firstMessage.threadId, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let nameAvatarFrame = CGRect(origin: CGPoint(x: nameNodeFrame.minX, y: nameNodeFrame.minY - 4.0), size: CGSize(width: 26.0, height: 26.0))
|
let nameAvatarFrame = CGRect(origin: CGPoint(x: nameNodeFrame.minX, y: nameNodeFrame.minY - 4.0), size: CGSize(width: 26.0, height: 26.0))
|
||||||
let nameNavigateFrame = CGRect(origin: CGPoint(x: nameNodeFrame.maxX + 4.0 + nameNavigateButtonOffset, y: nameNodeFrame.minY - 4.0), size: CGSize(width: 26.0, height: 26.0))
|
let nameNavigateFrame = CGRect(origin: CGPoint(x: nameNodeFrame.maxX + 4.0 + nameNavigateButtonOffset, y: nameNodeFrame.minY - 4.0), size: CGSize(width: 26.0, height: 26.0))
|
||||||
|
|
||||||
@ -3563,11 +3555,38 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
}
|
}
|
||||||
nameAvatarNode.updateSize(size: nameAvatarFrame.size)
|
nameAvatarNode.updateSize(size: nameAvatarFrame.size)
|
||||||
|
|
||||||
nameNavigateButton.update(size: nameNavigateFrame.size, color: authorNameColor ?? item.presentationData.theme.theme.chat.message.incoming.accentTextColor)
|
if hasTitleTopicNavigation {
|
||||||
|
let nameNavigateButton: NameNavigateButton
|
||||||
|
if let current = strongSelf.nameNavigateButton {
|
||||||
|
nameNavigateButton = current
|
||||||
|
} else {
|
||||||
|
nameNavigateButton = NameNavigateButton(frame: CGRect())
|
||||||
|
strongSelf.nameNavigateButton = nameNavigateButton
|
||||||
|
strongSelf.clippingNode.view.addSubview(nameNavigateButton)
|
||||||
|
nameNavigateButton.action = { [weak strongSelf] in
|
||||||
|
guard let strongSelf, let item = strongSelf.item else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
item.controllerInteraction.updateChatLocationThread(item.content.firstMessage.threadId, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nameNavigateButton.update(size: nameNavigateFrame.size, color: authorNameColor ?? item.presentationData.theme.theme.chat.message.incoming.accentTextColor)
|
||||||
|
} else {
|
||||||
|
if let nameNavigateButton = strongSelf.nameNavigateButton {
|
||||||
|
strongSelf.nameNavigateButton = nil
|
||||||
|
nameNavigateButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak nameNavigateButton] _ in
|
||||||
|
nameNavigateButton?.removeFromSuperview()
|
||||||
|
})
|
||||||
|
animation.animator.updateFrame(layer: nameNavigateButton.layer, frame: CGRect(origin: CGPoint(x: nameNodeFrame.maxX + nameNavigateButtonOffset - 26.0 * 0.5, y: nameNodeFrame.minY - 4.0), size: CGSize(width: 26.0, height: 26.0)), completion: nil)
|
||||||
|
animation.transition.updateTransformScale(layer: nameNavigateButton.layer, scale: CGPoint(x: 0.001, y: 0.001))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if animateNameAvatar {
|
if animateNameAvatar {
|
||||||
animation.animator.updateFrame(layer: nameAvatarNode.layer, frame: nameAvatarFrame, completion: nil)
|
animation.animator.updateFrame(layer: nameAvatarNode.layer, frame: nameAvatarFrame, completion: nil)
|
||||||
animation.animator.updateFrame(layer: nameNavigateButton.layer, frame: nameNavigateFrame, completion: nil)
|
if let nameNavigateButton = strongSelf.nameNavigateButton {
|
||||||
|
animation.animator.updateFrame(layer: nameNavigateButton.layer, frame: nameNavigateFrame, completion: nil)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
nameAvatarNode.frame = CGRect(origin: CGPoint(x: previousNameNodeFrame.minX - 26.0 * 0.5, y: previousNameNodeFrame.minY - 4.0), size: CGSize(width: 26.0, height: 26.0))
|
nameAvatarNode.frame = CGRect(origin: CGPoint(x: previousNameNodeFrame.minX - 26.0 * 0.5, y: previousNameNodeFrame.minY - 4.0), size: CGSize(width: 26.0, height: 26.0))
|
||||||
animation.animator.updateFrame(layer: nameAvatarNode.layer, frame: nameAvatarFrame, completion: nil)
|
animation.animator.updateFrame(layer: nameAvatarNode.layer, frame: nameAvatarFrame, completion: nil)
|
||||||
@ -3576,11 +3595,13 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
nameAvatarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
nameAvatarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
||||||
}
|
}
|
||||||
|
|
||||||
nameNavigateButton.frame = CGRect(origin: CGPoint(x: previousNameNodeFrame.maxX + nameNavigateButtonOffset - 26.0 * 0.5, y: previousNameNodeFrame.minY - 4.0), size: CGSize(width: 26.0, height: 26.0))
|
if let nameNavigateButton = strongSelf.nameNavigateButton {
|
||||||
animation.animator.updateFrame(layer: nameNavigateButton.layer, frame: nameNavigateFrame, completion: nil)
|
nameNavigateButton.frame = CGRect(origin: CGPoint(x: previousNameNodeFrame.maxX + nameNavigateButtonOffset - 26.0 * 0.5, y: previousNameNodeFrame.minY - 4.0), size: CGSize(width: 26.0, height: 26.0))
|
||||||
if animation.isAnimated {
|
animation.animator.updateFrame(layer: nameNavigateButton.layer, frame: nameNavigateFrame, completion: nil)
|
||||||
animation.transition.animateTransformScale(view: nameNavigateButton, from: 0.001)
|
if animation.isAnimated {
|
||||||
nameNavigateButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
animation.transition.animateTransformScale(view: nameNavigateButton, from: 0.001)
|
||||||
|
nameNavigateButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +141,14 @@ private func messagesShouldBeMerged(accountPeerId: PeerId, _ lhs: Message, _ rhs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if abs(lhsEffectiveTimestamp - rhsEffectiveTimestamp) < Int32(10 * 60) && sameChat && sameAuthor && sameThread && !isPaid {
|
var isNonMergeablePaid = isPaid
|
||||||
|
if isNonMergeablePaid {
|
||||||
|
if let channel = lhs.peers[lhs.id.peerId] as? TelegramChannel, channel.flags.contains(.isMonoforum) {
|
||||||
|
isNonMergeablePaid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if abs(lhsEffectiveTimestamp - rhsEffectiveTimestamp) < Int32(10 * 60) && sameChat && sameAuthor && sameThread && !isNonMergeablePaid {
|
||||||
if let channel = lhs.peers[lhs.id.peerId] as? TelegramChannel, case .group = channel.info, lhsEffectiveAuthor?.id == channel.id, !lhs.effectivelyIncoming(accountPeerId) {
|
if let channel = lhs.peers[lhs.id.peerId] as? TelegramChannel, case .group = channel.info, lhsEffectiveAuthor?.id == channel.id, !lhs.effectivelyIncoming(accountPeerId) {
|
||||||
return .none
|
return .none
|
||||||
}
|
}
|
||||||
|
@ -178,6 +178,9 @@ final class ChatMessageNotificationItemNode: NotificationItemNode {
|
|||||||
if firstMessage.id.peerId.isRepliesOrVerificationCodes, let author = firstMessage.forwardInfo?.author {
|
if firstMessage.id.peerId.isRepliesOrVerificationCodes, let author = firstMessage.forwardInfo?.author {
|
||||||
avatarPeer = EnginePeer(author)
|
avatarPeer = EnginePeer(author)
|
||||||
}
|
}
|
||||||
|
if case let .channel(channel) = avatarPeer, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = firstMessage.peers[linkedMonoforumId] as? TelegramChannel {
|
||||||
|
avatarPeer = .channel(mainChannel)
|
||||||
|
}
|
||||||
self.avatarNode.setPeer(context: item.context, theme: presentationData.theme, peer: avatarPeer, overrideImage: peer.id == item.context.account.peerId ? .savedMessagesIcon : nil, emptyColor: presentationData.theme.list.mediaPlaceholderColor)
|
self.avatarNode.setPeer(context: item.context, theme: presentationData.theme, peer: avatarPeer, overrideImage: peer.id == item.context.account.peerId ? .savedMessagesIcon : nil, emptyColor: presentationData.theme.list.mediaPlaceholderColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -995,7 +995,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
|
|
||||||
let placeholderFrame = updatedImageFrame.insetBy(dx: innerImageInset, dy: innerImageInset)
|
let placeholderFrame = updatedImageFrame.insetBy(dx: innerImageInset, dy: innerImageInset)
|
||||||
strongSelf.placeholderNode.update(backgroundColor: nil, foregroundColor: foregroundColor, shimmeringColor: shimmeringColor, data: immediateThumbnailData, size: placeholderFrame.size, enableEffect: item.context.sharedContext.energyUsageSettings.fullTranslucency)
|
strongSelf.placeholderNode.update(backgroundColor: nil, foregroundColor: foregroundColor, shimmeringColor: shimmeringColor, data: immediateThumbnailData, size: placeholderFrame.size, enableEffect: item.context.sharedContext.energyUsageSettings.fullTranslucency)
|
||||||
strongSelf.placeholderNode.frame = placeholderFrame
|
animation.animator.updateFrame(layer: strongSelf.placeholderNode.layer, frame: placeholderFrame, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.messageAccessibilityArea.frame = CGRect(origin: CGPoint(), size: layoutSize)
|
strongSelf.messageAccessibilityArea.frame = CGRect(origin: CGPoint(), size: layoutSize)
|
||||||
@ -1067,7 +1067,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
strongSelf.contextSourceNode.contentNode.addSubnode(threadInfoNode)
|
strongSelf.contextSourceNode.contentNode.addSubnode(threadInfoNode)
|
||||||
}
|
}
|
||||||
let threadInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 6.0) : (params.width - params.rightInset - threadInfoSize.width - layoutConstants.bubble.edgeInset - 8.0)), y: 8.0), size: threadInfoSize)
|
let threadInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 6.0) : (params.width - params.rightInset - threadInfoSize.width - layoutConstants.bubble.edgeInset - 8.0)), y: 8.0), size: threadInfoSize)
|
||||||
threadInfoNode.frame = threadInfoFrame
|
animation.animator.updateFrame(layer: threadInfoNode.layer, frame: threadInfoFrame, completion: nil)
|
||||||
|
|
||||||
headersOffset += threadInfoSize.height + 10.0
|
headersOffset += threadInfoSize.height + 10.0
|
||||||
} else if let replyInfoNode = strongSelf.replyInfoNode {
|
} else if let replyInfoNode = strongSelf.replyInfoNode {
|
||||||
@ -1120,7 +1120,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0 + 5.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 8.0 - 5.0)), y: headersOffset + 8.0 + messageInfoSize.height), size: forwardInfoSize)
|
let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0 + 5.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 8.0 - 5.0)), y: headersOffset + 8.0 + messageInfoSize.height), size: forwardInfoSize)
|
||||||
forwardInfoNode.frame = forwardInfoFrame
|
animation.animator.updateFrame(layer: forwardInfoNode.layer, frame: forwardInfoFrame, completion: nil)
|
||||||
|
|
||||||
messageInfoSize = CGSize(width: messageInfoSize.width, height: messageInfoSize.height + forwardInfoSize.height + 8.0)
|
messageInfoSize = CGSize(width: messageInfoSize.width, height: messageInfoSize.height + forwardInfoSize.height + 8.0)
|
||||||
|
|
||||||
|
@ -590,6 +590,8 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
|
|
||||||
public final class View: UIView {
|
public final class View: UIView {
|
||||||
private let scrollView: ScrollView
|
private let scrollView: ScrollView
|
||||||
|
private let scrollContainerView: UIView
|
||||||
|
private let scrollViewMask: UIImageView
|
||||||
|
|
||||||
private let background = ComponentView<Empty>()
|
private let background = ComponentView<Empty>()
|
||||||
private let separatorLayer: SimpleLayer
|
private let separatorLayer: SimpleLayer
|
||||||
@ -612,13 +614,20 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
self.selectedLineView = UIImageView()
|
self.selectedLineView = UIImageView()
|
||||||
self.scrollView = ScrollView(frame: CGRect())
|
self.scrollView = ScrollView(frame: CGRect())
|
||||||
|
|
||||||
|
self.scrollContainerView = UIView()
|
||||||
|
self.scrollViewMask = UIImageView(image: generateGradientImage(size: CGSize(width: 8.0, height: 8.0), colors: [
|
||||||
|
UIColor(white: 1.0, alpha: 0.0),
|
||||||
|
UIColor(white: 1.0, alpha: 1.0)
|
||||||
|
], locations: [0.0, 1.0], direction: .vertical)?.stretchableImage(withLeftCapWidth: 0, topCapHeight: 8))
|
||||||
|
self.scrollContainerView.mask = self.scrollViewMask
|
||||||
|
|
||||||
self.separatorLayer = SimpleLayer()
|
self.separatorLayer = SimpleLayer()
|
||||||
|
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
self.scrollView.delaysContentTouches = false
|
self.scrollView.delaysContentTouches = false
|
||||||
self.scrollView.canCancelContentTouches = true
|
self.scrollView.canCancelContentTouches = true
|
||||||
self.scrollView.clipsToBounds = false
|
self.scrollView.clipsToBounds = true
|
||||||
self.scrollView.contentInsetAdjustmentBehavior = .never
|
self.scrollView.contentInsetAdjustmentBehavior = .never
|
||||||
if #available(iOS 13.0, *) {
|
if #available(iOS 13.0, *) {
|
||||||
self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false
|
self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false
|
||||||
@ -629,7 +638,8 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
self.scrollView.alwaysBounceVertical = false
|
self.scrollView.alwaysBounceVertical = false
|
||||||
self.scrollView.scrollsToTop = false
|
self.scrollView.scrollsToTop = false
|
||||||
|
|
||||||
self.addSubview(self.scrollView)
|
self.addSubview(self.scrollContainerView)
|
||||||
|
self.scrollContainerView.addSubview(self.scrollView)
|
||||||
self.scrollView.addSubview(self.selectedLineView)
|
self.scrollView.addSubview(self.selectedLineView)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -709,7 +719,7 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
|
|
||||||
if let backgroundView = self.background.view {
|
if let backgroundView = self.background.view {
|
||||||
if backgroundView.superview == nil {
|
if backgroundView.superview == nil {
|
||||||
self.insertSubview(backgroundView, belowSubview: self.scrollView)
|
self.insertSubview(backgroundView, at: 0)
|
||||||
}
|
}
|
||||||
transition.setFrame(view: backgroundView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
transition.setFrame(view: backgroundView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||||
}
|
}
|
||||||
@ -735,20 +745,9 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
|
|
||||||
let itemSpacing: CGFloat = 24.0
|
let itemSpacing: CGFloat = 24.0
|
||||||
|
|
||||||
var contentSize = CGSize(width: panelWidth, height: 0.0)
|
var topContainerInset: CGFloat = containerInsets.top
|
||||||
contentSize.height += containerInsets.top
|
|
||||||
|
|
||||||
var validIds: [Item.Id] = []
|
|
||||||
var isFirst = true
|
|
||||||
var selectedItemFrame: CGRect?
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if isFirst {
|
|
||||||
isFirst = false
|
|
||||||
} else {
|
|
||||||
contentSize.height += itemSpacing
|
|
||||||
}
|
|
||||||
|
|
||||||
var itemTransition = transition
|
var itemTransition = transition
|
||||||
var animateIn = false
|
var animateIn = false
|
||||||
let itemView: TabItemView
|
let itemView: TabItemView
|
||||||
@ -764,11 +763,11 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
component.togglePanel()
|
component.togglePanel()
|
||||||
})
|
})
|
||||||
self.tabItemView = itemView
|
self.tabItemView = itemView
|
||||||
self.scrollView.addSubview(itemView)
|
self.addSubview(itemView)
|
||||||
}
|
}
|
||||||
|
|
||||||
let itemSize = itemView.update(context: component.context, theme: component.theme, width: panelWidth, transition: .immediate)
|
let itemSize = itemView.update(context: component.context, theme: component.theme, width: panelWidth, transition: .immediate)
|
||||||
let itemFrame = CGRect(origin: CGPoint(x: containerInsets.left, y: contentSize.height), size: itemSize)
|
let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: topContainerInset), size: itemSize)
|
||||||
|
|
||||||
itemTransition.setPosition(layer: itemView.layer, position: itemFrame.center)
|
itemTransition.setPosition(layer: itemView.layer, position: itemFrame.center)
|
||||||
itemTransition.setBounds(layer: itemView.layer, bounds: CGRect(origin: CGPoint(), size: itemFrame.size))
|
itemTransition.setBounds(layer: itemView.layer, bounds: CGRect(origin: CGPoint(), size: itemFrame.size))
|
||||||
@ -778,10 +777,17 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
transition.containedViewLayoutTransition.animateTransformScale(view: itemView, from: 0.001)
|
transition.containedViewLayoutTransition.animateTransformScale(view: itemView, from: 0.001)
|
||||||
}
|
}
|
||||||
|
|
||||||
contentSize.height += itemSize.height
|
topContainerInset += itemSize.height
|
||||||
contentSize.height -= 20.0
|
topContainerInset -= 24.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var contentSize = CGSize(width: panelWidth, height: 0.0)
|
||||||
|
contentSize.height += 36.0
|
||||||
|
|
||||||
|
var validIds: [Item.Id] = []
|
||||||
|
var isFirst = true
|
||||||
|
var selectedItemFrame: CGRect?
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if isFirst {
|
if isFirst {
|
||||||
isFirst = false
|
isFirst = false
|
||||||
@ -931,7 +937,11 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
|
|
||||||
contentSize.height += containerInsets.bottom
|
contentSize.height += containerInsets.bottom
|
||||||
|
|
||||||
let scrollSize = CGSize(width: availableSize.width, height: availableSize.height)
|
let scrollSize = CGSize(width: availableSize.width, height: availableSize.height - topContainerInset)
|
||||||
|
|
||||||
|
self.scrollContainerView.frame = CGRect(origin: CGPoint(x: 0.0, y: topContainerInset), size: scrollSize)
|
||||||
|
self.scrollViewMask.frame = CGRect(origin: CGPoint(x: 0.0, y: topContainerInset), size: scrollSize)
|
||||||
|
|
||||||
if self.scrollView.bounds.size != scrollSize {
|
if self.scrollView.bounds.size != scrollSize {
|
||||||
self.scrollView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: scrollSize)
|
self.scrollView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: scrollSize)
|
||||||
}
|
}
|
||||||
|
@ -145,6 +145,8 @@ final class ChatIntroItemComponent: Component {
|
|||||||
backgroundNode: backgroundNode,
|
backgroundNode: backgroundNode,
|
||||||
size: size,
|
size: size,
|
||||||
insets: UIEdgeInsets(),
|
insets: UIEdgeInsets(),
|
||||||
|
leftInset: 0.0,
|
||||||
|
rightInset: 0.0,
|
||||||
transition: .immediate
|
transition: .immediate
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -810,9 +810,16 @@ extension ChatControllerImpl {
|
|||||||
contactStatus = ChatContactStatus(canAddContact: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy, managingBot: managingBot)
|
contactStatus = ChatContactStatus(canAddContact: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy, managingBot: managingBot)
|
||||||
|
|
||||||
if let channel = peerView.peers[peerView.peerId] as? TelegramChannel {
|
if let channel = peerView.peers[peerView.peerId] as? TelegramChannel {
|
||||||
if channel.flags.contains(.isCreator) || channel.adminRights != nil {
|
if channel.isMonoForum {
|
||||||
|
if let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = peerView.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.sendSomething) {
|
||||||
|
} else {
|
||||||
|
sendPaidMessageStars = channel.sendPaidMessageStars
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
sendPaidMessageStars = channel.sendPaidMessageStars
|
if channel.flags.contains(.isCreator) || channel.adminRights != nil {
|
||||||
|
} else {
|
||||||
|
sendPaidMessageStars = channel.sendPaidMessageStars
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1857,8 +1864,6 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
if globalRemainingUnreadChatCount > 0 {
|
if globalRemainingUnreadChatCount > 0 {
|
||||||
strongSelf.initialNavigationBadge = "\(globalRemainingUnreadChatCount)"
|
strongSelf.initialNavigationBadge = "\(globalRemainingUnreadChatCount)"
|
||||||
} else {
|
|
||||||
strongSelf.initialNavigationBadge = ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,7 +195,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
private var emptyNode: ChatEmptyNode?
|
private var emptyNode: ChatEmptyNode?
|
||||||
private(set) var emptyType: ChatHistoryNodeLoadState.EmptyType?
|
private(set) var emptyType: ChatHistoryNodeLoadState.EmptyType?
|
||||||
private var didDisplayEmptyGreeting = false
|
private var didDisplayEmptyGreeting = false
|
||||||
private var validEmptyNodeLayout: (CGSize, UIEdgeInsets)?
|
private var validEmptyNodeLayout: (CGSize, UIEdgeInsets, CGFloat, CGFloat)?
|
||||||
var restrictedNode: ChatRecentActionsEmptyNode?
|
var restrictedNode: ChatRecentActionsEmptyNode?
|
||||||
|
|
||||||
private(set) var validLayout: (ContainerViewLayout, CGFloat)?
|
private(set) var validLayout: (ContainerViewLayout, CGFloat)?
|
||||||
@ -1019,7 +1019,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
self.contentContainerNode.contentNode.insertSubnode(emptyNode, aboveSubnode: self.historyNodeContainer)
|
self.contentContainerNode.contentNode.insertSubnode(emptyNode, aboveSubnode: self.historyNodeContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (size, insets) = self.validEmptyNodeLayout {
|
if let (size, insets, leftInset, rightInset) = self.validEmptyNodeLayout {
|
||||||
let mappedType: ChatEmptyNode.Subject.EmptyType
|
let mappedType: ChatEmptyNode.Subject.EmptyType
|
||||||
switch emptyType {
|
switch emptyType {
|
||||||
case .generic:
|
case .generic:
|
||||||
@ -1033,7 +1033,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
case .botInfo:
|
case .botInfo:
|
||||||
mappedType = .botInfo
|
mappedType = .botInfo
|
||||||
}
|
}
|
||||||
emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, subject: .emptyChat(mappedType), loadingNode: wasLoading && self.loadingNode.supernode != nil ? self.loadingNode : nil, backgroundNode: self.backgroundNode, size: size, insets: insets, transition: .immediate)
|
emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, subject: .emptyChat(mappedType), loadingNode: wasLoading && self.loadingNode.supernode != nil ? self.loadingNode : nil, backgroundNode: self.backgroundNode, size: size, insets: insets, leftInset: leftInset, rightInset: rightInset, transition: .immediate)
|
||||||
emptyNode.frame = CGRect(origin: CGPoint(), size: size)
|
emptyNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||||
}
|
}
|
||||||
if animated {
|
if animated {
|
||||||
@ -2046,6 +2046,10 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
transition.updatePosition(node: self.historyNode, position: CGPoint(x: contentBounds.size.width / 2.0, y: contentBounds.size.height / 2.0))
|
transition.updatePosition(node: self.historyNode, position: CGPoint(x: contentBounds.size.width / 2.0, y: contentBounds.size.height / 2.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if immediatelyLayoutLeftPanelNodeAndAnimateAppearance || dismissedLeftPanel != nil || immediatelyLayoutTitleTopicsAccessoryPanelNodeAndAnimateAppearance || dismissedTitleTopicsAccessoryPanelNode != nil {
|
||||||
|
self.historyNode.resetScrolledToItem()
|
||||||
|
}
|
||||||
|
|
||||||
if let blurredHistoryNode = self.blurredHistoryNode {
|
if let blurredHistoryNode = self.blurredHistoryNode {
|
||||||
transition.updateFrame(node: blurredHistoryNode, frame: contentBounds)
|
transition.updateFrame(node: blurredHistoryNode, frame: contentBounds)
|
||||||
}
|
}
|
||||||
@ -2199,28 +2203,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var emptyNodeInsets = insets
|
|
||||||
emptyNodeInsets.bottom += inputPanelsHeight
|
|
||||||
self.validEmptyNodeLayout = (contentBounds.size, emptyNodeInsets)
|
|
||||||
if let emptyNode = self.emptyNode, let emptyType = self.emptyType {
|
|
||||||
let mappedType: ChatEmptyNode.Subject.EmptyType
|
|
||||||
switch emptyType {
|
|
||||||
case .generic:
|
|
||||||
mappedType = .generic
|
|
||||||
case .joined:
|
|
||||||
mappedType = .joined
|
|
||||||
case .clearedHistory:
|
|
||||||
mappedType = .clearedHistory
|
|
||||||
case .topic:
|
|
||||||
mappedType = .topic
|
|
||||||
case .botInfo:
|
|
||||||
mappedType = .botInfo
|
|
||||||
}
|
|
||||||
emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, subject: .emptyChat(mappedType), loadingNode: nil, backgroundNode: self.backgroundNode, size: contentBounds.size, insets: emptyNodeInsets, transition: transition)
|
|
||||||
transition.updateFrame(node: emptyNode, frame: contentBounds)
|
|
||||||
emptyNode.update(rect: contentBounds, within: contentBounds.size, transition: transition)
|
|
||||||
}
|
|
||||||
|
|
||||||
var contentBottomInset: CGFloat = inputPanelsHeight + 4.0
|
var contentBottomInset: CGFloat = inputPanelsHeight + 4.0
|
||||||
|
|
||||||
if let scrollContainerNode = self.scrollContainerNode {
|
if let scrollContainerNode = self.scrollContainerNode {
|
||||||
@ -2239,10 +2221,17 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
|
|
||||||
let visibleAreaInset = UIEdgeInsets(top: containerInsets.top, left: 0.0, bottom: containerInsets.bottom + inputPanelsHeight, right: 0.0)
|
let visibleAreaInset = UIEdgeInsets(top: containerInsets.top, left: 0.0, bottom: containerInsets.bottom + inputPanelsHeight, right: 0.0)
|
||||||
self.visibleAreaInset = visibleAreaInset
|
self.visibleAreaInset = visibleAreaInset
|
||||||
self.loadingNode.updateLayout(size: contentBounds.size, insets: visibleAreaInset, transition: transition)
|
|
||||||
|
var loadingNodeInsets = visibleAreaInset
|
||||||
|
loadingNodeInsets.left = layout.safeInsets.left
|
||||||
|
loadingNodeInsets.right = layout.safeInsets.right
|
||||||
|
if let leftPanelSize {
|
||||||
|
loadingNodeInsets.left += leftPanelSize.width
|
||||||
|
}
|
||||||
|
self.loadingNode.updateLayout(size: contentBounds.size, insets: loadingNodeInsets, transition: transition)
|
||||||
|
|
||||||
if let loadingPlaceholderNode = self.loadingPlaceholderNode {
|
if let loadingPlaceholderNode = self.loadingPlaceholderNode {
|
||||||
loadingPlaceholderNode.updateLayout(size: contentBounds.size, insets: visibleAreaInset, metrics: layout.metrics, transition: transition)
|
loadingPlaceholderNode.updateLayout(size: contentBounds.size, insets: loadingNodeInsets, metrics: layout.metrics, transition: transition)
|
||||||
loadingPlaceholderNode.update(rect: contentBounds, within: contentBounds.size, transition: transition)
|
loadingPlaceholderNode.update(rect: contentBounds, within: contentBounds.size, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2293,6 +2282,28 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
listInsets.left += leftPanelSize.width
|
listInsets.left += leftPanelSize.width
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var emptyNodeInsets = insets
|
||||||
|
emptyNodeInsets.bottom += inputPanelsHeight
|
||||||
|
self.validEmptyNodeLayout = (contentBounds.size, emptyNodeInsets, listInsets.left, listInsets.right)
|
||||||
|
if let emptyNode = self.emptyNode, let emptyType = self.emptyType {
|
||||||
|
let mappedType: ChatEmptyNode.Subject.EmptyType
|
||||||
|
switch emptyType {
|
||||||
|
case .generic:
|
||||||
|
mappedType = .generic
|
||||||
|
case .joined:
|
||||||
|
mappedType = .joined
|
||||||
|
case .clearedHistory:
|
||||||
|
mappedType = .clearedHistory
|
||||||
|
case .topic:
|
||||||
|
mappedType = .topic
|
||||||
|
case .botInfo:
|
||||||
|
mappedType = .botInfo
|
||||||
|
}
|
||||||
|
emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, subject: .emptyChat(mappedType), loadingNode: nil, backgroundNode: self.backgroundNode, size: contentBounds.size, insets: emptyNodeInsets, leftInset: listInsets.left, rightInset: listInsets.right, transition: transition)
|
||||||
|
transition.updateFrame(node: emptyNode, frame: contentBounds)
|
||||||
|
emptyNode.update(rect: contentBounds, within: contentBounds.size, transition: transition)
|
||||||
|
}
|
||||||
|
|
||||||
var displayTopDimNode = false
|
var displayTopDimNode = false
|
||||||
let ensureTopInsetForOverlayHighlightedItems: CGFloat? = nil
|
let ensureTopInsetForOverlayHighlightedItems: CGFloat? = nil
|
||||||
var expandTopDimNode = false
|
var expandTopDimNode = false
|
||||||
@ -3283,71 +3294,16 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
guard let self else {
|
guard let self else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
guard let peerId = self.chatPresentationInterfaceState.chatLocation.peerId else {
|
guard let peer = self.chatPresentationInterfaceState.renderedPeer?.peer else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !peer.isForumOrMonoForum {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let viewKey: PostboxViewKey = .savedMessagesIndex(peerId: peerId)
|
let threadListSignal: Signal<EngineChatList, NoError> = context.sharedContext.subscribeChatListData(context: self.context, location: peer.isMonoForum ? .savedMessagesChats(peerId: peer.id) : .forum(peerId: peer.id))
|
||||||
|
|
||||||
let threadListSignal: Signal<EngineChatList?, NoError> = self.context.account.postbox.combinedView(keys: [viewKey])
|
return threadListSignal |> map(Optional.init)
|
||||||
|> map { views -> EngineChatList? in
|
|
||||||
guard let view = views.views[viewKey] as? MessageHistorySavedMessagesIndexView else {
|
|
||||||
preconditionFailure()
|
|
||||||
}
|
|
||||||
|
|
||||||
var items: [EngineChatList.Item] = []
|
|
||||||
for item in view.items {
|
|
||||||
guard let sourcePeer = item.peer else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
let sourceId = PeerId(item.id)
|
|
||||||
|
|
||||||
var messages: [EngineMessage] = []
|
|
||||||
if let topMessage = item.topMessage {
|
|
||||||
messages.append(EngineMessage(topMessage))
|
|
||||||
}
|
|
||||||
|
|
||||||
let mappedMessageIndex = MessageIndex(id: MessageId(peerId: sourceId, namespace: item.index.id.namespace, id: item.index.id.id), timestamp: item.index.timestamp)
|
|
||||||
|
|
||||||
items.append(EngineChatList.Item(
|
|
||||||
id: .chatList(sourceId),
|
|
||||||
index: .chatList(ChatListIndex(pinningIndex: item.pinnedIndex.flatMap(UInt16.init), messageIndex: mappedMessageIndex)),
|
|
||||||
messages: messages,
|
|
||||||
readCounters: EnginePeerReadCounters(
|
|
||||||
incomingReadId: 0, outgoingReadId: 0, count: Int32(item.unreadCount), markedUnread: item.markedUnread),
|
|
||||||
isMuted: false,
|
|
||||||
draft: nil,
|
|
||||||
threadData: nil,
|
|
||||||
renderedPeer: EngineRenderedPeer(peer: EnginePeer(sourcePeer)),
|
|
||||||
presence: nil,
|
|
||||||
hasUnseenMentions: false,
|
|
||||||
hasUnseenReactions: false,
|
|
||||||
forumTopicData: nil,
|
|
||||||
topForumTopicItems: [],
|
|
||||||
hasFailed: false,
|
|
||||||
isContact: false,
|
|
||||||
autoremoveTimeout: nil,
|
|
||||||
storyStats: nil,
|
|
||||||
displayAsTopicList: false,
|
|
||||||
isPremiumRequiredToMessage: false,
|
|
||||||
mediaDraftContentType: nil
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
let list = EngineChatList(
|
|
||||||
items: items.reversed(),
|
|
||||||
groupItems: [],
|
|
||||||
additionalItems: [],
|
|
||||||
hasEarlier: false,
|
|
||||||
hasLater: false,
|
|
||||||
isLoading: view.isLoading
|
|
||||||
)
|
|
||||||
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
return threadListSignal
|
|
||||||
},
|
},
|
||||||
loadMoreSearchResults: { [weak self] in
|
loadMoreSearchResults: { [weak self] in
|
||||||
guard let self, let controller = self.controller else {
|
guard let self, let controller = self.controller else {
|
||||||
|
@ -135,6 +135,20 @@ func chatHistoryEntriesForView(
|
|||||||
continue loop
|
continue loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if case .peer = location {
|
||||||
|
for media in message.media {
|
||||||
|
if let action = media as? TelegramMediaAction, case .groupCreated = action.action {
|
||||||
|
var chatPeer: Peer?
|
||||||
|
for entry in view.additionalData {
|
||||||
|
if case let .peer(_, peer) = entry {
|
||||||
|
chatPeer = peer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let channel = chatPeer as? TelegramChannel, channel.isMonoForum {
|
||||||
|
continue loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
count += 1
|
count += 1
|
||||||
|
@ -241,7 +241,7 @@ func titleTopicsPanelForChatPresentationInterfaceState(_ chatPresentationInterfa
|
|||||||
return panel
|
return panel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForum, chatPresentationInterfaceState.search == nil {
|
} else if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForum, (channel.flags.contains(.displayForumAsTabs) || context.sharedContext.immediateExperimentalUISettings.allForumsHaveTabs), chatPresentationInterfaceState.search == nil {
|
||||||
let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top
|
let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top
|
||||||
if case .top = topicListDisplayMode, let peerId = chatPresentationInterfaceState.chatLocation.peerId {
|
if case .top = topicListDisplayMode, let peerId = chatPresentationInterfaceState.chatLocation.peerId {
|
||||||
if let currentPanel = currentPanel as? ChatTopicListTitleAccessoryPanelNode {
|
if let currentPanel = currentPanel as? ChatTopicListTitleAccessoryPanelNode {
|
||||||
|
@ -182,7 +182,7 @@ private final class CustomBadgeComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, ChatControllerCustomNavigationPanelNode, ASScrollViewDelegate {
|
final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, ChatControllerCustomNavigationPanelNode {
|
||||||
private struct Params: Equatable {
|
private struct Params: Equatable {
|
||||||
var width: CGFloat
|
var width: CGFloat
|
||||||
var leftInset: CGFloat
|
var leftInset: CGFloat
|
||||||
@ -736,6 +736,8 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
private let isMonoforum: Bool
|
private let isMonoforum: Bool
|
||||||
|
|
||||||
private let scrollView: ScrollView
|
private let scrollView: ScrollView
|
||||||
|
private let scrollViewContainer: UIView
|
||||||
|
private let scrollViewMask: UIImageView
|
||||||
|
|
||||||
private var params: Params?
|
private var params: Params?
|
||||||
|
|
||||||
@ -756,12 +758,18 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
self.selectedLineView = UIImageView()
|
self.selectedLineView = UIImageView()
|
||||||
|
|
||||||
self.scrollView = ScrollView(frame: CGRect())
|
self.scrollView = ScrollView(frame: CGRect())
|
||||||
|
self.scrollViewMask = UIImageView(image: generateGradientImage(size: CGSize(width: 8.0, height: 8.0), colors: [
|
||||||
|
UIColor(white: 1.0, alpha: 0.0),
|
||||||
|
UIColor(white: 1.0, alpha: 1.0)
|
||||||
|
], locations: [0.0, 1.0], direction: .horizontal)?.stretchableImage(withLeftCapWidth: 8, topCapHeight: 0))
|
||||||
|
|
||||||
|
self.scrollViewContainer = UIView()
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.scrollView.delaysContentTouches = false
|
self.scrollView.delaysContentTouches = false
|
||||||
self.scrollView.canCancelContentTouches = true
|
self.scrollView.canCancelContentTouches = true
|
||||||
self.scrollView.clipsToBounds = false
|
self.scrollView.clipsToBounds = true
|
||||||
self.scrollView.contentInsetAdjustmentBehavior = .never
|
self.scrollView.contentInsetAdjustmentBehavior = .never
|
||||||
if #available(iOS 13.0, *) {
|
if #available(iOS 13.0, *) {
|
||||||
self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false
|
self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false
|
||||||
@ -771,9 +779,11 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
self.scrollView.alwaysBounceHorizontal = false
|
self.scrollView.alwaysBounceHorizontal = false
|
||||||
self.scrollView.alwaysBounceVertical = false
|
self.scrollView.alwaysBounceVertical = false
|
||||||
self.scrollView.scrollsToTop = false
|
self.scrollView.scrollsToTop = false
|
||||||
self.scrollView.delegate = self.wrappedScrollViewDelegate
|
|
||||||
|
|
||||||
self.view.addSubview(self.scrollView)
|
self.scrollViewContainer.addSubview(self.scrollView)
|
||||||
|
self.scrollViewContainer.mask = self.scrollViewMask
|
||||||
|
|
||||||
|
self.view.addSubview(self.scrollViewContainer)
|
||||||
|
|
||||||
self.scrollView.addSubview(self.selectedLineView)
|
self.scrollView.addSubview(self.selectedLineView)
|
||||||
|
|
||||||
@ -843,20 +853,9 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
let containerInsets = UIEdgeInsets(top: 0.0, left: params.leftInset + 16.0, bottom: 0.0, right: params.rightInset + 16.0)
|
let containerInsets = UIEdgeInsets(top: 0.0, left: params.leftInset + 16.0, bottom: 0.0, right: params.rightInset + 16.0)
|
||||||
let itemSpacing: CGFloat = 24.0
|
let itemSpacing: CGFloat = 24.0
|
||||||
|
|
||||||
var contentSize = CGSize(width: 0.0, height: panelHeight)
|
var leftContentInset: CGFloat = containerInsets.left + 8.0
|
||||||
contentSize.width += containerInsets.left + 8.0
|
|
||||||
|
|
||||||
var validIds: [Item.Id] = []
|
|
||||||
var isFirst = true
|
|
||||||
var selectedItemFrame: CGRect?
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if isFirst {
|
|
||||||
isFirst = false
|
|
||||||
} else {
|
|
||||||
contentSize.width += itemSpacing
|
|
||||||
}
|
|
||||||
|
|
||||||
var itemTransition = transition
|
var itemTransition = transition
|
||||||
var animateIn = false
|
var animateIn = false
|
||||||
let itemView: TabItemView
|
let itemView: TabItemView
|
||||||
@ -872,11 +871,11 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
self.interfaceInteraction?.toggleChatSidebarMode()
|
self.interfaceInteraction?.toggleChatSidebarMode()
|
||||||
})
|
})
|
||||||
self.tabItemView = itemView
|
self.tabItemView = itemView
|
||||||
self.scrollView.addSubview(itemView)
|
self.view.addSubview(itemView)
|
||||||
}
|
}
|
||||||
|
|
||||||
let itemSize = itemView.update(context: self.context, theme: params.interfaceState.theme, height: panelHeight, transition: .immediate)
|
let itemSize = itemView.update(context: self.context, theme: params.interfaceState.theme, height: panelHeight, transition: .immediate)
|
||||||
let itemFrame = CGRect(origin: CGPoint(x: contentSize.width, y: -5.0), size: itemSize)
|
let itemFrame = CGRect(origin: CGPoint(x: leftContentInset, y: -5.0), size: itemSize)
|
||||||
|
|
||||||
itemTransition.updatePosition(layer: itemView.layer, position: itemFrame.center)
|
itemTransition.updatePosition(layer: itemView.layer, position: itemFrame.center)
|
||||||
itemTransition.updateBounds(layer: itemView.layer, bounds: CGRect(origin: CGPoint(), size: itemFrame.size))
|
itemTransition.updateBounds(layer: itemView.layer, bounds: CGRect(origin: CGPoint(), size: itemFrame.size))
|
||||||
@ -886,9 +885,15 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
transition.animateTransformScale(view: itemView, from: 0.001)
|
transition.animateTransformScale(view: itemView, from: 0.001)
|
||||||
}
|
}
|
||||||
|
|
||||||
contentSize.width += itemSize.width
|
leftContentInset += itemSize.width + 8.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var contentSize = CGSize(width: itemSpacing - 8.0, height: panelHeight)
|
||||||
|
|
||||||
|
var validIds: [Item.Id] = []
|
||||||
|
var isFirst = true
|
||||||
|
var selectedItemFrame: CGRect?
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if isFirst {
|
if isFirst {
|
||||||
isFirst = false
|
isFirst = false
|
||||||
@ -1034,9 +1039,12 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
|
|
||||||
contentSize.width += containerInsets.right
|
contentSize.width += containerInsets.right
|
||||||
|
|
||||||
let scrollSize = CGSize(width: params.width, height: contentSize.height)
|
let scrollSize = CGSize(width: params.width - leftContentInset, height: contentSize.height)
|
||||||
|
self.scrollViewContainer.frame = CGRect(origin: CGPoint(x: leftContentInset, y: 0.0), size: scrollSize)
|
||||||
|
self.scrollViewMask.frame = CGRect(origin: CGPoint(), size: scrollSize)
|
||||||
|
|
||||||
if self.scrollView.bounds.size != scrollSize {
|
if self.scrollView.bounds.size != scrollSize {
|
||||||
self.scrollView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: scrollSize)
|
self.scrollView.frame = CGRect(origin: CGPoint(), size: scrollSize)
|
||||||
}
|
}
|
||||||
if self.scrollView.contentSize != contentSize {
|
if self.scrollView.contentSize != contentSize {
|
||||||
self.scrollView.contentSize = contentSize
|
self.scrollView.contentSize = contentSize
|
||||||
|
@ -167,9 +167,13 @@ final class ChatTranslationPanelNode: ASDisplayNode {
|
|||||||
let buttonTextSize = self.buttonTextNode.updateLayout(CGSize(width: width - contentRightInset - moreButtonSize.width, height: panelHeight))
|
let buttonTextSize = self.buttonTextNode.updateLayout(CGSize(width: width - contentRightInset - moreButtonSize.width, height: panelHeight))
|
||||||
if let icon = self.buttonIconNode.image {
|
if let icon = self.buttonIconNode.image {
|
||||||
let buttonSize = CGSize(width: buttonTextSize.width + icon.size.width + buttonSpacing + buttonPadding * 2.0, height: panelHeight)
|
let buttonSize = CGSize(width: buttonTextSize.width + icon.size.width + buttonSpacing + buttonPadding * 2.0, height: panelHeight)
|
||||||
self.button.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - buttonSize.width) / 2.0), y: 0.0), size: buttonSize)
|
transition.updateFrame(node: self.button, frame: CGRect(origin: CGPoint(x: leftInset + floorToScreenPixels((width - leftInset - rightInset - buttonSize.width) / 2.0), y: 0.0), size: buttonSize))
|
||||||
self.buttonIconNode.frame = CGRect(origin: CGPoint(x: buttonPadding, y: floorToScreenPixels((buttonSize.height - icon.size.height) / 2.0)), size: icon.size)
|
|
||||||
self.buttonTextNode.frame = CGRect(origin: CGPoint(x: buttonPadding + icon.size.width + buttonSpacing, y: floorToScreenPixels((buttonSize.height - buttonTextSize.height) / 2.0)), size: buttonTextSize)
|
transition.updateFrame(node: self.buttonIconNode, frame: CGRect(origin: CGPoint(x: buttonPadding, y: floorToScreenPixels((buttonSize.height - icon.size.height) / 2.0)), size: icon.size))
|
||||||
|
|
||||||
|
let buttonTextFrame = CGRect(origin: CGPoint(x: buttonPadding + icon.size.width + buttonSpacing, y: floorToScreenPixels((buttonSize.height - buttonTextSize.height) / 2.0)), size: buttonTextSize)
|
||||||
|
transition.updatePosition(node: self.buttonTextNode, position: buttonTextFrame.center)
|
||||||
|
self.buttonTextNode.bounds = CGRect(origin: CGPoint(), size: buttonTextFrame.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel)))
|
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel)))
|
||||||
|
@ -42,16 +42,20 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
|
|||||||
viewForumAsMessages = .single(false)
|
viewForumAsMessages = .single(false)
|
||||||
}
|
}
|
||||||
} else if case let .peer(peer) = params.chatLocation, case let .channel(channel) = peer, channel.flags.contains(.isForum) {
|
} else if case let .peer(peer) = params.chatLocation, case let .channel(channel) = peer, channel.flags.contains(.isForum) {
|
||||||
viewForumAsMessages = params.context.account.postbox.combinedView(keys: [.cachedPeerData(peerId: peer.id)])
|
if channel.flags.contains(.displayForumAsTabs) {
|
||||||
|> take(1)
|
viewForumAsMessages = .single(true)
|
||||||
|> map { combinedView in
|
} else {
|
||||||
guard let cachedDataView = combinedView.views[.cachedPeerData(peerId: peer.id)] as? CachedPeerDataView else {
|
viewForumAsMessages = params.context.account.postbox.combinedView(keys: [.cachedPeerData(peerId: peer.id)])
|
||||||
return false
|
|> take(1)
|
||||||
}
|
|> map { combinedView in
|
||||||
if let cachedData = cachedDataView.cachedPeerData as? CachedChannelData, case let .known(viewForumAsMessages) = cachedData.viewForumAsMessages, viewForumAsMessages {
|
guard let cachedDataView = combinedView.views[.cachedPeerData(peerId: peer.id)] as? CachedPeerDataView else {
|
||||||
return true
|
return false
|
||||||
} else {
|
}
|
||||||
return false
|
if let cachedData = cachedDataView.cachedPeerData as? CachedChannelData, case let .known(viewForumAsMessages) = cachedData.viewForumAsMessages, viewForumAsMessages {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if case let .peer(peer) = params.chatLocation, peer.id == params.context.account.peerId {
|
} else if case let .peer(peer) = params.chatLocation, peer.id == params.context.account.peerId {
|
||||||
|
@ -64,7 +64,7 @@ private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceh
|
|||||||
self.wallpaperBackgroundNode.updateLayout(size: size, displayMode: needsTiling ? .aspectFit : .aspectFill, transition: transition)
|
self.wallpaperBackgroundNode.updateLayout(size: size, displayMode: needsTiling ? .aspectFit : .aspectFill, transition: transition)
|
||||||
transition.updateFrame(node: self.wallpaperBackgroundNode, frame: contentBounds)
|
transition.updateFrame(node: self.wallpaperBackgroundNode, frame: contentBounds)
|
||||||
|
|
||||||
self.emptyNode.updateLayout(interfaceState: self.presentationInterfaceState, subject: .detailsPlaceholder, loadingNode: nil, backgroundNode: self.wallpaperBackgroundNode, size: contentBounds.size, insets: .zero, transition: transition)
|
self.emptyNode.updateLayout(interfaceState: self.presentationInterfaceState, subject: .detailsPlaceholder, loadingNode: nil, backgroundNode: self.wallpaperBackgroundNode, size: contentBounds.size, insets: .zero, leftInset: 0.0, rightInset: 0.0, transition: transition)
|
||||||
transition.updateFrame(node: self.emptyNode, frame: CGRect(origin: .zero, size: size))
|
transition.updateFrame(node: self.emptyNode, frame: CGRect(origin: .zero, size: size))
|
||||||
self.emptyNode.update(rect: contentBounds, within: contentBounds.size, transition: transition)
|
self.emptyNode.update(rect: contentBounds, within: contentBounds.size, transition: transition)
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
public var fakeAds: Bool
|
public var fakeAds: Bool
|
||||||
public var conferenceDebug: Bool
|
public var conferenceDebug: Bool
|
||||||
public var checkSerializedData: Bool
|
public var checkSerializedData: Bool
|
||||||
|
public var allForumsHaveTabs: Bool
|
||||||
|
|
||||||
public static var defaultSettings: ExperimentalUISettings {
|
public static var defaultSettings: ExperimentalUISettings {
|
||||||
return ExperimentalUISettings(
|
return ExperimentalUISettings(
|
||||||
@ -109,7 +110,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
devRequests: false,
|
devRequests: false,
|
||||||
fakeAds: false,
|
fakeAds: false,
|
||||||
conferenceDebug: false,
|
conferenceDebug: false,
|
||||||
checkSerializedData: false
|
checkSerializedData: false,
|
||||||
|
allForumsHaveTabs: false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +156,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
devRequests: Bool,
|
devRequests: Bool,
|
||||||
fakeAds: Bool,
|
fakeAds: Bool,
|
||||||
conferenceDebug: Bool,
|
conferenceDebug: Bool,
|
||||||
checkSerializedData: Bool
|
checkSerializedData: Bool,
|
||||||
|
allForumsHaveTabs: Bool
|
||||||
) {
|
) {
|
||||||
self.keepChatNavigationStack = keepChatNavigationStack
|
self.keepChatNavigationStack = keepChatNavigationStack
|
||||||
self.skipReadHistory = skipReadHistory
|
self.skipReadHistory = skipReadHistory
|
||||||
@ -197,6 +200,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
self.fakeAds = fakeAds
|
self.fakeAds = fakeAds
|
||||||
self.conferenceDebug = conferenceDebug
|
self.conferenceDebug = conferenceDebug
|
||||||
self.checkSerializedData = checkSerializedData
|
self.checkSerializedData = checkSerializedData
|
||||||
|
self.allForumsHaveTabs = allForumsHaveTabs
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
@ -243,6 +247,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
self.fakeAds = try container.decodeIfPresent(Bool.self, forKey: "fakeAds") ?? false
|
self.fakeAds = try container.decodeIfPresent(Bool.self, forKey: "fakeAds") ?? false
|
||||||
self.conferenceDebug = try container.decodeIfPresent(Bool.self, forKey: "conferenceDebug") ?? false
|
self.conferenceDebug = try container.decodeIfPresent(Bool.self, forKey: "conferenceDebug") ?? false
|
||||||
self.checkSerializedData = try container.decodeIfPresent(Bool.self, forKey: "checkSerializedData") ?? false
|
self.checkSerializedData = try container.decodeIfPresent(Bool.self, forKey: "checkSerializedData") ?? false
|
||||||
|
self.allForumsHaveTabs = try container.decodeIfPresent(Bool.self, forKey: "allForumsHaveTabs") ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
@ -289,6 +294,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
try container.encodeIfPresent(self.fakeAds, forKey: "fakeAds")
|
try container.encodeIfPresent(self.fakeAds, forKey: "fakeAds")
|
||||||
try container.encodeIfPresent(self.conferenceDebug, forKey: "conferenceDebug")
|
try container.encodeIfPresent(self.conferenceDebug, forKey: "conferenceDebug")
|
||||||
try container.encodeIfPresent(self.checkSerializedData, forKey: "checkSerializedData")
|
try container.encodeIfPresent(self.checkSerializedData, forKey: "checkSerializedData")
|
||||||
|
try container.encodeIfPresent(self.allForumsHaveTabs, forKey: "allForumsHaveTabs")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user