mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 01:10:09 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
6c337b0f84
@ -1785,8 +1785,10 @@ ios_application(
|
|||||||
#"//submodules/TelegramApi",
|
#"//submodules/TelegramApi",
|
||||||
#"//submodules/TelegramCore",
|
#"//submodules/TelegramCore",
|
||||||
#"//submodules/FFMpegBinding",
|
#"//submodules/FFMpegBinding",
|
||||||
"//submodules/Display",
|
#"//submodules/Display",
|
||||||
#"//third-party/webrtc",
|
#"//third-party/webrtc",
|
||||||
|
"//submodules/AsyncDisplayKit",
|
||||||
|
"//submodules/ObjCRuntimeUtils",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -95,6 +95,7 @@ _IGNORE_OBJC_LIBRARY_ATTRS = [
|
|||||||
"toolchains",
|
"toolchains",
|
||||||
"transitive_configs",
|
"transitive_configs",
|
||||||
"visibility",
|
"visibility",
|
||||||
|
"package_metadata",
|
||||||
]
|
]
|
||||||
|
|
||||||
_IGNORE_OBJC_LIBRARY_EMPTY_ATTRS = [
|
_IGNORE_OBJC_LIBRARY_EMPTY_ATTRS = [
|
||||||
@ -106,6 +107,8 @@ _IGNORE_OBJC_LIBRARY_EMPTY_ATTRS = [
|
|||||||
"restricted_to",
|
"restricted_to",
|
||||||
"textual_hdrs",
|
"textual_hdrs",
|
||||||
"sdk_includes",
|
"sdk_includes",
|
||||||
|
"conlyopts",
|
||||||
|
"cxxopts",
|
||||||
]
|
]
|
||||||
|
|
||||||
_OBJC_LIBRARY_ATTRS = {
|
_OBJC_LIBRARY_ATTRS = {
|
||||||
@ -154,6 +157,8 @@ _IGNORE_SWIFT_LIBRARY_ATTRS = [
|
|||||||
"toolchains",
|
"toolchains",
|
||||||
"transitive_configs",
|
"transitive_configs",
|
||||||
"visibility",
|
"visibility",
|
||||||
|
"library_evolution",
|
||||||
|
"package_metadata",
|
||||||
]
|
]
|
||||||
|
|
||||||
_IGNORE_SWIFT_LIBRARY_EMPTY_ATTRS = [
|
_IGNORE_SWIFT_LIBRARY_EMPTY_ATTRS = [
|
||||||
@ -332,9 +337,9 @@ def _collect_spm_modules_impl(target, ctx):
|
|||||||
|
|
||||||
# Extract the path from the label
|
# Extract the path from the label
|
||||||
# Example: @//path/ModuleName:ModuleSubname -> path/ModuleName
|
# Example: @//path/ModuleName:ModuleSubname -> path/ModuleName
|
||||||
if not str(ctx.label).startswith("@//"):
|
if not str(ctx.label).startswith("@@//"):
|
||||||
fail("Invalid label: {}".format(ctx.label))
|
fail("Invalid label: {}".format(ctx.label))
|
||||||
module_path = str(ctx.label).split(":")[0].split("@//")[1]
|
module_path = str(ctx.label).split(":")[0].split("@@//")[1]
|
||||||
|
|
||||||
if module_type == "objc_library":
|
if module_type == "objc_library":
|
||||||
module_info = {
|
module_info = {
|
||||||
|
|||||||
@ -51,6 +51,11 @@ class EncryptionV2
|
|||||||
key = keyIv[0..31]
|
key = keyIv[0..31]
|
||||||
iv = keyIv[32..43]
|
iv = keyIv[32..43]
|
||||||
auth_data = keyIv[44..-1]
|
auth_data = keyIv[44..-1]
|
||||||
|
|
||||||
|
puts "key: #{key.inspect}"
|
||||||
|
puts "iv: #{iv.inspect}"
|
||||||
|
puts "auth_data: #{auth_data.inspect}"
|
||||||
|
|
||||||
cipher.key = key
|
cipher.key = key
|
||||||
cipher.iv = iv
|
cipher.iv = iv
|
||||||
cipher.auth_data = auth_data
|
cipher.auth_data = auth_data
|
||||||
|
|||||||
@ -1078,6 +1078,7 @@ public protocol SharedAccountContext: AnyObject {
|
|||||||
selectedMessages: Signal<Set<MessageId>?, NoError>,
|
selectedMessages: Signal<Set<MessageId>?, NoError>,
|
||||||
mode: ChatHistoryListMode
|
mode: ChatHistoryListMode
|
||||||
) -> ChatHistoryListNode
|
) -> ChatHistoryListNode
|
||||||
|
func subscribeChatListData(context: AccountContext, location: ChatListControllerLocation) -> Signal<EngineChatList, NoError>
|
||||||
func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: ((UIView?, CGPoint?) -> Void)?, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, accountPeer: Peer?, isCentered: Bool, isPreview: Bool, isStandalone: Bool) -> ListViewItem
|
func makeChatMessagePreviewItem(context: AccountContext, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder, forcedResourceStatus: FileMediaResourceStatus?, tapMessage: ((Message) -> Void)?, clickThroughMessage: ((UIView?, CGPoint?) -> Void)?, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, accountPeer: Peer?, isCentered: Bool, isPreview: Bool, isStandalone: Bool) -> ListViewItem
|
||||||
func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader
|
func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader
|
||||||
func makeChatMessageAvatarHeaderItem(context: AccountContext, timestamp: Int32, peer: Peer, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader
|
func makeChatMessageAvatarHeaderItem(context: AccountContext, timestamp: Int32, peer: Peer, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader
|
||||||
@ -1120,7 +1121,7 @@ public protocol SharedAccountContext: AnyObject {
|
|||||||
|
|
||||||
func navigateToChatController(_ params: NavigateToChatControllerParams)
|
func navigateToChatController(_ params: NavigateToChatControllerParams)
|
||||||
func navigateToForumChannel(context: AccountContext, peerId: EnginePeer.Id, navigationController: NavigationController)
|
func navigateToForumChannel(context: AccountContext, peerId: EnginePeer.Id, navigationController: NavigationController)
|
||||||
func navigateToForumThread(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64, messageId: EngineMessage.Id?, navigationController: NavigationController, activateInput: ChatControllerActivateInput?, scrollToEndIfExists: Bool, keepStack: NavigateToChatKeepStack) -> Signal<Never, NoError>
|
func navigateToForumThread(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64, messageId: EngineMessage.Id?, navigationController: NavigationController, activateInput: ChatControllerActivateInput?, scrollToEndIfExists: Bool, keepStack: NavigateToChatKeepStack, animated: Bool) -> Signal<Never, NoError>
|
||||||
func chatControllerForForumThread(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64) -> Signal<ChatController, NoError>
|
func chatControllerForForumThread(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64) -> Signal<ChatController, NoError>
|
||||||
func openStorageUsage(context: AccountContext)
|
func openStorageUsage(context: AccountContext)
|
||||||
func openLocationScreen(context: AccountContext, messageId: MessageId, navigationController: NavigationController)
|
func openLocationScreen(context: AccountContext, messageId: MessageId, navigationController: NavigationController)
|
||||||
|
|||||||
@ -949,9 +949,11 @@ public let ChatControllerCount = Atomic<Int32>(value: 0)
|
|||||||
|
|
||||||
public final class PeerInfoNavigationSourceTag {
|
public final class PeerInfoNavigationSourceTag {
|
||||||
public let peerId: EnginePeer.Id
|
public let peerId: EnginePeer.Id
|
||||||
|
public let threadId: Int64?
|
||||||
|
|
||||||
public init(peerId: EnginePeer.Id) {
|
public init(peerId: EnginePeer.Id, threadId: Int64?) {
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
|
self.threadId = threadId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1016,6 +1018,13 @@ public protocol ChatControllerCustomNavigationPanelNode: ASDisplayNode {
|
|||||||
func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, chatController: ChatController) -> LayoutResult
|
func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, chatController: ChatController) -> LayoutResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ChatControllerAnimateInnerChatSwitchDirection {
|
||||||
|
case up
|
||||||
|
case down
|
||||||
|
case left
|
||||||
|
case right
|
||||||
|
}
|
||||||
|
|
||||||
public protocol ChatController: ViewController {
|
public protocol ChatController: ViewController {
|
||||||
var chatLocation: ChatLocation { get }
|
var chatLocation: ChatLocation { get }
|
||||||
var canReadHistory: ValuePromise<Bool> { get }
|
var canReadHistory: ValuePromise<Bool> { get }
|
||||||
|
|||||||
@ -1267,7 +1267,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
}, openBoostToUnrestrict: {
|
}, openBoostToUnrestrict: {
|
||||||
}, updateVideoTrimRange: { _, _, _, _ in
|
}, updateVideoTrimRange: { _, _, _, _ in
|
||||||
}, updateHistoryFilter: { _ in
|
}, updateHistoryFilter: { _ in
|
||||||
}, updateChatLocationThread: { _ in
|
}, updateChatLocationThread: { _, _ in
|
||||||
}, toggleChatSidebarMode: {
|
}, toggleChatSidebarMode: {
|
||||||
}, updateDisplayHistoryFilterAsList: { _ in
|
}, updateDisplayHistoryFilterAsList: { _ in
|
||||||
}, requestLayout: { _ in
|
}, requestLayout: { _ in
|
||||||
|
|||||||
@ -175,7 +175,7 @@ public final class BrowserBookmarksScreen: ViewController {
|
|||||||
}, forceUpdateWarpContents: {
|
}, forceUpdateWarpContents: {
|
||||||
}, playShakeAnimation: {
|
}, playShakeAnimation: {
|
||||||
}, displayQuickShare: { _, _ ,_ in
|
}, displayQuickShare: { _, _ ,_ in
|
||||||
}, updateChatLocationThread: { _ in
|
}, updateChatLocationThread: { _, _ in
|
||||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil))
|
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -814,7 +814,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .forum(peerId):
|
case let .forum(peerId):
|
||||||
self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId)
|
self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId, threadId: nil)
|
||||||
self.navigationBar?.allowsCustomTransition = { [weak self] in
|
self.navigationBar?.allowsCustomTransition = { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return false
|
return false
|
||||||
@ -1345,7 +1345,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
navigationAnimationOptions = .removeOnMasterDetails
|
navigationAnimationOptions = .removeOnMasterDetails
|
||||||
}
|
}
|
||||||
if case let .channel(channel) = actualPeer, channel.isForumOrMonoForum, let threadId {
|
if case let .channel(channel) = actualPeer, channel.isForumOrMonoForum, let threadId {
|
||||||
let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: peer.id, threadId: threadId, messageId: messageId, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .never).startStandalone()
|
let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: peer.id, threadId: threadId, messageId: messageId, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .never, animated: true).startStandalone()
|
||||||
} else {
|
} else {
|
||||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeer), subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false), purposefulAction: {
|
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(actualPeer), subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false), purposefulAction: {
|
||||||
if deactivateOnAction {
|
if deactivateOnAction {
|
||||||
@ -1378,7 +1378,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
navigationAnimationOptions = .removeOnMasterDetails
|
navigationAnimationOptions = .removeOnMasterDetails
|
||||||
}
|
}
|
||||||
if case let .channel(channel) = peer, channel.isForumOrMonoForum, let threadId {
|
if case let .channel(channel) = peer, channel.isForumOrMonoForum, let threadId {
|
||||||
let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: peer.id, threadId: threadId, messageId: nil, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .never).startStandalone()
|
let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: peer.id, threadId: threadId, messageId: nil, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .never, animated: true).startStandalone()
|
||||||
} else {
|
} else {
|
||||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), purposefulAction: { [weak self] in
|
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), purposefulAction: { [weak self] in
|
||||||
self?.deactivateSearch(animated: false)
|
self?.deactivateSearch(animated: false)
|
||||||
@ -1462,7 +1462,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
|
|
||||||
let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: iconColor, iconFileId: fileId)
|
let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: iconColor, iconFileId: fileId)
|
||||||
|> deliverOnMainQueue).startStandalone(next: { topicId in
|
|> deliverOnMainQueue).startStandalone(next: { topicId in
|
||||||
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, messageId: nil, navigationController: navigationController, activateInput: .text, scrollToEndIfExists: false, keepStack: .never).startStandalone()
|
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, messageId: nil, navigationController: navigationController, activateInput: .text, scrollToEndIfExists: false, keepStack: .never, animated: true).startStandalone()
|
||||||
}, error: { _ in
|
}, error: { _ in
|
||||||
controller?.isInProgress = false
|
controller?.isInProgress = false
|
||||||
})
|
})
|
||||||
@ -3762,6 +3762,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var needsSeparatorForCreateTopic = true
|
||||||
if let sourceController = sourceController as? ChatController {
|
if let sourceController = sourceController as? ChatController {
|
||||||
items.append(.separator)
|
items.append(.separator)
|
||||||
items.append(.action(ContextMenuActionItem(text: strings.Conversation_Search, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: strings.Conversation_Search, icon: { theme in
|
||||||
@ -3771,8 +3772,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
|
|
||||||
sourceController?.beginMessageSearch("")
|
sourceController?.beginMessageSearch("")
|
||||||
})))
|
})))
|
||||||
} else if channel.hasPermission(.createTopics) {
|
needsSeparatorForCreateTopic = false
|
||||||
items.append(.separator)
|
}
|
||||||
|
if channel.hasPermission(.createTopics) {
|
||||||
|
if needsSeparatorForCreateTopic {
|
||||||
|
items.append(.separator)
|
||||||
|
}
|
||||||
|
|
||||||
items.append(.action(ContextMenuActionItem(text: strings.Chat_CreateTopic, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: strings.Chat_CreateTopic, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
|
||||||
@ -3788,7 +3793,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: iconColor, iconFileId: fileId)
|
let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: iconColor, iconFileId: fileId)
|
||||||
|> deliverOnMainQueue).startStandalone(next: { topicId in
|
|> deliverOnMainQueue).startStandalone(next: { topicId in
|
||||||
if let navigationController = (sourceController.navigationController as? NavigationController) {
|
if let navigationController = (sourceController.navigationController as? NavigationController) {
|
||||||
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, messageId: nil, navigationController: navigationController, activateInput: .text, scrollToEndIfExists: false, keepStack: .never).startStandalone()
|
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, messageId: nil, navigationController: navigationController, activateInput: .text, scrollToEndIfExists: false, keepStack: .never, animated: true).startStandalone()
|
||||||
}
|
}
|
||||||
}, error: { _ in
|
}, error: { _ in
|
||||||
controller?.isInProgress = false
|
controller?.isInProgress = false
|
||||||
|
|||||||
@ -1023,7 +1023,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
let titleTopicIconContent: EmojiStatusComponent.Content?
|
let titleTopicIconContent: EmojiStatusComponent.Content?
|
||||||
if threadId == 1 {
|
if threadId == 1 {
|
||||||
titleTopicIconContent = .image(image: PresentationResourcesChatList.generalTopicSmallIcon(theme))
|
titleTopicIconContent = .image(image: PresentationResourcesChatList.generalTopicSmallIcon(theme), tintColor: nil)
|
||||||
} else if let fileId = iconId, fileId != 0 {
|
} else if let fileId = iconId, fileId != 0 {
|
||||||
titleTopicIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 36.0, height: 36.0), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .count(0))
|
titleTopicIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 36.0, height: 36.0), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .count(0))
|
||||||
} else if let iconColor {
|
} else if let iconColor {
|
||||||
@ -3850,7 +3850,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
let avatarIconContent: EmojiStatusComponent.Content
|
let avatarIconContent: EmojiStatusComponent.Content
|
||||||
if threadInfo.id == 1 {
|
if threadInfo.id == 1 {
|
||||||
avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicIcon(item.presentationData.theme))
|
avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicIcon(item.presentationData.theme), tintColor: nil)
|
||||||
} else if let fileId = threadInfo.info.icon, fileId != 0 {
|
} else if let fileId = threadInfo.info.icon, fileId != 0 {
|
||||||
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(0))
|
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(0))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -6,12 +6,12 @@ import Display
|
|||||||
import TelegramUIPreferences
|
import TelegramUIPreferences
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
|
||||||
enum ChatListNodeLocation: Equatable {
|
public enum ChatListNodeLocation: Equatable {
|
||||||
case initial(count: Int, filter: ChatListFilter?)
|
case initial(count: Int, filter: ChatListFilter?)
|
||||||
case navigation(index: EngineChatList.Item.Index, filter: ChatListFilter?)
|
case navigation(index: EngineChatList.Item.Index, filter: ChatListFilter?)
|
||||||
case scroll(index: EngineChatList.Item.Index, sourceIndex: EngineChatList.Item.Index, scrollPosition: ListViewScrollPosition, animated: Bool, filter: ChatListFilter?)
|
case scroll(index: EngineChatList.Item.Index, sourceIndex: EngineChatList.Item.Index, scrollPosition: ListViewScrollPosition, animated: Bool, filter: ChatListFilter?)
|
||||||
|
|
||||||
var filter: ChatListFilter? {
|
public var filter: ChatListFilter? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .initial(_, filter):
|
case let .initial(_, filter):
|
||||||
return filter
|
return filter
|
||||||
@ -23,10 +23,16 @@ enum ChatListNodeLocation: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ChatListNodeViewUpdate {
|
public struct ChatListNodeViewUpdate {
|
||||||
let list: EngineChatList
|
public let list: EngineChatList
|
||||||
let type: ViewUpdateType
|
public let type: ViewUpdateType
|
||||||
let scrollPosition: ChatListNodeViewScrollPosition?
|
public let scrollPosition: ChatListNodeViewScrollPosition?
|
||||||
|
|
||||||
|
public init(list: EngineChatList, type: ViewUpdateType, scrollPosition: ChatListNodeViewScrollPosition?) {
|
||||||
|
self.list = list
|
||||||
|
self.type = type
|
||||||
|
self.scrollPosition = scrollPosition
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func chatListFilterPredicate(filter: ChatListFilterData, accountPeerId: EnginePeer.Id) -> ChatListFilterPredicate {
|
public func chatListFilterPredicate(filter: ChatListFilterData, accountPeerId: EnginePeer.Id) -> ChatListFilterPredicate {
|
||||||
@ -113,7 +119,7 @@ public func chatListFilterPredicate(filter: ChatListFilterData, accountPeerId: E
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func chatListViewForLocation(chatListLocation: ChatListControllerLocation, location: ChatListNodeLocation, account: Account, shouldLoadCanMessagePeer: Bool) -> Signal<ChatListNodeViewUpdate, NoError> {
|
public func chatListViewForLocation(chatListLocation: ChatListControllerLocation, location: ChatListNodeLocation, account: Account, shouldLoadCanMessagePeer: Bool) -> Signal<ChatListNodeViewUpdate, NoError> {
|
||||||
let accountPeerId = account.peerId
|
let accountPeerId = account.peerId
|
||||||
|
|
||||||
switch chatListLocation {
|
switch chatListLocation {
|
||||||
|
|||||||
@ -47,7 +47,7 @@ struct ChatListNodeViewTransition {
|
|||||||
let animateCrossfade: Bool
|
let animateCrossfade: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ChatListNodeViewScrollPosition {
|
public enum ChatListNodeViewScrollPosition {
|
||||||
case index(index: ChatListIndex, position: ListViewScrollPosition, directionHint: ListViewScrollToItemDirectionHint, animated: Bool)
|
case index(index: ChatListIndex, position: ListViewScrollPosition, directionHint: ListViewScrollToItemDirectionHint, animated: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -172,7 +172,7 @@ public final class ChatPanelInterfaceInteraction {
|
|||||||
public let openStarsPurchase: (Int64?) -> Void
|
public let openStarsPurchase: (Int64?) -> Void
|
||||||
public let openMessagePayment: () -> Void
|
public let openMessagePayment: () -> Void
|
||||||
public let updateHistoryFilter: ((ChatPresentationInterfaceState.HistoryFilter?) -> ChatPresentationInterfaceState.HistoryFilter?) -> Void
|
public let updateHistoryFilter: ((ChatPresentationInterfaceState.HistoryFilter?) -> ChatPresentationInterfaceState.HistoryFilter?) -> Void
|
||||||
public let updateChatLocationThread: (Int64?) -> Void
|
public let updateChatLocationThread: (Int64?, ChatControllerAnimateInnerChatSwitchDirection?) -> Void
|
||||||
public let toggleChatSidebarMode: () -> Void
|
public let toggleChatSidebarMode: () -> Void
|
||||||
public let updateDisplayHistoryFilterAsList: (Bool) -> Void
|
public let updateDisplayHistoryFilterAsList: (Bool) -> Void
|
||||||
public let openBoostToUnrestrict: () -> Void
|
public let openBoostToUnrestrict: () -> Void
|
||||||
@ -294,7 +294,7 @@ public final class ChatPanelInterfaceInteraction {
|
|||||||
openBoostToUnrestrict: @escaping () -> Void,
|
openBoostToUnrestrict: @escaping () -> Void,
|
||||||
updateVideoTrimRange: @escaping (Double, Double, Bool, Bool) -> Void,
|
updateVideoTrimRange: @escaping (Double, Double, Bool, Bool) -> Void,
|
||||||
updateHistoryFilter: @escaping ((ChatPresentationInterfaceState.HistoryFilter?) -> ChatPresentationInterfaceState.HistoryFilter?) -> Void,
|
updateHistoryFilter: @escaping ((ChatPresentationInterfaceState.HistoryFilter?) -> ChatPresentationInterfaceState.HistoryFilter?) -> Void,
|
||||||
updateChatLocationThread: @escaping (Int64?) -> Void,
|
updateChatLocationThread: @escaping (Int64?, ChatControllerAnimateInnerChatSwitchDirection?) -> Void,
|
||||||
toggleChatSidebarMode: @escaping () -> Void,
|
toggleChatSidebarMode: @escaping () -> Void,
|
||||||
updateDisplayHistoryFilterAsList: @escaping (Bool) -> Void,
|
updateDisplayHistoryFilterAsList: @escaping (Bool) -> Void,
|
||||||
requestLayout: @escaping (ContainedViewLayoutTransition) -> Void,
|
requestLayout: @escaping (ContainedViewLayoutTransition) -> Void,
|
||||||
@ -540,7 +540,7 @@ public final class ChatPanelInterfaceInteraction {
|
|||||||
}, openBoostToUnrestrict: {
|
}, openBoostToUnrestrict: {
|
||||||
}, updateVideoTrimRange: { _, _, _, _ in
|
}, updateVideoTrimRange: { _, _, _, _ in
|
||||||
}, updateHistoryFilter: { _ in
|
}, updateHistoryFilter: { _ in
|
||||||
}, updateChatLocationThread: { _ in
|
}, updateChatLocationThread: { _, _ in
|
||||||
}, toggleChatSidebarMode: {
|
}, toggleChatSidebarMode: {
|
||||||
}, updateDisplayHistoryFilterAsList: { _ in
|
}, updateDisplayHistoryFilterAsList: { _ in
|
||||||
}, requestLayout: { _ in
|
}, requestLayout: { _ in
|
||||||
|
|||||||
@ -2376,16 +2376,24 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel
|
|||||||
} else {
|
} else {
|
||||||
let updateItem = updateIndicesAndItems[0]
|
let updateItem = updateIndicesAndItems[0]
|
||||||
if let previousNode = previousNodes[updateItem.index] {
|
if let previousNode = previousNodes[updateItem.index] {
|
||||||
|
let threadId = pthread_self()
|
||||||
|
var tailRecurse = false
|
||||||
self.nodeForItem(synchronous: synchronous, synchronousLoads: synchronousLoads, item: updateItem.item, previousNode: previousNode, index: updateItem.index, previousItem: updateItem.index == 0 ? nil : self.items[updateItem.index - 1], nextItem: updateItem.index == (self.items.count - 1) ? nil : self.items[updateItem.index + 1], params: ListViewItemLayoutParams(width: state.visibleSize.width, leftInset: state.insets.left, rightInset: state.insets.right, availableHeight: state.visibleSize.height - state.insets.top - state.insets.bottom), updateAnimationIsAnimated: animated, updateAnimationIsCrossfade: crossfade, customAnimationTransition: customAnimationTransition, completion: { _, layout, apply in
|
self.nodeForItem(synchronous: synchronous, synchronousLoads: synchronousLoads, item: updateItem.item, previousNode: previousNode, index: updateItem.index, previousItem: updateItem.index == 0 ? nil : self.items[updateItem.index - 1], nextItem: updateItem.index == (self.items.count - 1) ? nil : self.items[updateItem.index + 1], params: ListViewItemLayoutParams(width: state.visibleSize.width, leftInset: state.insets.left, rightInset: state.insets.right, availableHeight: state.visibleSize.height - state.insets.top - state.insets.bottom), updateAnimationIsAnimated: animated, updateAnimationIsCrossfade: crossfade, customAnimationTransition: customAnimationTransition, completion: { _, layout, apply in
|
||||||
state.updateNodeAtItemIndex(updateItem.index, layout: layout, direction: updateItem.directionHint, isAnimated: animated, apply: apply, operations: &operations)
|
state.updateNodeAtItemIndex(updateItem.index, layout: layout, direction: updateItem.directionHint, isAnimated: animated, apply: apply, operations: &operations)
|
||||||
|
|
||||||
updateIndicesAndItems.remove(at: 0)
|
updateIndicesAndItems.remove(at: 0)
|
||||||
self.updateNodes(synchronous: synchronous, synchronousLoads: synchronousLoads, crossfade: crossfade, customAnimationTransition: customAnimationTransition, animated: animated, updateIndicesAndItems: updateIndicesAndItems, inputState: state, previousNodes: previousNodes, inputOperations: operations, completion: completion)
|
if pthread_equal(pthread_self(), threadId) != 0 && !tailRecurse {
|
||||||
|
tailRecurse = true
|
||||||
|
} else {
|
||||||
|
self.updateNodes(synchronous: synchronous, synchronousLoads: synchronousLoads, crossfade: crossfade, customAnimationTransition: customAnimationTransition, animated: animated, updateIndicesAndItems: updateIndicesAndItems, inputState: state, previousNodes: previousNodes, inputOperations: operations, completion: completion)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
break
|
if !tailRecurse {
|
||||||
|
tailRecurse = true
|
||||||
|
break
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
updateIndicesAndItems.remove(at: 0)
|
updateIndicesAndItems.remove(at: 0)
|
||||||
//self.updateNodes(synchronous: synchronous, animated: animated, updateIndicesAndItems: updateIndicesAndItems, inputState: state, previousNodes: previousNodes, inputOperations: operations, completion: completion)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -458,6 +458,56 @@ open class BlurredBackgroundView: UIView {
|
|||||||
public protocol NavigationBarHeaderView: UIView {
|
public protocol NavigationBarHeaderView: UIView {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class NavigationBackgroundCutoutView: UIView {
|
||||||
|
private let topLayer: SimpleLayer
|
||||||
|
private let rightLayer: SimpleLayer
|
||||||
|
|
||||||
|
weak var targetNode: ASDisplayNode?
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
self.topLayer = SimpleLayer()
|
||||||
|
self.topLayer.backgroundColor = UIColor.white.cgColor
|
||||||
|
|
||||||
|
self.rightLayer = SimpleLayer()
|
||||||
|
self.rightLayer.backgroundColor = UIColor.white.cgColor
|
||||||
|
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.layer.addSublayer(self.topLayer)
|
||||||
|
self.layer.addSublayer(self.rightLayer)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(size: CGSize, cutout: CGSize?, transition: ContainedViewLayoutTransition) {
|
||||||
|
let cutout = cutout ?? CGSize()
|
||||||
|
|
||||||
|
let topFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: cutout.height))
|
||||||
|
let rightFrame = CGRect(origin: CGPoint(x: cutout.width, y: 0.0), size: CGSize(width: max(0.0, size.width - cutout.width), height: size.height))
|
||||||
|
|
||||||
|
if self.topLayer.frame != topFrame || self.rightLayer.frame != rightFrame {
|
||||||
|
if cutout.width != 0.0 && cutout.height != 0.0 {
|
||||||
|
self.targetNode?.view.mask = self
|
||||||
|
//self.targetNode?.view.addSubview(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
transition.updateFrame(layer: self.topLayer, frame: topFrame)
|
||||||
|
transition.updateFrame(layer: self.rightLayer, frame: rightFrame, completion: { [weak self] completed in
|
||||||
|
guard let self, completed else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(cutout.width != 0.0 && cutout.height != 0.0) {
|
||||||
|
self.targetNode?.view.mask = nil
|
||||||
|
//self.removeFromSuperview()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
open class NavigationBar: ASDisplayNode {
|
open class NavigationBar: ASDisplayNode {
|
||||||
public static var defaultSecondaryContentHeight: CGFloat {
|
public static var defaultSecondaryContentHeight: CGFloat {
|
||||||
return 38.0
|
return 38.0
|
||||||
@ -489,7 +539,7 @@ open class NavigationBar: ASDisplayNode {
|
|||||||
|
|
||||||
var presentationData: NavigationBarPresentationData
|
var presentationData: NavigationBarPresentationData
|
||||||
|
|
||||||
private var validLayout: (size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool)?
|
private var validLayout: (size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool)?
|
||||||
private var requestedLayout: Bool = false
|
private var requestedLayout: Bool = false
|
||||||
var requestContainerLayout: (ContainedViewLayoutTransition) -> Void = { _ in }
|
var requestContainerLayout: (ContainedViewLayoutTransition) -> Void = { _ in }
|
||||||
|
|
||||||
@ -1013,6 +1063,8 @@ open class NavigationBar: ASDisplayNode {
|
|||||||
public let leftButtonNode: NavigationButtonNode
|
public let leftButtonNode: NavigationButtonNode
|
||||||
public let rightButtonNode: NavigationButtonNode
|
public let rightButtonNode: NavigationButtonNode
|
||||||
public let additionalContentNode: SparseNode
|
public let additionalContentNode: SparseNode
|
||||||
|
|
||||||
|
private let navigationBackgroundCutoutView: NavigationBackgroundCutoutView
|
||||||
|
|
||||||
public func reattachAdditionalContentNode() {
|
public func reattachAdditionalContentNode() {
|
||||||
if self.additionalContentNode.supernode !== self {
|
if self.additionalContentNode.supernode !== self {
|
||||||
@ -1139,6 +1191,9 @@ open class NavigationBar: ASDisplayNode {
|
|||||||
|
|
||||||
self.secondaryContentHeight = NavigationBar.defaultSecondaryContentHeight
|
self.secondaryContentHeight = NavigationBar.defaultSecondaryContentHeight
|
||||||
|
|
||||||
|
self.navigationBackgroundCutoutView = NavigationBackgroundCutoutView(frame: CGRect())
|
||||||
|
self.navigationBackgroundCutoutView.targetNode = self.backgroundNode
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.addSubnode(self.backgroundNode)
|
self.addSubnode(self.backgroundNode)
|
||||||
@ -1246,22 +1301,25 @@ open class NavigationBar: ASDisplayNode {
|
|||||||
|
|
||||||
if let validLayout = self.validLayout, self.requestedLayout {
|
if let validLayout = self.validLayout, self.requestedLayout {
|
||||||
self.requestedLayout = false
|
self.requestedLayout = false
|
||||||
self.updateLayout(size: validLayout.size, defaultHeight: validLayout.defaultHeight, additionalTopHeight: validLayout.additionalTopHeight, additionalContentHeight: validLayout.additionalContentHeight, additionalBackgroundHeight: validLayout.additionalBackgroundHeight, leftInset: validLayout.leftInset, rightInset: validLayout.rightInset, appearsHidden: validLayout.appearsHidden, isLandscape: validLayout.isLandscape, transition: .immediate)
|
self.updateLayout(size: validLayout.size, defaultHeight: validLayout.defaultHeight, additionalTopHeight: validLayout.additionalTopHeight, additionalContentHeight: validLayout.additionalContentHeight, additionalBackgroundHeight: validLayout.additionalBackgroundHeight, additionalCutout: validLayout.additionalCutout, leftInset: validLayout.leftInset, rightInset: validLayout.rightInset, appearsHidden: validLayout.appearsHidden, isLandscape: validLayout.isLandscape, transition: .immediate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLayout(size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool, transition: ContainedViewLayoutTransition) {
|
func updateLayout(size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool, transition: ContainedViewLayoutTransition) {
|
||||||
if self.layoutSuspended {
|
if self.layoutSuspended {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.validLayout = (size, defaultHeight, additionalTopHeight, additionalContentHeight, additionalBackgroundHeight, leftInset, rightInset, appearsHidden, isLandscape)
|
self.validLayout = (size, defaultHeight, additionalTopHeight, additionalContentHeight, additionalBackgroundHeight, additionalCutout, leftInset, rightInset, appearsHidden, isLandscape)
|
||||||
|
|
||||||
let backgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight))
|
let backgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight))
|
||||||
if self.backgroundNode.frame != backgroundFrame {
|
if self.backgroundNode.frame != backgroundFrame {
|
||||||
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
|
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
|
||||||
self.backgroundNode.update(size: backgroundFrame.size, transition: transition)
|
self.backgroundNode.update(size: backgroundFrame.size, transition: transition)
|
||||||
|
|
||||||
|
transition.updateFrame(view: self.navigationBackgroundCutoutView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
||||||
}
|
}
|
||||||
|
self.navigationBackgroundCutoutView.update(size: backgroundFrame.size, cutout: additionalCutout, transition: transition)
|
||||||
|
|
||||||
let apparentAdditionalHeight: CGFloat = self.secondaryContentNode != nil ? (self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction) : 0.0
|
let apparentAdditionalHeight: CGFloat = self.secondaryContentNode != nil ? (self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction) : 0.0
|
||||||
|
|
||||||
@ -1293,7 +1351,7 @@ open class NavigationBar: ASDisplayNode {
|
|||||||
contentNode.updateLayout(size: contentNodeFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
|
contentNode.updateLayout(size: contentNodeFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
transition.updateFrame(node: self.stripeNode, frame: CGRect(x: 0.0, y: size.height + additionalBackgroundHeight, width: size.width, height: UIScreenPixel))
|
transition.updateFrame(node: self.stripeNode, frame: CGRect(x: (additionalCutout?.width ?? 0.0), y: size.height + additionalBackgroundHeight, width: size.width - (additionalCutout?.width ?? 0.0), height: UIScreenPixel))
|
||||||
|
|
||||||
let nominalHeight: CGFloat = defaultHeight
|
let nominalHeight: CGFloat = defaultHeight
|
||||||
let contentVerticalOrigin = additionalTopHeight
|
let contentVerticalOrigin = additionalTopHeight
|
||||||
|
|||||||
@ -424,10 +424,10 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
open func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: 0.0, transition: transition)
|
self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: 0.0, additionalCutout: nil, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func applyNavigationBarLayout(_ layout: ContainerViewLayout, navigationLayout: NavigationLayout, additionalBackgroundHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
public func applyNavigationBarLayout(_ layout: ContainerViewLayout, navigationLayout: NavigationLayout, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, transition: ContainedViewLayoutTransition) {
|
||||||
let statusBarHeight: CGFloat = layout.statusBarHeight ?? 0.0
|
let statusBarHeight: CGFloat = layout.statusBarHeight ?? 0.0
|
||||||
|
|
||||||
var navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: navigationLayout.navigationFrame.maxY))
|
var navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: navigationLayout.navigationFrame.maxY))
|
||||||
@ -451,7 +451,7 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject {
|
|||||||
navigationBarFrame.size.height += navigationBar.secondaryContentHeight
|
navigationBarFrame.size.height += navigationBar.secondaryContentHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
navigationBar.updateLayout(size: navigationBarFrame.size, defaultHeight: navigationLayout.defaultContentHeight, additionalTopHeight: statusBarHeight, additionalContentHeight: self.additionalNavigationBarHeight, additionalBackgroundHeight: additionalBackgroundHeight, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, appearsHidden: !self.displayNavigationBar, isLandscape: isLandscape, transition: transition)
|
navigationBar.updateLayout(size: navigationBarFrame.size, defaultHeight: navigationLayout.defaultContentHeight, additionalTopHeight: statusBarHeight, additionalContentHeight: self.additionalNavigationBarHeight, additionalBackgroundHeight: additionalBackgroundHeight, additionalCutout: additionalCutout, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, appearsHidden: !self.displayNavigationBar, isLandscape: isLandscape, transition: transition)
|
||||||
if !transition.isAnimated {
|
if !transition.isAnimated {
|
||||||
navigationBar.layer.removeAnimation(forKey: "bounds")
|
navigationBar.layer.removeAnimation(forKey: "bounds")
|
||||||
navigationBar.layer.removeAnimation(forKey: "position")
|
navigationBar.layer.removeAnimation(forKey: "position")
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
#import "RuntimeUtils.h"
|
#import "RuntimeUtils.h"
|
||||||
|
#include <Foundation/Foundation.h>
|
||||||
|
|
||||||
#import <objc/runtime.h>
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
|
|||||||
@ -977,7 +977,7 @@ public class ReplaceBoostScreen: ViewController {
|
|||||||
|
|
||||||
layout.statusBarHeight = nil
|
layout.statusBarHeight = nil
|
||||||
|
|
||||||
self.applyNavigationBarLayout(layout, navigationLayout: navigationLayout, additionalBackgroundHeight: 0.0, transition: transition)
|
self.applyNavigationBarLayout(layout, navigationLayout: navigationLayout, additionalBackgroundHeight: 0.0, additionalCutout: nil, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
|
|||||||
@ -394,13 +394,14 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
|
|||||||
private var suspendNavigationBarLayout: Bool = false
|
private var suspendNavigationBarLayout: Bool = false
|
||||||
private var suspendedNavigationBarLayout: ContainerViewLayout?
|
private var suspendedNavigationBarLayout: ContainerViewLayout?
|
||||||
private var additionalNavigationBarBackgroundHeight: CGFloat = 0.0
|
private var additionalNavigationBarBackgroundHeight: CGFloat = 0.0
|
||||||
|
private var additionalNavigationBarCutout: CGSize?
|
||||||
|
|
||||||
override open func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
override open func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
if self.suspendNavigationBarLayout {
|
if self.suspendNavigationBarLayout {
|
||||||
self.suspendedNavigationBarLayout = layout
|
self.suspendedNavigationBarLayout = layout
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: transition)
|
self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, additionalCutout: self.additionalNavigationBarCutout, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
@ -969,7 +970,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
|
|||||||
self.suspendNavigationBarLayout = false
|
self.suspendNavigationBarLayout = false
|
||||||
if let suspendedNavigationBarLayout = self.suspendedNavigationBarLayout {
|
if let suspendedNavigationBarLayout = self.suspendedNavigationBarLayout {
|
||||||
self.suspendedNavigationBarLayout = suspendedNavigationBarLayout
|
self.suspendedNavigationBarLayout = suspendedNavigationBarLayout
|
||||||
self.applyNavigationBarLayout(suspendedNavigationBarLayout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: transition)
|
self.applyNavigationBarLayout(suspendedNavigationBarLayout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, additionalCutout: self.additionalNavigationBarCutout, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.accessoryPanelContainerHeight = additionalHeight
|
self.accessoryPanelContainerHeight = additionalHeight
|
||||||
|
|||||||
@ -71,6 +71,7 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
|||||||
case empty
|
case empty
|
||||||
case tag(MemoryBuffer)
|
case tag(MemoryBuffer)
|
||||||
case search(query: String, includeSavedPeers: Bool)
|
case search(query: String, includeSavedPeers: Bool)
|
||||||
|
case monoforumChats(query: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
public let context: AccountContext
|
public let context: AccountContext
|
||||||
@ -85,6 +86,7 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
|||||||
public let loadTagMessages: (MemoryBuffer, MessageIndex?) -> Signal<MessageHistoryView, NoError>?
|
public let loadTagMessages: (MemoryBuffer, MessageIndex?) -> Signal<MessageHistoryView, NoError>?
|
||||||
public let getSearchResult: () -> Signal<SearchMessagesResult?, NoError>?
|
public let getSearchResult: () -> Signal<SearchMessagesResult?, NoError>?
|
||||||
public let getSavedPeers: (String) -> Signal<[(EnginePeer, MessageIndex?)], NoError>?
|
public let getSavedPeers: (String) -> Signal<[(EnginePeer, MessageIndex?)], NoError>?
|
||||||
|
public let getChats: (String) -> Signal<EngineChatList?, NoError>?
|
||||||
public let loadMoreSearchResults: () -> Void
|
public let loadMoreSearchResults: () -> Void
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
@ -100,6 +102,7 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
|||||||
loadTagMessages: @escaping (MemoryBuffer, MessageIndex?) -> Signal<MessageHistoryView, NoError>?,
|
loadTagMessages: @escaping (MemoryBuffer, MessageIndex?) -> Signal<MessageHistoryView, NoError>?,
|
||||||
getSearchResult: @escaping () -> Signal<SearchMessagesResult?, NoError>?,
|
getSearchResult: @escaping () -> Signal<SearchMessagesResult?, NoError>?,
|
||||||
getSavedPeers: @escaping (String) -> Signal<[(EnginePeer, MessageIndex?)], NoError>?,
|
getSavedPeers: @escaping (String) -> Signal<[(EnginePeer, MessageIndex?)], NoError>?,
|
||||||
|
getChats: @escaping (String) -> Signal<EngineChatList?, NoError>?,
|
||||||
loadMoreSearchResults: @escaping () -> Void
|
loadMoreSearchResults: @escaping () -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -114,6 +117,7 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
|||||||
self.loadTagMessages = loadTagMessages
|
self.loadTagMessages = loadTagMessages
|
||||||
self.getSearchResult = getSearchResult
|
self.getSearchResult = getSearchResult
|
||||||
self.getSavedPeers = getSavedPeers
|
self.getSavedPeers = getSavedPeers
|
||||||
|
self.getChats = getChats
|
||||||
self.loadMoreSearchResults = loadMoreSearchResults
|
self.loadMoreSearchResults = loadMoreSearchResults
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,10 +150,12 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
|||||||
enum Id: Hashable {
|
enum Id: Hashable {
|
||||||
case peer(EnginePeer.Id)
|
case peer(EnginePeer.Id)
|
||||||
case message(EngineMessage.Id)
|
case message(EngineMessage.Id)
|
||||||
|
case chat(EngineChatList.Item.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
case peer(EnginePeer)
|
case peer(EnginePeer)
|
||||||
case message(EngineMessage)
|
case message(EngineMessage)
|
||||||
|
case chat(EngineChatList.Item)
|
||||||
|
|
||||||
var id: Id {
|
var id: Id {
|
||||||
switch self {
|
switch self {
|
||||||
@ -157,6 +163,8 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
|||||||
return .peer(peer.id)
|
return .peer(peer.id)
|
||||||
case let .message(message):
|
case let .message(message):
|
||||||
return .message(message.id)
|
return .message(message.id)
|
||||||
|
case let .chat(chat):
|
||||||
|
return .chat(chat.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,6 +182,12 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
case let .chat(chat):
|
||||||
|
if case .chat(chat) = rhs {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,14 +202,27 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
|||||||
return lhsPeer.id < rhsPeer.id
|
return lhsPeer.id < rhsPeer.id
|
||||||
case .message:
|
case .message:
|
||||||
return true
|
return true
|
||||||
|
case .chat:
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
case let .message(lhsMessage):
|
case let .message(lhsMessage):
|
||||||
switch rhs {
|
switch rhs {
|
||||||
case .peer:
|
case .peer:
|
||||||
return false
|
return false
|
||||||
|
case .chat:
|
||||||
|
return false
|
||||||
case let .message(rhsMessage):
|
case let .message(rhsMessage):
|
||||||
return lhsMessage.index > rhsMessage.index
|
return lhsMessage.index > rhsMessage.index
|
||||||
}
|
}
|
||||||
|
case let .chat(lhsChat):
|
||||||
|
switch rhs {
|
||||||
|
case let .chat(rhsChat):
|
||||||
|
return lhsChat.index > rhsChat.index
|
||||||
|
case .peer:
|
||||||
|
return false
|
||||||
|
case .message:
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -418,6 +445,8 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
|||||||
}
|
}
|
||||||
case .search:
|
case .search:
|
||||||
break
|
break
|
||||||
|
case .monoforumChats:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let (currentIndex, disposable) = self.searchContents {
|
} else if let (currentIndex, disposable) = self.searchContents {
|
||||||
@ -431,6 +460,8 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
|||||||
self.searchContents = (loadAroundIndex, disposable)
|
self.searchContents = (loadAroundIndex, disposable)
|
||||||
|
|
||||||
component.loadMoreSearchResults()
|
component.loadMoreSearchResults()
|
||||||
|
case .monoforumChats:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -581,6 +612,127 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case let .monoforumChats(query):
|
||||||
|
let _ = query
|
||||||
|
|
||||||
|
if previousComponent?.contents != component.contents {
|
||||||
|
self.tagContents?.disposable?.dispose()
|
||||||
|
self.tagContents = nil
|
||||||
|
|
||||||
|
self.searchContents?.disposable?.dispose()
|
||||||
|
self.searchContents = nil
|
||||||
|
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
self.searchContents = (nil, disposable)
|
||||||
|
|
||||||
|
let savedPeers: Signal<EngineChatList?, NoError>
|
||||||
|
if let savedPeersSignal = component.getChats(query) {
|
||||||
|
savedPeers = savedPeersSignal
|
||||||
|
} else {
|
||||||
|
savedPeers = .single(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
disposable.set((savedPeers
|
||||||
|
|> deliverOnMainQueue).startStrict(next: { [weak self] chatList in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let messages: [EngineMessage] = [] /*result?.messages.map { entry in
|
||||||
|
return EngineMessage(entry)
|
||||||
|
} ?? []*/
|
||||||
|
|
||||||
|
var entries: [Entry] = []
|
||||||
|
if let chatList {
|
||||||
|
for item in chatList.items {
|
||||||
|
entries.append(.chat(item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for message in messages {
|
||||||
|
entries.append(.message(message))
|
||||||
|
}
|
||||||
|
entries.sort()
|
||||||
|
|
||||||
|
let contentsId = self.nextContentsId
|
||||||
|
self.nextContentsId += 1
|
||||||
|
self.contentsState = ContentsState(
|
||||||
|
id: contentsId,
|
||||||
|
contentId: .search(query),
|
||||||
|
entries: entries,
|
||||||
|
messages: messages,
|
||||||
|
hasEarlier: false, //!(result?.completed ?? true),
|
||||||
|
hasLater: false
|
||||||
|
)
|
||||||
|
if !self.isUpdating {
|
||||||
|
self.state?.updated(transition: .immediate)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.didSetReady {
|
||||||
|
self.didSetReady = true
|
||||||
|
self.isReadyPromise.set(.single(true))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
/*if !query.isEmpty, let savedPeersSignal = component.getSavedPeers(query) {
|
||||||
|
savedPeers = savedPeersSignal
|
||||||
|
} else {
|
||||||
|
savedPeers = .single([])
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*if let historySignal = component.getSearchResult() {
|
||||||
|
disposable.set((savedPeers
|
||||||
|
|> mapToSignal { savedPeers -> Signal<([(EnginePeer, MessageIndex?)], SearchMessagesResult?), NoError> in
|
||||||
|
if savedPeers.isEmpty {
|
||||||
|
return historySignal
|
||||||
|
|> map { result in
|
||||||
|
return ([], result)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return (.single(nil) |> then(historySignal))
|
||||||
|
|> map { result in
|
||||||
|
return (savedPeers, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> deliverOnMainQueue).startStrict(next: { [weak self] savedPeers, result in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let messages: [EngineMessage] = result?.messages.map { entry in
|
||||||
|
return EngineMessage(entry)
|
||||||
|
} ?? []
|
||||||
|
|
||||||
|
var entries: [Entry] = []
|
||||||
|
for (peer, _) in savedPeers {
|
||||||
|
entries.append(.peer(peer))
|
||||||
|
}
|
||||||
|
for message in messages {
|
||||||
|
entries.append(.message(message))
|
||||||
|
}
|
||||||
|
entries.sort()
|
||||||
|
|
||||||
|
let contentsId = self.nextContentsId
|
||||||
|
self.nextContentsId += 1
|
||||||
|
self.contentsState = ContentsState(
|
||||||
|
id: contentsId,
|
||||||
|
contentId: .search(query),
|
||||||
|
entries: entries,
|
||||||
|
messages: messages,
|
||||||
|
hasEarlier: !(result?.completed ?? true),
|
||||||
|
hasLater: false
|
||||||
|
)
|
||||||
|
if !self.isUpdating {
|
||||||
|
self.state?.updated(transition: .immediate)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.didSetReady {
|
||||||
|
self.didSetReady = true
|
||||||
|
self.isReadyPromise.set(.single(true))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}*/
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let contentsState = self.contentsState, self.contentsState != self.appliedContentsState {
|
if let contentsState = self.contentsState, self.contentsState != self.appliedContentsState {
|
||||||
@ -607,13 +759,17 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
|||||||
},
|
},
|
||||||
additionalCategorySelected: { _ in
|
additionalCategorySelected: { _ in
|
||||||
},
|
},
|
||||||
messageSelected: { [weak self] _, _, message, _ in
|
messageSelected: { [weak self] peer, _, message, _ in
|
||||||
guard let self else {
|
guard let self, let component = self.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.listNode.clearHighlightAnimated(true)
|
self.listNode.clearHighlightAnimated(true)
|
||||||
|
|
||||||
self.component?.messageSelected(message)
|
if case .monoforumChats = component.contents {
|
||||||
|
component.peerSelected(peer)
|
||||||
|
} else {
|
||||||
|
component.messageSelected(message)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
groupSelected: { _ in
|
groupSelected: { _ in
|
||||||
},
|
},
|
||||||
@ -845,6 +1001,45 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
|||||||
hiddenOffset: false,
|
hiddenOffset: false,
|
||||||
interaction: chatListNodeInteraction
|
interaction: chatListNodeInteraction
|
||||||
)
|
)
|
||||||
|
case let .chat(item):
|
||||||
|
return ChatListItem(
|
||||||
|
presentationData: chatListPresentationData,
|
||||||
|
context: component.context,
|
||||||
|
chatListLocation: component.peerId.flatMap { peerId in .savedMessagesChats(peerId: peerId) } ?? .chatList(groupId: .root),
|
||||||
|
filterData: nil,
|
||||||
|
index: item.index,
|
||||||
|
content: .peer(ChatListItemContent.PeerData(
|
||||||
|
messages: item.messages,
|
||||||
|
peer: item.renderedPeer,
|
||||||
|
threadInfo: nil,
|
||||||
|
combinedReadState: nil,
|
||||||
|
isRemovedFromTotalUnreadCount: false,
|
||||||
|
presence: nil,
|
||||||
|
hasUnseenMentions: false,
|
||||||
|
hasUnseenReactions: false,
|
||||||
|
draftState: nil,
|
||||||
|
mediaDraftContentType: nil,
|
||||||
|
inputActivities: nil,
|
||||||
|
promoInfo: nil,
|
||||||
|
ignoreUnreadBadge: false,
|
||||||
|
displayAsMessage: component.peerId != component.context.account.peerId && !component.showEmptyResults,
|
||||||
|
hasFailedMessages: false,
|
||||||
|
forumTopicData: nil,
|
||||||
|
topForumTopicItems: [],
|
||||||
|
autoremoveTimeout: nil,
|
||||||
|
storyState: nil,
|
||||||
|
requiresPremiumForMessaging: false,
|
||||||
|
displayAsTopicList: false,
|
||||||
|
tags: []
|
||||||
|
)),
|
||||||
|
editing: false,
|
||||||
|
hasActiveRevealControls: false,
|
||||||
|
selected: false,
|
||||||
|
header: nil,
|
||||||
|
enableContextActions: false,
|
||||||
|
hiddenOffset: false,
|
||||||
|
interaction: chatListNodeInteraction
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -816,7 +816,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
let makeForwardInfoLayout = ChatMessageForwardInfoNode.asyncLayout(self.forwardInfoNode)
|
let makeForwardInfoLayout = ChatMessageForwardInfoNode.asyncLayout(self.forwardInfoNode)
|
||||||
|
|
||||||
let viaBotLayout = TextNode.asyncLayout(self.viaBotNode)
|
let viaBotLayout = TextNode.asyncLayout(self.viaBotNode)
|
||||||
let makeThreadInfoLayout = ChatMessageThreadInfoNode.asyncLayout(self.threadInfoNode)
|
//let makeThreadInfoLayout = ChatMessageThreadInfoNode.asyncLayout(self.threadInfoNode)
|
||||||
let makeReplyInfoLayout = ChatMessageReplyInfoNode.asyncLayout(self.replyInfoNode)
|
let makeReplyInfoLayout = ChatMessageReplyInfoNode.asyncLayout(self.replyInfoNode)
|
||||||
let currentShareButtonNode = self.shareButtonNode
|
let currentShareButtonNode = self.shareButtonNode
|
||||||
let currentForwardInfo = self.appliedForwardInfo
|
let currentForwardInfo = self.appliedForwardInfo
|
||||||
@ -1107,7 +1107,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
let (dateAndStatusSize, dateAndStatusApply) = statusSuggestedWidthAndContinue.1(statusSuggestedWidthAndContinue.0)
|
let (dateAndStatusSize, dateAndStatusApply) = statusSuggestedWidthAndContinue.1(statusSuggestedWidthAndContinue.0)
|
||||||
|
|
||||||
var viaBotApply: (TextNodeLayout, () -> TextNode)?
|
var viaBotApply: (TextNodeLayout, () -> TextNode)?
|
||||||
var threadInfoApply: (CGSize, (Bool) -> ChatMessageThreadInfoNode)?
|
let threadInfoApply: (CGSize, (Bool) -> ChatMessageThreadInfoNode)? = nil
|
||||||
var replyInfoApply: (CGSize, (CGSize, Bool, ListViewItemUpdateAnimation) -> ChatMessageReplyInfoNode)?
|
var replyInfoApply: (CGSize, (CGSize, Bool, ListViewItemUpdateAnimation) -> ChatMessageReplyInfoNode)?
|
||||||
var replyMarkup: ReplyMarkupMessageAttribute?
|
var replyMarkup: ReplyMarkupMessageAttribute?
|
||||||
|
|
||||||
@ -1173,7 +1173,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
hasReply = false
|
hasReply = false
|
||||||
}
|
}
|
||||||
|
|
||||||
threadInfoApply = makeThreadInfoLayout(ChatMessageThreadInfoNode.Arguments(
|
/*threadInfoApply = makeThreadInfoLayout(ChatMessageThreadInfoNode.Arguments(
|
||||||
presentationData: item.presentationData,
|
presentationData: item.presentationData,
|
||||||
strings: item.presentationData.strings,
|
strings: item.presentationData.strings,
|
||||||
context: item.context,
|
context: item.context,
|
||||||
@ -1185,7 +1185,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
constrainedSize: CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude),
|
constrainedSize: CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude),
|
||||||
animationCache: item.controllerInteraction.presentationContext.animationCache,
|
animationCache: item.controllerInteraction.presentationContext.animationCache,
|
||||||
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
|
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
|
||||||
))
|
))*/
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasReply, (replyMessage != nil || replyForward != nil || replyStory != nil) {
|
if hasReply, (replyMessage != nil || replyForward != nil || replyStory != nil) {
|
||||||
|
|||||||
@ -2413,8 +2413,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
var boostNodeSizeApply: (CGSize, () -> TextNode?) = (CGSize(), { nil })
|
var boostNodeSizeApply: (CGSize, () -> TextNode?) = (CGSize(), { nil })
|
||||||
var viaWidth: CGFloat = 0.0
|
var viaWidth: CGFloat = 0.0
|
||||||
|
|
||||||
var threadInfoOriginY: CGFloat = 0.0
|
let threadInfoOriginY: CGFloat = 0.0
|
||||||
var threadInfoSizeApply: (CGSize, (Bool) -> ChatMessageThreadInfoNode?) = (CGSize(), { _ in nil })
|
let threadInfoSizeApply: (CGSize, (Bool) -> ChatMessageThreadInfoNode?) = (CGSize(), { _ in nil })
|
||||||
|
|
||||||
var replyInfoOriginY: CGFloat = 0.0
|
var replyInfoOriginY: CGFloat = 0.0
|
||||||
var replyInfoSizeApply: (CGSize, (CGSize, Bool, ListViewItemUpdateAnimation) -> ChatMessageReplyInfoNode?) = (CGSize(), { _, _, _ in nil })
|
var replyInfoSizeApply: (CGSize, (CGSize, Bool, ListViewItemUpdateAnimation) -> ChatMessageReplyInfoNode?) = (CGSize(), { _, _, _ in nil })
|
||||||
@ -2625,8 +2625,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasThreadInfo = false
|
let hasThreadInfo = !"".isEmpty
|
||||||
if case let .peer(peerId) = item.chatLocation, (peerId == replyMessage?.id.peerId || item.message.threadId == 1 || item.associatedData.isRecentActions), let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.isForum, item.message.associatedThreadInfo != nil {
|
/*if case let .peer(peerId) = item.chatLocation, (peerId == replyMessage?.id.peerId || item.message.threadId == 1 || item.associatedData.isRecentActions), let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.isForum, item.message.associatedThreadInfo != nil {
|
||||||
hasThreadInfo = true
|
hasThreadInfo = true
|
||||||
} else if case let .customChatContents(contents) = item.associatedData.subject, case .hashTagSearch = contents.kind {
|
} else if case let .customChatContents(contents) = item.associatedData.subject, case .hashTagSearch = contents.kind {
|
||||||
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
|
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
|
||||||
@ -2634,7 +2634,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
} else {
|
} else {
|
||||||
hasThreadInfo = true
|
hasThreadInfo = true
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
var hasReply = replyMessage != nil || replyForward != nil || replyStory != nil
|
var hasReply = replyMessage != nil || replyForward != nil || replyStory != nil
|
||||||
if !isInstantVideo, hasThreadInfo {
|
if !isInstantVideo, hasThreadInfo {
|
||||||
@ -2642,7 +2642,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
hasReply = false
|
hasReply = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if !mergedTop.merged {
|
/*if !mergedTop.merged {
|
||||||
if headerSize.height.isZero {
|
if headerSize.height.isZero {
|
||||||
headerSize.height += 14.0
|
headerSize.height += 14.0
|
||||||
} else {
|
} else {
|
||||||
@ -2666,7 +2666,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
threadInfoOriginY = headerSize.height
|
threadInfoOriginY = headerSize.height
|
||||||
headerSize.width = max(headerSize.width, threadInfoSizeApply.0.width + bubbleWidthInsets)
|
headerSize.width = max(headerSize.width, threadInfoSizeApply.0.width + bubbleWidthInsets)
|
||||||
headerSize.height += threadInfoSizeApply.0.height + 5.0
|
headerSize.height += threadInfoSizeApply.0.height + 5.0
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isInstantVideo, hasReply, (replyMessage != nil || replyForward != nil || replyStory != nil) {
|
if !isInstantVideo, hasReply, (replyMessage != nil || replyForward != nil || replyStory != nil) {
|
||||||
@ -3549,7 +3549,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
guard let strongSelf, let item = strongSelf.item else {
|
guard let strongSelf, let item = strongSelf.item else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item.controllerInteraction.updateChatLocationThread(item.content.firstMessage.threadId)
|
item.controllerInteraction.updateChatLocationThread(item.content.firstMessage.threadId, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6000,7 +6000,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let channel = item.message.peers[item.message.id.peerId], channel.isMonoForum, case .peer = item.chatLocation {
|
} else if let channel = item.message.peers[item.message.id.peerId], channel.isMonoForum, case .peer = item.chatLocation {
|
||||||
item.controllerInteraction.updateChatLocationThread(item.message.threadId)
|
item.controllerInteraction.updateChatLocationThread(item.message.threadId, nil)
|
||||||
} else {
|
} else {
|
||||||
if !self.disablesComments {
|
if !self.disablesComments {
|
||||||
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
|
if let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
|
||||||
|
|||||||
@ -35,6 +35,8 @@ swift_library(
|
|||||||
"//submodules/Components/HierarchyTrackingLayer",
|
"//submodules/Components/HierarchyTrackingLayer",
|
||||||
"//submodules/WallpaperBackgroundNode",
|
"//submodules/WallpaperBackgroundNode",
|
||||||
"//submodules/AvatarVideoNode",
|
"//submodules/AvatarVideoNode",
|
||||||
|
"//submodules/ComponentFlow",
|
||||||
|
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -17,6 +17,8 @@ import ChatControllerInteraction
|
|||||||
import AvatarVideoNode
|
import AvatarVideoNode
|
||||||
import ChatMessageItem
|
import ChatMessageItem
|
||||||
import AvatarNode
|
import AvatarNode
|
||||||
|
import ComponentFlow
|
||||||
|
import EmojiStatusComponent
|
||||||
|
|
||||||
private let timezoneOffset: Int32 = {
|
private let timezoneOffset: Int32 = {
|
||||||
let nowTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
let nowTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||||
@ -39,18 +41,23 @@ public final class ChatMessageDateHeader: ListViewItemHeader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class PeerData {
|
public final class HeaderData {
|
||||||
public let peer: EnginePeer
|
public enum Contents {
|
||||||
|
case peer(EnginePeer)
|
||||||
|
case thread(id: Int64, info: Message.AssociatedThreadInfo)
|
||||||
|
}
|
||||||
|
|
||||||
public init(peer: EnginePeer) {
|
public let contents: Contents
|
||||||
self.peer = peer
|
|
||||||
|
public init(contents: Contents) {
|
||||||
|
self.contents = contents
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let timestamp: Int32
|
private let timestamp: Int32
|
||||||
private let roundedTimestamp: Int32
|
private let roundedTimestamp: Int32
|
||||||
private let scheduled: Bool
|
private let scheduled: Bool
|
||||||
public let displayPeer: PeerData?
|
public let displayHeader: HeaderData?
|
||||||
|
|
||||||
public let id: ListViewItemNode.HeaderId
|
public let id: ListViewItemNode.HeaderId
|
||||||
public let stackingId: ListViewItemNode.HeaderId?
|
public let stackingId: ListViewItemNode.HeaderId?
|
||||||
@ -60,16 +67,16 @@ public final class ChatMessageDateHeader: ListViewItemHeader {
|
|||||||
public let context: AccountContext
|
public let context: AccountContext
|
||||||
public let action: ((Int32, Bool) -> Void)?
|
public let action: ((Int32, Bool) -> Void)?
|
||||||
|
|
||||||
public init(timestamp: Int32, separableThreadId: Int64?, scheduled: Bool, displayPeer: PeerData?, presentationData: ChatPresentationData, controllerInteraction: ChatControllerInteraction?, context: AccountContext, action: ((Int32, Bool) -> Void)? = nil) {
|
public init(timestamp: Int32, separableThreadId: Int64?, scheduled: Bool, displayHeader: HeaderData?, presentationData: ChatPresentationData, controllerInteraction: ChatControllerInteraction?, context: AccountContext, action: ((Int32, Bool) -> Void)? = nil) {
|
||||||
self.timestamp = timestamp
|
self.timestamp = timestamp
|
||||||
self.scheduled = scheduled
|
self.scheduled = scheduled
|
||||||
self.displayPeer = displayPeer
|
self.displayHeader = displayHeader
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.controllerInteraction = controllerInteraction
|
self.controllerInteraction = controllerInteraction
|
||||||
self.context = context
|
self.context = context
|
||||||
self.action = action
|
self.action = action
|
||||||
self.roundedTimestamp = dateHeaderTimestampId(timestamp: timestamp)
|
self.roundedTimestamp = dateHeaderTimestampId(timestamp: timestamp)
|
||||||
if let _ = self.displayPeer {
|
if let _ = self.displayHeader {
|
||||||
self.idValue = ChatMessageDateHeader.Id(roundedTimestamp: 0, separableThreadId: separableThreadId)
|
self.idValue = ChatMessageDateHeader.Id(roundedTimestamp: 0, separableThreadId: separableThreadId)
|
||||||
self.id = ListViewItemNode.HeaderId(space: 3, id: self.idValue)
|
self.id = ListViewItemNode.HeaderId(space: 3, id: self.idValue)
|
||||||
self.stackingId = ListViewItemNode.HeaderId(space: 2, id: ChatMessageDateHeader.Id(roundedTimestamp: Int64(self.roundedTimestamp), separableThreadId: nil))
|
self.stackingId = ListViewItemNode.HeaderId(space: 2, id: ChatMessageDateHeader.Id(roundedTimestamp: Int64(self.roundedTimestamp), separableThreadId: nil))
|
||||||
@ -98,7 +105,7 @@ public final class ChatMessageDateHeader: ListViewItemHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func node(synchronousLoad: Bool) -> ListViewItemHeaderNode {
|
public func node(synchronousLoad: Bool) -> ListViewItemHeaderNode {
|
||||||
return ChatMessageDateHeaderNodeImpl(localTimestamp: self.roundedTimestamp, scheduled: self.scheduled, displayPeer: self.displayPeer, presentationData: self.presentationData, controllerInteraction: self.controllerInteraction, context: self.context, action: self.action)
|
return ChatMessageDateHeaderNodeImpl(localTimestamp: self.roundedTimestamp, scheduled: self.scheduled, displayHeader: self.displayHeader, presentationData: self.presentationData, controllerInteraction: self.controllerInteraction, context: self.context, action: self.action)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?) {
|
public func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?) {
|
||||||
@ -362,9 +369,11 @@ private final class ChatMessagePeerContentNode: ASDisplayNode {
|
|||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private var presentationData: ChatPresentationData
|
private var presentationData: ChatPresentationData
|
||||||
private let controllerInteraction: ChatControllerInteraction?
|
private let controllerInteraction: ChatControllerInteraction?
|
||||||
private let peer: EnginePeer
|
private let displayHeader: ChatMessageDateHeader.HeaderData
|
||||||
|
|
||||||
|
private var avatarNode: AvatarNode?
|
||||||
|
private var icon: ComponentView<Empty>?
|
||||||
|
|
||||||
private let avatarNode: AvatarNode
|
|
||||||
public let labelNode: TextNode
|
public let labelNode: TextNode
|
||||||
public let backgroundNode: NavigationBackgroundNode
|
public let backgroundNode: NavigationBackgroundNode
|
||||||
public let stickBackgroundNode: ASImageNode
|
public let stickBackgroundNode: ASImageNode
|
||||||
@ -372,13 +381,11 @@ private final class ChatMessagePeerContentNode: ASDisplayNode {
|
|||||||
private var backgroundContent: WallpaperBubbleBackgroundNode?
|
private var backgroundContent: WallpaperBubbleBackgroundNode?
|
||||||
private var text: String
|
private var text: String
|
||||||
|
|
||||||
init(context: AccountContext, presentationData: ChatPresentationData, controllerInteraction: ChatControllerInteraction?, peer: EnginePeer) {
|
init(context: AccountContext, presentationData: ChatPresentationData, controllerInteraction: ChatControllerInteraction?, displayHeader: ChatMessageDateHeader.HeaderData) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.controllerInteraction = controllerInteraction
|
self.controllerInteraction = controllerInteraction
|
||||||
self.peer = peer
|
self.displayHeader = displayHeader
|
||||||
|
|
||||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 8.0))
|
|
||||||
|
|
||||||
self.labelNode = TextNode()
|
self.labelNode = TextNode()
|
||||||
self.labelNode.isUserInteractionEnabled = false
|
self.labelNode.isUserInteractionEnabled = false
|
||||||
@ -397,7 +404,13 @@ private final class ChatMessagePeerContentNode: ASDisplayNode {
|
|||||||
self.stickBackgroundNode.displayWithoutProcessing = true
|
self.stickBackgroundNode.displayWithoutProcessing = true
|
||||||
self.stickBackgroundNode.displaysAsynchronously = false
|
self.stickBackgroundNode.displaysAsynchronously = false
|
||||||
|
|
||||||
let text = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
let text: String
|
||||||
|
switch displayHeader.contents {
|
||||||
|
case let .peer(peer):
|
||||||
|
text = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||||
|
case let .thread(_, info):
|
||||||
|
text = info.title
|
||||||
|
}
|
||||||
self.text = text
|
self.text = text
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
@ -415,7 +428,50 @@ private final class ChatMessagePeerContentNode: ASDisplayNode {
|
|||||||
} else {
|
} else {
|
||||||
self.addSubnode(self.backgroundNode)
|
self.addSubnode(self.backgroundNode)
|
||||||
}
|
}
|
||||||
self.addSubnode(self.avatarNode)
|
|
||||||
|
switch displayHeader.contents {
|
||||||
|
case let .peer(peer):
|
||||||
|
let avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 8.0))
|
||||||
|
self.avatarNode = avatarNode
|
||||||
|
self.addSubnode(avatarNode)
|
||||||
|
|
||||||
|
if peer.smallProfileImage != nil {
|
||||||
|
avatarNode.setPeerV2(context: context, theme: presentationData.theme.theme, peer: peer, displayDimensions: CGSize(width: 16.0, height: 16.0))
|
||||||
|
} else {
|
||||||
|
avatarNode.setPeer(context: context, theme: presentationData.theme.theme, peer: peer, displayDimensions: CGSize(width: 16.0, height: 16.0))
|
||||||
|
}
|
||||||
|
case let .thread(id, info):
|
||||||
|
let avatarIconContent: EmojiStatusComponent.Content
|
||||||
|
if id != 1 {
|
||||||
|
if let fileId = info.icon, fileId != 0 {
|
||||||
|
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 16.0, height: 16.0), placeholderColor: presentationData.theme.theme.list.mediaPlaceholderColor, themeColor: presentationData.theme.theme.list.itemAccentColor, loopMode: .count(0))
|
||||||
|
} else {
|
||||||
|
avatarIconContent = .topic(title: String(info.title.prefix(1)), color: info.iconColor, size: CGSize(width: 16.0, height: 16.0))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicIcon(presentationData.theme.theme)?.withRenderingMode(.alwaysTemplate), tintColor: bubbleVariableColor(variableColor: presentationData.theme.theme.chat.serviceMessage.dateTextColor, wallpaper: presentationData.theme.wallpaper))
|
||||||
|
}
|
||||||
|
|
||||||
|
let avatarIconComponent = EmojiStatusComponent(
|
||||||
|
context: context,
|
||||||
|
animationCache: context.animationCache,
|
||||||
|
animationRenderer: context.animationRenderer,
|
||||||
|
content: avatarIconContent,
|
||||||
|
isVisibleForAnimations: false,
|
||||||
|
action: nil
|
||||||
|
)
|
||||||
|
let icon = ComponentView<Empty>()
|
||||||
|
self.icon = icon
|
||||||
|
let _ = icon.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(avatarIconComponent),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: 16.0, height: 16.0)
|
||||||
|
)
|
||||||
|
if let iconView = icon.view {
|
||||||
|
self.view.addSubview(iconView)
|
||||||
|
}
|
||||||
|
}
|
||||||
self.addSubnode(self.labelNode)
|
self.addSubnode(self.labelNode)
|
||||||
|
|
||||||
let titleFont = Font.medium(min(18.0, floor(presentationData.fontSize.baseDisplaySize * 13.0 / 17.0)))
|
let titleFont = Font.medium(min(18.0, floor(presentationData.fontSize.baseDisplaySize * 13.0 / 17.0)))
|
||||||
@ -461,7 +517,13 @@ private final class ChatMessagePeerContentNode: ASDisplayNode {
|
|||||||
let chatDateInset: CGFloat = 6.0
|
let chatDateInset: CGFloat = 6.0
|
||||||
|
|
||||||
let avatarDiameter: CGFloat = 16.0
|
let avatarDiameter: CGFloat = 16.0
|
||||||
let avatarInset: CGFloat = 2.0
|
let avatarInset: CGFloat
|
||||||
|
switch self.displayHeader.contents {
|
||||||
|
case .peer:
|
||||||
|
avatarInset = 2.0
|
||||||
|
case .thread:
|
||||||
|
avatarInset = 4.0
|
||||||
|
}
|
||||||
let avatarSpacing: CGFloat = 4.0
|
let avatarSpacing: CGFloat = 4.0
|
||||||
|
|
||||||
let labelSize = self.labelNode.bounds.size
|
let labelSize = self.labelNode.bounds.size
|
||||||
@ -469,14 +531,14 @@ private final class ChatMessagePeerContentNode: ASDisplayNode {
|
|||||||
|
|
||||||
let backgroundFrame = CGRect(origin: CGPoint(x: leftInset + floorToScreenPixels((size.width - leftInset - rightInset - backgroundSize.width) / 2.0), y: (size.height - chatDateSize) / 2.0), size: backgroundSize)
|
let backgroundFrame = CGRect(origin: CGPoint(x: leftInset + floorToScreenPixels((size.width - leftInset - rightInset - backgroundSize.width) / 2.0), y: (size.height - chatDateSize) / 2.0), size: backgroundSize)
|
||||||
|
|
||||||
let avatarFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + avatarInset, y: backgroundFrame.origin.y + floorToScreenPixels((backgroundSize.height - avatarDiameter) / 2.0)), size: CGSize(width: avatarDiameter, height: avatarDiameter))
|
let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + avatarInset, y: backgroundFrame.origin.y + floorToScreenPixels((backgroundSize.height - avatarDiameter) / 2.0)), size: CGSize(width: avatarDiameter, height: avatarDiameter))
|
||||||
transition.updateFrame(node: self.avatarNode, frame: avatarFrame)
|
|
||||||
self.avatarNode.updateSize(size: avatarFrame.size)
|
|
||||||
|
|
||||||
if self.peer.smallProfileImage != nil {
|
if let avatarNode = self.avatarNode {
|
||||||
self.avatarNode.setPeerV2(context: self.context, theme: self.presentationData.theme.theme, peer: self.peer, displayDimensions: avatarFrame.size)
|
transition.updateFrame(node: avatarNode, frame: iconFrame)
|
||||||
} else {
|
avatarNode.updateSize(size: iconFrame.size)
|
||||||
self.avatarNode.setPeer(context: self.context, theme: self.presentationData.theme.theme, peer: self.peer, displayDimensions: avatarFrame.size)
|
}
|
||||||
|
if let iconView = self.icon?.view {
|
||||||
|
transition.updateFrame(view: iconView, frame: iconFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
transition.updateFrame(node: self.stickBackgroundNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
transition.updateFrame(node: self.stickBackgroundNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
||||||
@ -517,7 +579,7 @@ public final class ChatMessageDateHeaderNodeImpl: ListViewItemHeaderNode, ChatMe
|
|||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let localTimestamp: Int32
|
private let localTimestamp: Int32
|
||||||
private let scheduled: Bool
|
private let scheduled: Bool
|
||||||
private let displayPeer: ChatMessageDateHeader.PeerData?
|
private let displayHeader: ChatMessageDateHeader.HeaderData?
|
||||||
private var presentationData: ChatPresentationData
|
private var presentationData: ChatPresentationData
|
||||||
private let controllerInteraction: ChatControllerInteraction?
|
private let controllerInteraction: ChatControllerInteraction?
|
||||||
|
|
||||||
@ -528,14 +590,14 @@ public final class ChatMessageDateHeaderNodeImpl: ListViewItemHeaderNode, ChatMe
|
|||||||
private var params: (size: CGSize, leftInset: CGFloat, rightInset: CGFloat)?
|
private var params: (size: CGSize, leftInset: CGFloat, rightInset: CGFloat)?
|
||||||
private var absolutePosition: (CGRect, CGSize)?
|
private var absolutePosition: (CGRect, CGSize)?
|
||||||
|
|
||||||
public init(localTimestamp: Int32, scheduled: Bool, displayPeer: ChatMessageDateHeader.PeerData?, presentationData: ChatPresentationData, controllerInteraction: ChatControllerInteraction?, context: AccountContext, action: ((Int32, Bool) -> Void)? = nil) {
|
public init(localTimestamp: Int32, scheduled: Bool, displayHeader: ChatMessageDateHeader.HeaderData?, presentationData: ChatPresentationData, controllerInteraction: ChatControllerInteraction?, context: AccountContext, action: ((Int32, Bool) -> Void)? = nil) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.controllerInteraction = controllerInteraction
|
self.controllerInteraction = controllerInteraction
|
||||||
|
|
||||||
self.localTimestamp = localTimestamp
|
self.localTimestamp = localTimestamp
|
||||||
self.scheduled = scheduled
|
self.scheduled = scheduled
|
||||||
self.displayPeer = displayPeer
|
self.displayHeader = displayHeader
|
||||||
self.action = action
|
self.action = action
|
||||||
|
|
||||||
let isRotated = controllerInteraction?.chatIsRotated ?? true
|
let isRotated = controllerInteraction?.chatIsRotated ?? true
|
||||||
@ -546,13 +608,13 @@ public final class ChatMessageDateHeaderNodeImpl: ListViewItemHeaderNode, ChatMe
|
|||||||
self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
|
self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let displayPeer {
|
if let displayHeader {
|
||||||
if self.peerContentNode == nil {
|
if self.peerContentNode == nil {
|
||||||
let sectionSeparator = ChatMessageDateSectionSeparatorNode(controllerInteraction: controllerInteraction, presentationData: presentationData)
|
let sectionSeparator = ChatMessageDateSectionSeparatorNode(controllerInteraction: controllerInteraction, presentationData: presentationData)
|
||||||
self.sectionSeparator = sectionSeparator
|
self.sectionSeparator = sectionSeparator
|
||||||
self.addSubnode(sectionSeparator)
|
self.addSubnode(sectionSeparator)
|
||||||
|
|
||||||
let peerContentNode = ChatMessagePeerContentNode(context: self.context, presentationData: self.presentationData, controllerInteraction: self.controllerInteraction, peer: displayPeer.peer)
|
let peerContentNode = ChatMessagePeerContentNode(context: self.context, presentationData: self.presentationData, controllerInteraction: self.controllerInteraction, displayHeader: displayHeader)
|
||||||
self.peerContentNode = peerContentNode
|
self.peerContentNode = peerContentNode
|
||||||
self.addSubnode(peerContentNode)
|
self.addSubnode(peerContentNode)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -92,10 +92,10 @@ private func messagesShouldBeMerged(accountPeerId: PeerId, _ lhs: Message, _ rhs
|
|||||||
isPaid = true
|
isPaid = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var sameThread = true
|
let sameThread = true
|
||||||
if let lhsPeer = lhs.peers[lhs.id.peerId], let rhsPeer = rhs.peers[rhs.id.peerId], arePeersEqual(lhsPeer, rhsPeer), let channel = lhsPeer as? TelegramChannel, channel.isForumOrMonoForum, lhs.threadId != rhs.threadId {
|
/*if let lhsPeer = lhs.peers[lhs.id.peerId], let rhsPeer = rhs.peers[rhs.id.peerId], arePeersEqual(lhsPeer, rhsPeer), let channel = lhsPeer as? TelegramChannel, channel.isForumOrMonoForum, lhs.threadId != rhs.threadId {
|
||||||
sameThread = false
|
sameThread = false
|
||||||
}
|
}*/
|
||||||
|
|
||||||
var sameAuthor = false
|
var sameAuthor = false
|
||||||
if lhsEffectiveAuthor?.id == rhsEffectiveAuthor?.id && lhs.effectivelyIncoming(accountPeerId) == rhs.effectivelyIncoming(accountPeerId) {
|
if lhsEffectiveAuthor?.id == rhsEffectiveAuthor?.id && lhs.effectivelyIncoming(accountPeerId) == rhs.effectivelyIncoming(accountPeerId) {
|
||||||
@ -288,7 +288,7 @@ public final class ChatMessageItemImpl: ChatMessageItem, CustomStringConvertible
|
|||||||
|
|
||||||
let messagePeerId: PeerId = chatLocation.peerId ?? content.firstMessage.id.peerId
|
let messagePeerId: PeerId = chatLocation.peerId ?? content.firstMessage.id.peerId
|
||||||
var headerSeparableThreadId: Int64?
|
var headerSeparableThreadId: Int64?
|
||||||
var headerDisplayPeer: ChatMessageDateHeader.PeerData?
|
var headerDisplayPeer: ChatMessageDateHeader.HeaderData?
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let peerId = messagePeerId
|
let peerId = messagePeerId
|
||||||
@ -320,15 +320,22 @@ public final class ChatMessageItemImpl: ChatMessageItem, CustomStringConvertible
|
|||||||
}
|
}
|
||||||
displayAuthorInfo = incoming && peerId.isGroupOrChannel && effectiveAuthor != nil
|
displayAuthorInfo = incoming && peerId.isGroupOrChannel && effectiveAuthor != nil
|
||||||
|
|
||||||
if let channel = content.firstMessage.peers[content.firstMessage.id.peerId] as? TelegramChannel, channel.isMonoForum {
|
if let channel = content.firstMessage.peers[content.firstMessage.id.peerId] as? TelegramChannel, channel.isForumOrMonoForum {
|
||||||
if case .replyThread = chatLocation {
|
if case .replyThread = chatLocation {
|
||||||
displayAuthorInfo = false
|
displayAuthorInfo = false
|
||||||
} else {
|
} else {
|
||||||
if let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = content.firstMessage.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil {
|
if channel.isMonoForum {
|
||||||
headerSeparableThreadId = content.firstMessage.threadId
|
if let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = content.firstMessage.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil {
|
||||||
|
headerSeparableThreadId = content.firstMessage.threadId
|
||||||
if let threadId = content.firstMessage.threadId, let peer = content.firstMessage.peers[EnginePeer.Id(threadId)] {
|
|
||||||
headerDisplayPeer = ChatMessageDateHeader.PeerData(peer: EnginePeer(peer))
|
if let threadId = content.firstMessage.threadId, let peer = content.firstMessage.peers[EnginePeer.Id(threadId)] {
|
||||||
|
headerDisplayPeer = ChatMessageDateHeader.HeaderData(contents: .peer(EnginePeer(peer)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let threadId = content.firstMessage.threadId {
|
||||||
|
if let threadInfo = content.firstMessage.associatedThreadInfo {
|
||||||
|
headerSeparableThreadId = content.firstMessage.threadId
|
||||||
|
headerDisplayPeer = ChatMessageDateHeader.HeaderData(contents: .thread(id: threadId, info: threadInfo))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -343,7 +350,7 @@ public final class ChatMessageItemImpl: ChatMessageItem, CustomStringConvertible
|
|||||||
isScheduledMessages = true
|
isScheduledMessages = true
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dateHeader = ChatMessageDateHeader(timestamp: content.index.timestamp, separableThreadId: nil, scheduled: isScheduledMessages, displayPeer: nil, presentationData: presentationData, controllerInteraction: controllerInteraction, context: context, action: { timestamp, alreadyThere in
|
self.dateHeader = ChatMessageDateHeader(timestamp: content.index.timestamp, separableThreadId: nil, scheduled: isScheduledMessages, displayHeader: nil, presentationData: presentationData, controllerInteraction: controllerInteraction, context: context, action: { timestamp, alreadyThere in
|
||||||
var calendar = NSCalendar.current
|
var calendar = NSCalendar.current
|
||||||
calendar.timeZone = TimeZone(abbreviation: "UTC")!
|
calendar.timeZone = TimeZone(abbreviation: "UTC")!
|
||||||
let date = Date(timeIntervalSince1970: TimeInterval(timestamp))
|
let date = Date(timeIntervalSince1970: TimeInterval(timestamp))
|
||||||
@ -355,8 +362,8 @@ public final class ChatMessageItemImpl: ChatMessageItem, CustomStringConvertible
|
|||||||
})
|
})
|
||||||
|
|
||||||
if let headerSeparableThreadId, let headerDisplayPeer {
|
if let headerSeparableThreadId, let headerDisplayPeer {
|
||||||
self.topicHeader = ChatMessageDateHeader(timestamp: content.index.timestamp, separableThreadId: headerSeparableThreadId, scheduled: false, displayPeer: headerDisplayPeer, presentationData: presentationData, controllerInteraction: controllerInteraction, context: context, action: { _, _ in
|
self.topicHeader = ChatMessageDateHeader(timestamp: content.index.timestamp, separableThreadId: headerSeparableThreadId, scheduled: false, displayHeader: headerDisplayPeer, presentationData: presentationData, controllerInteraction: controllerInteraction, context: context, action: { _, _ in
|
||||||
controllerInteraction.updateChatLocationThread(headerSeparableThreadId)
|
controllerInteraction.updateChatLocationThread(headerSeparableThreadId, nil)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
self.topicHeader = nil
|
self.topicHeader = nil
|
||||||
|
|||||||
@ -25,7 +25,7 @@ public class ChatReplyCountItem: ListViewItem {
|
|||||||
self.isComments = isComments
|
self.isComments = isComments
|
||||||
self.count = count
|
self.count = count
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.header = ChatMessageDateHeader(timestamp: index.timestamp, separableThreadId: nil, scheduled: false, displayPeer: nil, presentationData: presentationData, controllerInteraction: controllerInteraction, context: context)
|
self.header = ChatMessageDateHeader(timestamp: index.timestamp, separableThreadId: nil, scheduled: false, displayHeader: nil, presentationData: presentationData, controllerInteraction: controllerInteraction, context: context)
|
||||||
self.controllerInteraction = controllerInteraction
|
self.controllerInteraction = controllerInteraction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@ public class ChatUnreadItem: ListViewItem {
|
|||||||
self.index = index
|
self.index = index
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.controllerInteraction = controllerInteraction
|
self.controllerInteraction = controllerInteraction
|
||||||
self.header = ChatMessageDateHeader(timestamp: index.timestamp, separableThreadId: nil, scheduled: false, displayPeer: nil, presentationData: presentationData, controllerInteraction: controllerInteraction, context: context)
|
self.header = ChatMessageDateHeader(timestamp: index.timestamp, separableThreadId: nil, scheduled: false, displayHeader: nil, presentationData: presentationData, controllerInteraction: controllerInteraction, context: context)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||||
|
|||||||
@ -430,7 +430,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
let makeForwardInfoLayout = ChatMessageForwardInfoNode.asyncLayout(self.forwardInfoNode)
|
let makeForwardInfoLayout = ChatMessageForwardInfoNode.asyncLayout(self.forwardInfoNode)
|
||||||
|
|
||||||
let viaBotLayout = TextNode.asyncLayout(self.viaBotNode)
|
let viaBotLayout = TextNode.asyncLayout(self.viaBotNode)
|
||||||
let makeThreadInfoLayout = ChatMessageThreadInfoNode.asyncLayout(self.threadInfoNode)
|
//let makeThreadInfoLayout = ChatMessageThreadInfoNode.asyncLayout(self.threadInfoNode)
|
||||||
let makeReplyInfoLayout = ChatMessageReplyInfoNode.asyncLayout(self.replyInfoNode)
|
let makeReplyInfoLayout = ChatMessageReplyInfoNode.asyncLayout(self.replyInfoNode)
|
||||||
let currentShareButtonNode = self.shareButtonNode
|
let currentShareButtonNode = self.shareButtonNode
|
||||||
let currentForwardInfo = self.appliedForwardInfo
|
let currentForwardInfo = self.appliedForwardInfo
|
||||||
@ -669,7 +669,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
let (dateAndStatusSize, dateAndStatusApply) = statusSuggestedWidthAndContinue.1(statusSuggestedWidthAndContinue.0)
|
let (dateAndStatusSize, dateAndStatusApply) = statusSuggestedWidthAndContinue.1(statusSuggestedWidthAndContinue.0)
|
||||||
|
|
||||||
var viaBotApply: (TextNodeLayout, () -> TextNode)?
|
var viaBotApply: (TextNodeLayout, () -> TextNode)?
|
||||||
var threadInfoApply: (CGSize, (Bool) -> ChatMessageThreadInfoNode)?
|
let threadInfoApply: (CGSize, (Bool) -> ChatMessageThreadInfoNode)? = nil
|
||||||
var replyInfoApply: (CGSize, (CGSize, Bool, ListViewItemUpdateAnimation) -> ChatMessageReplyInfoNode)?
|
var replyInfoApply: (CGSize, (CGSize, Bool, ListViewItemUpdateAnimation) -> ChatMessageReplyInfoNode)?
|
||||||
var replyMarkup: ReplyMarkupMessageAttribute?
|
var replyMarkup: ReplyMarkupMessageAttribute?
|
||||||
|
|
||||||
@ -739,7 +739,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
hasReply = false
|
hasReply = false
|
||||||
}
|
}
|
||||||
|
|
||||||
threadInfoApply = makeThreadInfoLayout(ChatMessageThreadInfoNode.Arguments(
|
/*threadInfoApply = makeThreadInfoLayout(ChatMessageThreadInfoNode.Arguments(
|
||||||
presentationData: item.presentationData,
|
presentationData: item.presentationData,
|
||||||
strings: item.presentationData.strings,
|
strings: item.presentationData.strings,
|
||||||
context: item.context,
|
context: item.context,
|
||||||
@ -751,7 +751,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude),
|
constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude),
|
||||||
animationCache: item.controllerInteraction.presentationContext.animationCache,
|
animationCache: item.controllerInteraction.presentationContext.animationCache,
|
||||||
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
|
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
|
||||||
))
|
))*/
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasReply, (replyMessage != nil || replyForward != nil || replyStory != nil) {
|
if hasReply, (replyMessage != nil || replyForward != nil || replyStory != nil) {
|
||||||
|
|||||||
@ -514,7 +514,7 @@ public class ChatMessageThreadInfoNode: ASDisplayNode {
|
|||||||
var containerSize: CGSize = CGSize(width: 22.0, height: 22.0)
|
var containerSize: CGSize = CGSize(width: 22.0, height: 22.0)
|
||||||
var iconX: CGFloat = 0.0
|
var iconX: CGFloat = 0.0
|
||||||
if arguments.threadId == 1 {
|
if arguments.threadId == 1 {
|
||||||
titleTopicIconContent = .image(image: generalThreadIcon)
|
titleTopicIconContent = .image(image: generalThreadIcon, tintColor: nil)
|
||||||
containerSize = CGSize(width: 18.0, height: 18.0)
|
containerSize = CGSize(width: 18.0, height: 18.0)
|
||||||
iconX = 3.0
|
iconX = 3.0
|
||||||
} else if let fileId = topicIconId, fileId != 0 {
|
} else if let fileId = topicIconId, fileId != 0 {
|
||||||
|
|||||||
@ -526,7 +526,7 @@ final class AvatarComponent: Component {
|
|||||||
|
|
||||||
let avatarIconContent: EmojiStatusComponent.Content
|
let avatarIconContent: EmojiStatusComponent.Content
|
||||||
if threadData.id == 1 {
|
if threadData.id == 1 {
|
||||||
avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicIcon(theme))
|
avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicIcon(theme), tintColor: nil)
|
||||||
} else if let fileId = threadData.data.info.icon, fileId != 0 {
|
} else if let fileId = threadData.data.info.icon, fileId != 0 {
|
||||||
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .count(0))
|
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .count(0))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -171,7 +171,7 @@ public final class ChatRecentActionsController: TelegramBaseController {
|
|||||||
}, openBoostToUnrestrict: {
|
}, openBoostToUnrestrict: {
|
||||||
}, updateVideoTrimRange: { _, _, _, _ in
|
}, updateVideoTrimRange: { _, _, _, _ in
|
||||||
}, updateHistoryFilter: { _ in
|
}, updateHistoryFilter: { _ in
|
||||||
}, updateChatLocationThread: { _ in
|
}, updateChatLocationThread: { _, _ in
|
||||||
}, toggleChatSidebarMode: {
|
}, toggleChatSidebarMode: {
|
||||||
}, updateDisplayHistoryFilterAsList: { _ in
|
}, updateDisplayHistoryFilterAsList: { _ in
|
||||||
}, requestLayout: { _ in
|
}, requestLayout: { _ in
|
||||||
|
|||||||
@ -313,7 +313,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
}, navigateToMessageStandalone: { _ in
|
}, navigateToMessageStandalone: { _ in
|
||||||
}, navigateToThreadMessage: { [weak self] peerId, threadId, _ in
|
}, navigateToThreadMessage: { [weak self] peerId, threadId, _ in
|
||||||
if let context = self?.context, let navigationController = self?.getNavigationController() {
|
if let context = self?.context, let navigationController = self?.getNavigationController() {
|
||||||
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: threadId, messageId: nil, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .always).startStandalone()
|
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: threadId, messageId: nil, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .always, animated: true).startStandalone()
|
||||||
}
|
}
|
||||||
}, tapMessage: nil, clickThroughMessage: { _, _ in }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _, _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false }, sendEmoji: { _, _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _, _ in return false
|
}, tapMessage: nil, clickThroughMessage: { _, _ in }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _, _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false }, sendEmoji: { _, _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _, _ in return false
|
||||||
}, requestMessageActionCallback: { [weak self] messageId, _, _, _ in
|
}, requestMessageActionCallback: { [weak self] messageId, _, _, _ in
|
||||||
@ -646,7 +646,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
}, forceUpdateWarpContents: {
|
}, forceUpdateWarpContents: {
|
||||||
}, playShakeAnimation: {
|
}, playShakeAnimation: {
|
||||||
}, displayQuickShare: { _, _ ,_ in
|
}, displayQuickShare: { _, _ ,_ in
|
||||||
}, updateChatLocationThread: { _ in
|
}, updateChatLocationThread: { _, _ in
|
||||||
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
|
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
|
||||||
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: self.backgroundNode))
|
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: self.backgroundNode))
|
||||||
self.controllerInteraction = controllerInteraction
|
self.controllerInteraction = controllerInteraction
|
||||||
@ -1208,7 +1208,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
case let .replyThread(messageId):
|
case let .replyThread(messageId):
|
||||||
if let navigationController = strongSelf.getNavigationController() {
|
if let navigationController = strongSelf.getNavigationController() {
|
||||||
let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: messageId.peerId, threadId: Int64(messageId.id), messageId: nil, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .always).startStandalone()
|
let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: messageId.peerId, threadId: Int64(messageId.id), messageId: nil, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .always, animated: true).startStandalone()
|
||||||
}
|
}
|
||||||
case let .stickerPack(name, type):
|
case let .stickerPack(name, type):
|
||||||
let _ = type
|
let _ = type
|
||||||
|
|||||||
@ -501,7 +501,7 @@ public final class ChatSendGroupMediaMessageContextPreview: UIView, ChatSendMess
|
|||||||
}, forceUpdateWarpContents: {
|
}, forceUpdateWarpContents: {
|
||||||
}, playShakeAnimation: {
|
}, playShakeAnimation: {
|
||||||
}, displayQuickShare: { _, _ ,_ in
|
}, displayQuickShare: { _, _ ,_ in
|
||||||
}, updateChatLocationThread: { _ in
|
}, updateChatLocationThread: { _, _ in
|
||||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||||
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: self.context, backgroundNode: self.wallpaperBackgroundNode))
|
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: self.context, backgroundNode: self.wallpaperBackgroundNode))
|
||||||
|
|
||||||
|
|||||||
@ -37,23 +37,26 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let strings: PresentationStrings
|
let strings: PresentationStrings
|
||||||
let peerId: EnginePeer.Id
|
let peerId: EnginePeer.Id
|
||||||
|
let isMonoforum: Bool
|
||||||
let topicId: Int64?
|
let topicId: Int64?
|
||||||
let togglePanel: () -> Void
|
let togglePanel: () -> Void
|
||||||
let updateTopicId: (Int64?) -> Void
|
let updateTopicId: (Int64?, Bool) -> Void
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
strings: PresentationStrings,
|
strings: PresentationStrings,
|
||||||
peerId: EnginePeer.Id,
|
peerId: EnginePeer.Id,
|
||||||
|
isMonoforum: Bool,
|
||||||
topicId: Int64?,
|
topicId: Int64?,
|
||||||
togglePanel: @escaping () -> Void,
|
togglePanel: @escaping () -> Void,
|
||||||
updateTopicId: @escaping (Int64?) -> Void
|
updateTopicId: @escaping (Int64?, Bool) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
|
self.isMonoforum = isMonoforum
|
||||||
self.topicId = topicId
|
self.topicId = topicId
|
||||||
self.togglePanel = togglePanel
|
self.togglePanel = togglePanel
|
||||||
self.updateTopicId = updateTopicId
|
self.updateTopicId = updateTopicId
|
||||||
@ -72,6 +75,9 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
if lhs.peerId != rhs.peerId {
|
if lhs.peerId != rhs.peerId {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.isMonoforum != rhs.isMonoforum {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.topicId != rhs.topicId {
|
if lhs.topicId != rhs.topicId {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -111,7 +117,7 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
|
|
||||||
private let containerButton: HighlightTrackingButton
|
private let containerButton: HighlightTrackingButton
|
||||||
|
|
||||||
private let icon = ComponentView<Empty>()
|
private var icon: ComponentView<Empty>?
|
||||||
private var avatarNode: AvatarNode?
|
private var avatarNode: AvatarNode?
|
||||||
private let title = ComponentView<Empty>()
|
private let title = ComponentView<Empty>()
|
||||||
|
|
||||||
@ -179,34 +185,78 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
|
|
||||||
func update(context: AccountContext, item: Item, isSelected: Bool, theme: PresentationTheme, width: CGFloat, transition: ComponentTransition) -> CGSize {
|
func update(context: AccountContext, item: Item, isSelected: Bool, theme: PresentationTheme, width: CGFloat, transition: ComponentTransition) -> CGSize {
|
||||||
let spacing: CGFloat = 3.0
|
let spacing: CGFloat = 3.0
|
||||||
|
let iconSize = CGSize(width: 30.0, height: 30.0)
|
||||||
|
|
||||||
let avatarIconContent: EmojiStatusComponent.Content
|
var avatarIconContent: EmojiStatusComponent.Content?
|
||||||
if case let .forum(topicId) = item.item.id, topicId != 1, let threadData = item.item.threadData {
|
if case let .forum(topicId) = item.item.id {
|
||||||
if let fileId = threadData.info.icon, fileId != 0 {
|
if topicId != 1, let threadData = item.item.threadData {
|
||||||
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 18.0, height: 18.0), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .count(0))
|
if let fileId = threadData.info.icon, fileId != 0 {
|
||||||
|
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: iconSize, placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .count(0))
|
||||||
|
} else {
|
||||||
|
avatarIconContent = .topic(title: String(threadData.info.title.prefix(1)), color: threadData.info.iconColor, size: iconSize)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
avatarIconContent = .topic(title: String(threadData.info.title.prefix(1)), color: threadData.info.iconColor, size: CGSize(width: 18.0, height: 18.0))
|
avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicIcon(theme), tintColor: theme.rootController.navigationBar.secondaryTextColor)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicIcon(theme))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let avatarIconComponent = EmojiStatusComponent(
|
if let avatarIconContent {
|
||||||
context: context,
|
let avatarIconComponent = EmojiStatusComponent(
|
||||||
animationCache: context.animationCache,
|
context: context,
|
||||||
animationRenderer: context.animationRenderer,
|
animationCache: context.animationCache,
|
||||||
content: avatarIconContent,
|
animationRenderer: context.animationRenderer,
|
||||||
isVisibleForAnimations: false,
|
content: avatarIconContent,
|
||||||
action: nil
|
isVisibleForAnimations: false,
|
||||||
)
|
action: nil
|
||||||
let iconSize = self.icon.update(
|
)
|
||||||
transition: .immediate,
|
let icon: ComponentView<Empty>
|
||||||
component: AnyComponent(avatarIconComponent),
|
if let current = self.icon {
|
||||||
environment: {},
|
icon = current
|
||||||
containerSize: CGSize(width: 30.0, height: 30.0)
|
} else {
|
||||||
)
|
icon = ComponentView()
|
||||||
|
self.icon = icon
|
||||||
|
}
|
||||||
|
let _ = icon.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(avatarIconComponent),
|
||||||
|
environment: {},
|
||||||
|
containerSize: iconSize
|
||||||
|
)
|
||||||
|
} else if let icon = self.icon {
|
||||||
|
self.icon = nil
|
||||||
|
icon.view?.removeFromSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
let titleText: String
|
||||||
|
if case let .forum(topicId) = item.item.id {
|
||||||
|
let _ = topicId
|
||||||
|
if let threadData = item.item.threadData {
|
||||||
|
titleText = threadData.info.title
|
||||||
|
} else {
|
||||||
|
//TODO:localize
|
||||||
|
titleText = "General"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
titleText = item.item.renderedPeer.chatMainPeer?.compactDisplayTitle ?? " "
|
||||||
|
}
|
||||||
|
|
||||||
|
if let avatarIconContent, let icon = self.icon {
|
||||||
|
let avatarIconComponent = EmojiStatusComponent(
|
||||||
|
context: context,
|
||||||
|
animationCache: context.animationCache,
|
||||||
|
animationRenderer: context.animationRenderer,
|
||||||
|
content: avatarIconContent,
|
||||||
|
isVisibleForAnimations: false,
|
||||||
|
action: nil
|
||||||
|
)
|
||||||
|
let _ = icon.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(avatarIconComponent),
|
||||||
|
environment: {},
|
||||||
|
containerSize: iconSize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
let titleText: String = item.item.renderedPeer.chatMainPeer?.compactDisplayTitle ?? " "
|
|
||||||
let titleSize = self.title.update(
|
let titleSize = self.title.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(MultilineTextComponent(
|
component: AnyComponent(MultilineTextComponent(
|
||||||
@ -224,39 +274,38 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
let iconFrame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) * 0.5), y: 0.0), size: iconSize)
|
let iconFrame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) * 0.5), y: 0.0), size: iconSize)
|
||||||
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) * 0.5), y: iconFrame.maxY + spacing), size: titleSize)
|
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) * 0.5), y: iconFrame.maxY + spacing), size: titleSize)
|
||||||
|
|
||||||
if let iconView = self.icon.view {
|
if let icon = self.icon {
|
||||||
if iconView.superview == nil {
|
if let avatarNode = self.avatarNode {
|
||||||
iconView.isUserInteractionEnabled = false
|
|
||||||
self.containerButton.addSubview(iconView)
|
|
||||||
}
|
|
||||||
iconView.frame = iconFrame
|
|
||||||
|
|
||||||
if "".isEmpty {
|
|
||||||
iconView.isHidden = true
|
|
||||||
|
|
||||||
let avatarNode: AvatarNode
|
|
||||||
if let current = self.avatarNode {
|
|
||||||
avatarNode = current
|
|
||||||
} else {
|
|
||||||
avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 11.0))
|
|
||||||
avatarNode.isUserInteractionEnabled = false
|
|
||||||
self.avatarNode = avatarNode
|
|
||||||
self.containerButton.addSubview(avatarNode.view)
|
|
||||||
}
|
|
||||||
avatarNode.frame = iconFrame
|
|
||||||
avatarNode.updateSize(size: iconFrame.size)
|
|
||||||
|
|
||||||
if let peer = item.item.renderedPeer.chatMainPeer {
|
|
||||||
if peer.smallProfileImage != nil {
|
|
||||||
avatarNode.setPeerV2(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size)
|
|
||||||
} else {
|
|
||||||
avatarNode.setPeer(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if let avatarNode = self.avatarNode {
|
|
||||||
self.avatarNode = nil
|
self.avatarNode = nil
|
||||||
avatarNode.view.removeFromSuperview()
|
avatarNode.view.removeFromSuperview()
|
||||||
iconView.isHidden = false
|
}
|
||||||
|
|
||||||
|
if let iconView = icon.view {
|
||||||
|
if iconView.superview == nil {
|
||||||
|
iconView.isUserInteractionEnabled = false
|
||||||
|
self.containerButton.addSubview(iconView)
|
||||||
|
}
|
||||||
|
iconView.frame = iconFrame
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let avatarNode: AvatarNode
|
||||||
|
if let current = self.avatarNode {
|
||||||
|
avatarNode = current
|
||||||
|
} else {
|
||||||
|
avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 11.0))
|
||||||
|
avatarNode.isUserInteractionEnabled = false
|
||||||
|
self.avatarNode = avatarNode
|
||||||
|
self.containerButton.addSubview(avatarNode.view)
|
||||||
|
}
|
||||||
|
avatarNode.frame = iconFrame
|
||||||
|
avatarNode.updateSize(size: iconFrame.size)
|
||||||
|
|
||||||
|
if let peer = item.item.renderedPeer.chatMainPeer {
|
||||||
|
if peer.smallProfileImage != nil {
|
||||||
|
avatarNode.setPeerV2(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size)
|
||||||
|
} else {
|
||||||
|
avatarNode.setPeer(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,7 +649,15 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
|
|
||||||
public func topicIndex(threadId: Int64?) -> Int? {
|
public func topicIndex(threadId: Int64?) -> Int? {
|
||||||
if let threadId {
|
if let threadId {
|
||||||
if let value = self.items.firstIndex(where: { $0.id == .chatList(PeerId(threadId)) }) {
|
if let value = self.items.firstIndex(where: { item in
|
||||||
|
if item.id == .chatList(PeerId(threadId)) {
|
||||||
|
return true
|
||||||
|
} else if item.id == .forum(threadId) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}) {
|
||||||
return value + 1
|
return value + 1
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
@ -619,77 +676,7 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
if self.component == nil {
|
if self.component == nil {
|
||||||
let viewKey: PostboxViewKey = .savedMessagesIndex(peerId: component.peerId)
|
let threadListSignal: Signal<EngineChatList, NoError> = component.context.sharedContext.subscribeChatListData(context: component.context, location: component.isMonoforum ? .savedMessagesChats(peerId: component.peerId) : .forum(peerId: component.peerId))
|
||||||
let interfaceStateKey: PostboxViewKey = .chatInterfaceState(peerId: component.peerId)
|
|
||||||
|
|
||||||
let accountPeerId = component.context.account.peerId
|
|
||||||
let threadListSignal: Signal<EngineChatList, NoError> = component.context.account.postbox.combinedView(keys: [viewKey, interfaceStateKey])
|
|
||||||
|> map { views -> EngineChatList in
|
|
||||||
guard let view = views.views[viewKey] as? MessageHistorySavedMessagesIndexView else {
|
|
||||||
preconditionFailure()
|
|
||||||
}
|
|
||||||
|
|
||||||
var draft: EngineChatList.Draft?
|
|
||||||
if let interfaceStateView = views.views[interfaceStateKey] as? ChatInterfaceStateView {
|
|
||||||
if let embeddedState = interfaceStateView.value, let _ = embeddedState.overrideChatTimestamp {
|
|
||||||
if let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) {
|
|
||||||
if let text = opaqueState.synchronizeableInputState?.text {
|
|
||||||
draft = EngineChatList.Draft(text: text, entities: opaqueState.synchronizeableInputState?.entities ?? [])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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: nil,
|
|
||||||
isMuted: false,
|
|
||||||
draft: sourceId == accountPeerId ? 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
|
|
||||||
}
|
|
||||||
|
|
||||||
self.itemsDisposable = (threadListSignal
|
self.itemsDisposable = (threadListSignal
|
||||||
|> deliverOnMainQueue).startStrict(next: { [weak self] chatList in
|
|> deliverOnMainQueue).startStrict(next: { [weak self] chatList in
|
||||||
@ -814,7 +801,8 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
guard let self, let component = self.component else {
|
guard let self, let component = self.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
component.updateTopicId(nil)
|
|
||||||
|
component.updateTopicId(nil, false)
|
||||||
})
|
})
|
||||||
self.allItemView = itemView
|
self.allItemView = itemView
|
||||||
self.scrollView.addSubview(itemView)
|
self.scrollView.addSubview(itemView)
|
||||||
@ -864,8 +852,20 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
guard let self, let component = self.component else {
|
guard let self, let component = self.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let topicId = chatListItem.renderedPeer.peerId.toInt64()
|
|
||||||
component.updateTopicId(topicId)
|
let topicId: Int64
|
||||||
|
if case let .forum(topicIdValue) = chatListItem.id {
|
||||||
|
topicId = topicIdValue
|
||||||
|
} else {
|
||||||
|
topicId = chatListItem.renderedPeer.peerId.toInt64()
|
||||||
|
}
|
||||||
|
|
||||||
|
var direction = true
|
||||||
|
if let lhsIndex = self.topicIndex(threadId: component.topicId), let rhsIndex = self.topicIndex(threadId: topicId) {
|
||||||
|
direction = lhsIndex < rhsIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
component.updateTopicId(topicId, direction)
|
||||||
}, contextGesture: { gesture, sourceNode in
|
}, contextGesture: { gesture, sourceNode in
|
||||||
})
|
})
|
||||||
self.itemViews[itemId] = itemView
|
self.itemViews[itemId] = itemView
|
||||||
@ -873,8 +873,10 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var isSelected = false
|
var isSelected = false
|
||||||
if component.topicId == item.item.renderedPeer.peerId.toInt64() {
|
if case let .forum(topicId) = item.item.id {
|
||||||
isSelected = true
|
isSelected = component.topicId == topicId
|
||||||
|
} else {
|
||||||
|
isSelected = component.topicId == item.item.renderedPeer.peerId.toInt64()
|
||||||
}
|
}
|
||||||
let itemSize = itemView.update(context: component.context, item: item, isSelected: isSelected, theme: component.theme, width: panelWidth, transition: .immediate)
|
let itemSize = itemView.update(context: component.context, item: item, isSelected: isSelected, 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: containerInsets.left, y: contentSize.height), size: itemSize)
|
||||||
@ -893,6 +895,9 @@ public final class ChatSideTopicsPanel: Component {
|
|||||||
|
|
||||||
contentSize.height += itemSize.height
|
contentSize.height += itemSize.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contentSize.height += 12.0
|
||||||
|
|
||||||
var removedIds: [Item.Id] = []
|
var removedIds: [Item.Id] = []
|
||||||
for (id, itemView) in self.itemViews {
|
for (id, itemView) in self.itemViews {
|
||||||
if !validIds.contains(id) {
|
if !validIds.contains(id) {
|
||||||
|
|||||||
@ -280,7 +280,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
|
|||||||
public let forceUpdateWarpContents: () -> Void
|
public let forceUpdateWarpContents: () -> Void
|
||||||
public let playShakeAnimation: () -> Void
|
public let playShakeAnimation: () -> Void
|
||||||
public let displayQuickShare: (MessageId, ASDisplayNode, ContextGesture) -> Void
|
public let displayQuickShare: (MessageId, ASDisplayNode, ContextGesture) -> Void
|
||||||
public let updateChatLocationThread: (Int64?) -> Void
|
public let updateChatLocationThread: (Int64?, ChatControllerAnimateInnerChatSwitchDirection?) -> Void
|
||||||
|
|
||||||
public var canPlayMedia: Bool = false
|
public var canPlayMedia: Bool = false
|
||||||
public var hiddenMedia: [MessageId: [Media]] = [:]
|
public var hiddenMedia: [MessageId: [Media]] = [:]
|
||||||
@ -443,7 +443,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
|
|||||||
forceUpdateWarpContents: @escaping () -> Void,
|
forceUpdateWarpContents: @escaping () -> Void,
|
||||||
playShakeAnimation: @escaping () -> Void,
|
playShakeAnimation: @escaping () -> Void,
|
||||||
displayQuickShare: @escaping (MessageId, ASDisplayNode, ContextGesture) -> Void,
|
displayQuickShare: @escaping (MessageId, ASDisplayNode, ContextGesture) -> Void,
|
||||||
updateChatLocationThread: @escaping (Int64?) -> Void,
|
updateChatLocationThread: @escaping (Int64?, ChatControllerAnimateInnerChatSwitchDirection?) -> Void,
|
||||||
automaticMediaDownloadSettings: MediaAutoDownloadSettings,
|
automaticMediaDownloadSettings: MediaAutoDownloadSettings,
|
||||||
pollActionState: ChatInterfacePollActionState,
|
pollActionState: ChatInterfacePollActionState,
|
||||||
stickerSettings: ChatInterfaceStickerSettings,
|
stickerSettings: ChatInterfaceStickerSettings,
|
||||||
|
|||||||
@ -155,6 +155,13 @@ private enum ChatTitleCredibilityIcon: Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class ChatTitleView: UIView, NavigationBarTitleView {
|
public final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||||
|
public enum AnimateFromSnapshotDirection {
|
||||||
|
case up
|
||||||
|
case down
|
||||||
|
case left
|
||||||
|
case right
|
||||||
|
}
|
||||||
|
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
|
|
||||||
private var theme: PresentationTheme
|
private var theme: PresentationTheme
|
||||||
@ -1111,25 +1118,40 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func prepareSnapshotState() -> SnapshotState {
|
public func prepareSnapshotState() -> SnapshotState? {
|
||||||
let snapshotView = self.snapshotView(afterScreenUpdates: false)!
|
guard let snapshotView = self.snapshotView(afterScreenUpdates: false) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return SnapshotState(
|
return SnapshotState(
|
||||||
snapshotView: snapshotView
|
snapshotView: snapshotView
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func animateFromSnapshot(_ snapshotState: SnapshotState) {
|
public func animateFromSnapshot(_ snapshotState: SnapshotState, direction: AnimateFromSnapshotDirection = .up) {
|
||||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
self.layer.animatePosition(from: CGPoint(x: 0.0, y: 20.0), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true, additive: true)
|
|
||||||
|
var offset = CGPoint()
|
||||||
|
switch direction {
|
||||||
|
case .up:
|
||||||
|
offset.y = -20.0
|
||||||
|
case .down:
|
||||||
|
offset.y = 20.0
|
||||||
|
case .left:
|
||||||
|
offset.x = -20.0
|
||||||
|
case .right:
|
||||||
|
offset.x = 20.0
|
||||||
|
}
|
||||||
|
|
||||||
|
self.layer.animatePosition(from: offset, to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true, additive: true)
|
||||||
|
|
||||||
snapshotState.snapshotView.frame = self.frame
|
snapshotState.snapshotView.frame = self.frame
|
||||||
self.superview?.insertSubview(snapshotState.snapshotView, belowSubview: self)
|
self.superview?.insertSubview(snapshotState.snapshotView, belowSubview: self)
|
||||||
|
|
||||||
let snapshotView = snapshotState.snapshotView
|
let snapshotView = snapshotState.snapshotView
|
||||||
snapshotState.snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
snapshotState.snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.12, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
||||||
snapshotView?.removeFromSuperview()
|
snapshotView?.removeFromSuperview()
|
||||||
})
|
})
|
||||||
snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -20.0), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
|
snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -offset.x, y: -offset.y), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -50,7 +50,7 @@ public final class EmojiStatusComponent: Component {
|
|||||||
case text(color: UIColor, string: String)
|
case text(color: UIColor, string: String)
|
||||||
case animation(content: AnimationContent, size: CGSize, placeholderColor: UIColor, themeColor: UIColor?, loopMode: LoopMode)
|
case animation(content: AnimationContent, size: CGSize, placeholderColor: UIColor, themeColor: UIColor?, loopMode: LoopMode)
|
||||||
case topic(title: String, color: Int32, size: CGSize)
|
case topic(title: String, color: Int32, size: CGSize)
|
||||||
case image(image: UIImage?)
|
case image(image: UIImage?, tintColor: UIColor?)
|
||||||
}
|
}
|
||||||
|
|
||||||
public let postbox: Postbox
|
public let postbox: Postbox
|
||||||
@ -335,8 +335,9 @@ public final class EmojiStatusComponent: Component {
|
|||||||
} else {
|
} else {
|
||||||
iconImage = nil
|
iconImage = nil
|
||||||
}
|
}
|
||||||
case let .image(image):
|
case let .image(image, tintColor):
|
||||||
iconImage = image
|
iconImage = image
|
||||||
|
iconTintColor = tintColor
|
||||||
case let .verified(fillColor, foregroundColor, sizeType):
|
case let .verified(fillColor, foregroundColor, sizeType):
|
||||||
let imageNamePrefix: String
|
let imageNamePrefix: String
|
||||||
switch sizeType {
|
switch sizeType {
|
||||||
|
|||||||
@ -169,7 +169,7 @@ private final class TitleFieldComponent: Component {
|
|||||||
|
|
||||||
let iconContent: EmojiStatusComponent.Content
|
let iconContent: EmojiStatusComponent.Content
|
||||||
if component.isGeneral {
|
if component.isGeneral {
|
||||||
iconContent = .image(image: generateTintedImage(image: UIImage(bundleImageName: "Chat List/GeneralTopicIcon"), color: component.placeholderColor))
|
iconContent = .image(image: generateTintedImage(image: UIImage(bundleImageName: "Chat List/GeneralTopicIcon"), color: component.placeholderColor), tintColor: nil)
|
||||||
self.iconButton.isUserInteractionEnabled = false
|
self.iconButton.isUserInteractionEnabled = false
|
||||||
} else if component.fileId == 0 {
|
} else if component.fileId == 0 {
|
||||||
iconContent = .topic(title: String(component.text.prefix(1)), color: component.iconColor, size: CGSize(width: 32.0, height: 32.0))
|
iconContent = .topic(title: String(component.text.prefix(1)), color: component.iconColor, size: CGSize(width: 32.0, height: 32.0))
|
||||||
|
|||||||
@ -294,7 +294,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
let content: EmojiStatusComponent.Content
|
let content: EmojiStatusComponent.Content
|
||||||
if threadId == 1 {
|
if threadId == 1 {
|
||||||
content = .image(image: PresentationResourcesChat.chatGeneralThreadIcon(theme))
|
content = .image(image: PresentationResourcesChat.chatGeneralThreadIcon(theme), tintColor: nil)
|
||||||
} else if let iconFileId = threadInfo.icon {
|
} else if let iconFileId = threadInfo.icon {
|
||||||
content = .animation(content: .customEmoji(fileId: iconFileId), size: CGSize(width: avatarSize, height: avatarSize), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .forever)
|
content = .animation(content: .customEmoji(fileId: iconFileId), size: CGSize(width: avatarSize, height: avatarSize), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .forever)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -436,7 +436,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
|
|||||||
}, openBoostToUnrestrict: {
|
}, openBoostToUnrestrict: {
|
||||||
}, updateVideoTrimRange: { _, _, _, _ in
|
}, updateVideoTrimRange: { _, _, _, _ in
|
||||||
}, updateHistoryFilter: { _ in
|
}, updateHistoryFilter: { _ in
|
||||||
}, updateChatLocationThread: { _ in
|
}, updateChatLocationThread: { _, _ in
|
||||||
}, toggleChatSidebarMode: {
|
}, toggleChatSidebarMode: {
|
||||||
}, updateDisplayHistoryFilterAsList: { _ in
|
}, updateDisplayHistoryFilterAsList: { _ in
|
||||||
}, requestLayout: { _ in
|
}, requestLayout: { _ in
|
||||||
@ -3302,7 +3302,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
c?.dismiss(completion: {
|
c?.dismiss(completion: {
|
||||||
if let strongSelf = self, let currentPeer = strongSelf.data?.peer, let navigationController = strongSelf.controller?.navigationController as? NavigationController {
|
if let strongSelf = self, let currentPeer = strongSelf.data?.peer, let navigationController = strongSelf.controller?.navigationController as? NavigationController {
|
||||||
if let channel = currentPeer as? TelegramChannel, channel.isForumOrMonoForum, let threadId = message.threadId {
|
if let channel = currentPeer as? TelegramChannel, channel.isForumOrMonoForum, let threadId = message.threadId {
|
||||||
let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: currentPeer.id, threadId: threadId, messageId: message.id, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .default).startStandalone()
|
let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: currentPeer.id, threadId: threadId, messageId: message.id, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .default, animated: true).startStandalone()
|
||||||
} else {
|
} else {
|
||||||
let targetLocation: NavigateToChatControllerParams.Location
|
let targetLocation: NavigateToChatControllerParams.Location
|
||||||
if case let .replyThread(message) = strongSelf.chatLocation {
|
if case let .replyThread(message) = strongSelf.chatLocation {
|
||||||
@ -3464,7 +3464,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
c?.dismiss(completion: {
|
c?.dismiss(completion: {
|
||||||
if let strongSelf = self, let currentPeer = strongSelf.data?.peer, let navigationController = strongSelf.controller?.navigationController as? NavigationController {
|
if let strongSelf = self, let currentPeer = strongSelf.data?.peer, let navigationController = strongSelf.controller?.navigationController as? NavigationController {
|
||||||
if let channel = currentPeer as? TelegramChannel, channel.isForumOrMonoForum, let threadId = message.threadId {
|
if let channel = currentPeer as? TelegramChannel, channel.isForumOrMonoForum, let threadId = message.threadId {
|
||||||
let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: currentPeer.id, threadId: threadId, messageId: message.id, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .default).startStandalone()
|
let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: currentPeer.id, threadId: threadId, messageId: message.id, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .default, animated: true).startStandalone()
|
||||||
} else {
|
} else {
|
||||||
let targetLocation: NavigateToChatControllerParams.Location
|
let targetLocation: NavigateToChatControllerParams.Location
|
||||||
if case let .replyThread(message) = strongSelf.chatLocation {
|
if case let .replyThread(message) = strongSelf.chatLocation {
|
||||||
@ -3806,7 +3806,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
}, forceUpdateWarpContents: {
|
}, forceUpdateWarpContents: {
|
||||||
}, playShakeAnimation: {
|
}, playShakeAnimation: {
|
||||||
}, displayQuickShare: { _, _ ,_ in
|
}, displayQuickShare: { _, _ ,_ in
|
||||||
}, updateChatLocationThread: { _ in
|
}, updateChatLocationThread: { _, _ in
|
||||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||||
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil))
|
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil))
|
||||||
self.hiddenMediaDisposable = context.sharedContext.mediaManager.galleryHiddenMediaManager.hiddenIds().startStrict(next: { [weak self] ids in
|
self.hiddenMediaDisposable = context.sharedContext.mediaManager.galleryHiddenMediaManager.hiddenIds().startStrict(next: { [weak self] ids in
|
||||||
@ -13120,7 +13120,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||||||
if let allowsCustomTransition = other.allowsCustomTransition, !allowsCustomTransition() {
|
if let allowsCustomTransition = other.allowsCustomTransition, !allowsCustomTransition() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if let tag = other.userInfo as? PeerInfoNavigationSourceTag, tag.peerId == peerId {
|
if let tag = other.userInfo as? PeerInfoNavigationSourceTag, (tag.peerId == peerId || tag.threadId == peerId.toInt64()) {
|
||||||
return PeerInfoNavigationTransitionNode(screenNode: strongSelf.controllerNode, presentationData: strongSelf.presentationData, headerNode: strongSelf.controllerNode.headerNode)
|
return PeerInfoNavigationTransitionNode(screenNode: strongSelf.controllerNode, presentationData: strongSelf.presentationData, headerNode: strongSelf.controllerNode.headerNode)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -785,7 +785,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
|||||||
}, openBoostToUnrestrict: {
|
}, openBoostToUnrestrict: {
|
||||||
}, updateVideoTrimRange: { _, _, _, _ in
|
}, updateVideoTrimRange: { _, _, _, _ in
|
||||||
}, updateHistoryFilter: { _ in
|
}, updateHistoryFilter: { _ in
|
||||||
}, updateChatLocationThread: { _ in
|
}, updateChatLocationThread: { _, _ in
|
||||||
}, toggleChatSidebarMode: {
|
}, toggleChatSidebarMode: {
|
||||||
}, updateDisplayHistoryFilterAsList: { _ in
|
}, updateDisplayHistoryFilterAsList: { _ in
|
||||||
}, requestLayout: { _ in
|
}, requestLayout: { _ in
|
||||||
|
|||||||
@ -127,10 +127,21 @@ import PostSuggestionsSettingsScreen
|
|||||||
import ChatSendStarsScreen
|
import ChatSendStarsScreen
|
||||||
|
|
||||||
extension ChatControllerImpl {
|
extension ChatControllerImpl {
|
||||||
func reloadChatLocation() {
|
func reloadChatLocation(chatLocation: ChatLocation, isReady: Promise<Bool>?) {
|
||||||
|
self._chatLocationInfoReady.set(.single(false))
|
||||||
|
self.didSetChatLocationInfoReady = false
|
||||||
|
|
||||||
|
if let peerId = chatLocation.peerId, peerId != self.context.account.peerId {
|
||||||
|
switch subject {
|
||||||
|
case .pinnedMessages, .scheduledMessages, .messageOptions:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId, threadId: chatLocation.threadId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let context = self.context
|
let context = self.context
|
||||||
let chatLocation = self.chatLocation
|
let chatLocationPeerId: PeerId? = chatLocation.peerId
|
||||||
let chatLocationPeerId: PeerId? = self.chatLocation.peerId
|
|
||||||
let mode = self.mode
|
let mode = self.mode
|
||||||
let subject = self.subject
|
let subject = self.subject
|
||||||
let peerId = chatLocationPeerId
|
let peerId = chatLocationPeerId
|
||||||
@ -157,7 +168,7 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let managingBot: Signal<ChatManagingBot?, NoError>
|
let managingBot: Signal<ChatManagingBot?, NoError>
|
||||||
if let peerId = self.chatLocation.peerId, peerId.namespace == Namespaces.Peer.CloudUser {
|
if let peerId = chatLocation.peerId, peerId.namespace == Namespaces.Peer.CloudUser {
|
||||||
managingBot = self.context.engine.data.subscribe(
|
managingBot = self.context.engine.data.subscribe(
|
||||||
TelegramEngine.EngineData.Item.Peer.ChatManagingBot(id: peerId)
|
TelegramEngine.EngineData.Item.Peer.ChatManagingBot(id: peerId)
|
||||||
)
|
)
|
||||||
@ -505,7 +516,7 @@ extension ChatControllerImpl {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
let threadInfo: Signal<EngineMessageHistoryThread.Info?, NoError>
|
let threadInfo: Signal<EngineMessageHistoryThread.Info?, NoError>
|
||||||
if let threadId = self.chatLocation.threadId {
|
if let threadId = chatLocation.threadId {
|
||||||
let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: threadId)
|
let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: threadId)
|
||||||
threadInfo = context.account.postbox.combinedView(keys: [viewKey])
|
threadInfo = context.account.postbox.combinedView(keys: [viewKey])
|
||||||
|> map { views -> EngineMessageHistoryThread.Info? in
|
|> map { views -> EngineMessageHistoryThread.Info? in
|
||||||
@ -523,9 +534,9 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let hasSearchTags: Signal<Bool, NoError>
|
let hasSearchTags: Signal<Bool, NoError>
|
||||||
if let peerId = self.chatLocation.peerId, peerId == context.account.peerId {
|
if let peerId = chatLocation.peerId, peerId == context.account.peerId {
|
||||||
hasSearchTags = context.engine.data.subscribe(
|
hasSearchTags = context.engine.data.subscribe(
|
||||||
TelegramEngine.EngineData.Item.Messages.SavedMessageTagStats(peerId: context.account.peerId, threadId: self.chatLocation.threadId)
|
TelegramEngine.EngineData.Item.Messages.SavedMessageTagStats(peerId: context.account.peerId, threadId: chatLocation.threadId)
|
||||||
)
|
)
|
||||||
|> map { tags -> Bool in
|
|> map { tags -> Bool in
|
||||||
return !tags.isEmpty
|
return !tags.isEmpty
|
||||||
@ -536,14 +547,14 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let hasSavedChats: Signal<Bool, NoError>
|
let hasSavedChats: Signal<Bool, NoError>
|
||||||
if case .peer(context.account.peerId) = self.chatLocation {
|
if case .peer(context.account.peerId) = chatLocation {
|
||||||
hasSavedChats = context.engine.messages.savedMessagesHasPeersOtherThanSaved()
|
hasSavedChats = context.engine.messages.savedMessagesHasPeersOtherThanSaved()
|
||||||
} else {
|
} else {
|
||||||
hasSavedChats = .single(false)
|
hasSavedChats = .single(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
let isPremiumRequiredForMessaging: Signal<Bool, NoError>
|
let isPremiumRequiredForMessaging: Signal<Bool, NoError>
|
||||||
if let peerId = self.chatLocation.peerId {
|
if let peerId = chatLocation.peerId {
|
||||||
isPremiumRequiredForMessaging = context.engine.peers.subscribeIsPremiumRequiredForMessaging(id: peerId)
|
isPremiumRequiredForMessaging = context.engine.peers.subscribeIsPremiumRequiredForMessaging(id: peerId)
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
} else {
|
} else {
|
||||||
@ -558,7 +569,7 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let displayedPeerVerification: Signal<Bool, NoError>
|
let displayedPeerVerification: Signal<Bool, NoError>
|
||||||
if let peerId = self.chatLocation.peerId {
|
if let peerId = chatLocation.peerId {
|
||||||
displayedPeerVerification = ApplicationSpecificNotice.displayedPeerVerification(accountManager: context.sharedContext.accountManager, peerId: peerId)
|
displayedPeerVerification = ApplicationSpecificNotice.displayedPeerVerification(accountManager: context.sharedContext.accountManager, peerId: peerId)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
} else {
|
} else {
|
||||||
@ -906,7 +917,7 @@ extension ChatControllerImpl {
|
|||||||
boostsToUnrestrict = cachedChannelData.boostsToUnrestrict
|
boostsToUnrestrict = cachedChannelData.boostsToUnrestrict
|
||||||
}
|
}
|
||||||
|
|
||||||
if strongSelf.premiumOrStarsRequiredDisposable == nil, sendPaidMessageStars != nil, let peerId = strongSelf.chatLocation.peerId {
|
if strongSelf.premiumOrStarsRequiredDisposable == nil, sendPaidMessageStars != nil, let peerId = chatLocation.peerId {
|
||||||
strongSelf.premiumOrStarsRequiredDisposable = ((strongSelf.context.engine.peers.isPremiumRequiredToContact([peerId]) |> then(.complete() |> suspendAwareDelay(60.0, queue: Queue.concurrentDefaultQueue()))) |> restart).startStandalone()
|
strongSelf.premiumOrStarsRequiredDisposable = ((strongSelf.context.engine.peers.isPremiumRequiredToContact([peerId]) |> then(.complete() |> suspendAwareDelay(60.0, queue: Queue.concurrentDefaultQueue()))) |> restart).startStandalone()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -992,7 +1003,7 @@ extension ChatControllerImpl {
|
|||||||
} else {
|
} else {
|
||||||
isRegularChat = true
|
isRegularChat = true
|
||||||
}
|
}
|
||||||
if strongSelf.nextChannelToReadDisposable == nil, let peerId = strongSelf.chatLocation.peerId, let customChatNavigationStack = strongSelf.customChatNavigationStack {
|
if strongSelf.nextChannelToReadDisposable == nil, let peerId = chatLocation.peerId, let customChatNavigationStack = strongSelf.customChatNavigationStack {
|
||||||
if let index = customChatNavigationStack.firstIndex(of: peerId), index != customChatNavigationStack.count - 1 {
|
if let index = customChatNavigationStack.firstIndex(of: peerId), index != customChatNavigationStack.count - 1 {
|
||||||
let nextPeerId = customChatNavigationStack[index + 1]
|
let nextPeerId = customChatNavigationStack[index + 1]
|
||||||
strongSelf.nextChannelToReadDisposable = (combineLatest(queue: .mainQueue(),
|
strongSelf.nextChannelToReadDisposable = (combineLatest(queue: .mainQueue(),
|
||||||
@ -1074,6 +1085,7 @@ extension ChatControllerImpl {
|
|||||||
strongSelf.didSetChatLocationInfoReady = true
|
strongSelf.didSetChatLocationInfoReady = true
|
||||||
strongSelf._chatLocationInfoReady.set(.single(true))
|
strongSelf._chatLocationInfoReady.set(.single(true))
|
||||||
}
|
}
|
||||||
|
isReady?.set(.single(true))
|
||||||
strongSelf.updateReminderActivity()
|
strongSelf.updateReminderActivity()
|
||||||
if let upgradedToPeerId = upgradedToPeerId {
|
if let upgradedToPeerId = upgradedToPeerId {
|
||||||
if let navigationController = strongSelf.effectiveNavigationController {
|
if let navigationController = strongSelf.effectiveNavigationController {
|
||||||
@ -1267,9 +1279,9 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let hasSearchTags: Signal<Bool, NoError>
|
let hasSearchTags: Signal<Bool, NoError>
|
||||||
if let peerId = self.chatLocation.peerId, peerId == context.account.peerId {
|
if let peerId = chatLocation.peerId, peerId == context.account.peerId {
|
||||||
hasSearchTags = context.engine.data.subscribe(
|
hasSearchTags = context.engine.data.subscribe(
|
||||||
TelegramEngine.EngineData.Item.Messages.SavedMessageTagStats(peerId: context.account.peerId, threadId: self.chatLocation.threadId)
|
TelegramEngine.EngineData.Item.Messages.SavedMessageTagStats(peerId: context.account.peerId, threadId: chatLocation.threadId)
|
||||||
)
|
)
|
||||||
|> map { tags -> Bool in
|
|> map { tags -> Bool in
|
||||||
return !tags.isEmpty
|
return !tags.isEmpty
|
||||||
@ -1280,14 +1292,14 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let hasSavedChats: Signal<Bool, NoError>
|
let hasSavedChats: Signal<Bool, NoError>
|
||||||
if case .peer(context.account.peerId) = self.chatLocation {
|
if case .peer(context.account.peerId) = chatLocation {
|
||||||
hasSavedChats = context.engine.messages.savedMessagesHasPeersOtherThanSaved()
|
hasSavedChats = context.engine.messages.savedMessagesHasPeersOtherThanSaved()
|
||||||
} else {
|
} else {
|
||||||
hasSavedChats = .single(false)
|
hasSavedChats = .single(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
let isPremiumRequiredForMessaging: Signal<Bool, NoError>
|
let isPremiumRequiredForMessaging: Signal<Bool, NoError>
|
||||||
if let peerId = self.chatLocation.peerId {
|
if let peerId = chatLocation.peerId {
|
||||||
isPremiumRequiredForMessaging = context.engine.peers.subscribeIsPremiumRequiredForMessaging(id: peerId)
|
isPremiumRequiredForMessaging = context.engine.peers.subscribeIsPremiumRequiredForMessaging(id: peerId)
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
} else {
|
} else {
|
||||||
@ -1456,8 +1468,8 @@ extension ChatControllerImpl {
|
|||||||
strongSelf.chatTitleView?.titleContent = .peer(peerView: ChatTitleContent.PeerData(peerView: peerView), customTitle: threadInfo.title, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: peerIsMuted, customMessageCount: messageAndTopic.messageCount == 0 ? nil : messageAndTopic.messageCount, isEnabled: true)
|
strongSelf.chatTitleView?.titleContent = .peer(peerView: ChatTitleContent.PeerData(peerView: peerView), customTitle: threadInfo.title, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: peerIsMuted, customMessageCount: messageAndTopic.messageCount == 0 ? nil : messageAndTopic.messageCount, isEnabled: true)
|
||||||
|
|
||||||
let avatarContent: EmojiStatusComponent.Content
|
let avatarContent: EmojiStatusComponent.Content
|
||||||
if strongSelf.chatLocation.threadId == 1 {
|
if chatLocation.threadId == 1 {
|
||||||
avatarContent = .image(image: PresentationResourcesChat.chatGeneralThreadIcon(strongSelf.presentationData.theme))
|
avatarContent = .image(image: PresentationResourcesChat.chatGeneralThreadIcon(strongSelf.presentationData.theme), tintColor: nil)
|
||||||
} else if let fileId = threadInfo.icon {
|
} else if let fileId = threadInfo.icon {
|
||||||
avatarContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: strongSelf.presentationData.theme.list.mediaPlaceholderColor, themeColor: strongSelf.presentationData.theme.list.itemAccentColor, loopMode: .count(1))
|
avatarContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: strongSelf.presentationData.theme.list.mediaPlaceholderColor, themeColor: strongSelf.presentationData.theme.list.itemAccentColor, loopMode: .count(1))
|
||||||
} else {
|
} else {
|
||||||
@ -1633,7 +1645,7 @@ extension ChatControllerImpl {
|
|||||||
boostsToUnrestrict = cachedChannelData.boostsToUnrestrict
|
boostsToUnrestrict = cachedChannelData.boostsToUnrestrict
|
||||||
}
|
}
|
||||||
|
|
||||||
if strongSelf.premiumOrStarsRequiredDisposable == nil, sendPaidMessageStars != nil, let peerId = strongSelf.chatLocation.peerId {
|
if strongSelf.premiumOrStarsRequiredDisposable == nil, sendPaidMessageStars != nil, let peerId = chatLocation.peerId {
|
||||||
strongSelf.premiumOrStarsRequiredDisposable = ((strongSelf.context.engine.peers.isPremiumRequiredToContact([peerId]) |> then(.complete() |> suspendAwareDelay(60.0, queue: Queue.concurrentDefaultQueue()))) |> restart).startStandalone()
|
strongSelf.premiumOrStarsRequiredDisposable = ((strongSelf.context.engine.peers.isPremiumRequiredToContact([peerId]) |> then(.complete() |> suspendAwareDelay(60.0, queue: Queue.concurrentDefaultQueue()))) |> restart).startStandalone()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1709,6 +1721,7 @@ extension ChatControllerImpl {
|
|||||||
strongSelf.didSetChatLocationInfoReady = true
|
strongSelf.didSetChatLocationInfoReady = true
|
||||||
strongSelf._chatLocationInfoReady.set(.single(true))
|
strongSelf._chatLocationInfoReady.set(.single(true))
|
||||||
}
|
}
|
||||||
|
isReady?.set(.single(true))
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
} else if case .customChatContents = self.chatLocationInfoData {
|
} else if case .customChatContents = self.chatLocationInfoData {
|
||||||
@ -1785,6 +1798,7 @@ extension ChatControllerImpl {
|
|||||||
self.didSetChatLocationInfoReady = true
|
self.didSetChatLocationInfoReady = true
|
||||||
self._chatLocationInfoReady.set(.single(true))
|
self._chatLocationInfoReady.set(.single(true))
|
||||||
}
|
}
|
||||||
|
isReady?.set(.single(true))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2729,9 +2743,10 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mappedTransition = (ChatHistoryListViewTransition(historyView: transition.historyView, deleteItems: deleteItems, insertItems: insertItems, updateItems: transition.updateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: stationaryItemRange, initialData: transition.initialData, keyboardButtonsMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData, scrolledToIndex: transition.scrolledToIndex, scrolledToSomeIndex: transition.scrolledToSomeIndex, peerType: transition.peerType, networkType: transition.networkType, animateIn: false, reason: transition.reason, flashIndicators: transition.flashIndicators, animateFromPreviousFilter: false), updateSizeAndInsets)
|
mappedTransition = (ChatHistoryListViewTransition(historyView: transition.historyView, deleteItems: deleteItems, insertItems: insertItems, updateItems: transition.updateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: stationaryItemRange, initialData: transition.initialData, keyboardButtonsMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData, scrolledToIndex: transition.scrolledToIndex, scrolledToSomeIndex: transition.scrolledToSomeIndex, peerType: transition.peerType, networkType: transition.networkType, animateIn: false, reason: transition.reason, flashIndicators: transition.flashIndicators, animateFromPreviousFilter: false), updateSizeAndInsets)
|
||||||
}, updateExtraNavigationBarBackgroundHeight: { value, hitTestSlop, _ in
|
}, updateExtraNavigationBarBackgroundHeight: { value, hitTestSlop, cutout, _ in
|
||||||
strongSelf.additionalNavigationBarBackgroundHeight = value
|
strongSelf.additionalNavigationBarBackgroundHeight = value
|
||||||
strongSelf.additionalNavigationBarHitTestSlop = hitTestSlop
|
strongSelf.additionalNavigationBarHitTestSlop = hitTestSlop
|
||||||
|
strongSelf.additionalNavigationBarCutout = cutout
|
||||||
})
|
})
|
||||||
|
|
||||||
if let mappedTransition = mappedTransition {
|
if let mappedTransition = mappedTransition {
|
||||||
@ -6041,44 +6056,14 @@ extension ChatControllerImpl {
|
|||||||
} else {
|
} else {
|
||||||
apply()
|
apply()
|
||||||
}
|
}
|
||||||
}, updateChatLocationThread: { [weak self] threadId in
|
}, updateChatLocationThread: { [weak self] threadId, animationDirection in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let peerId = self.chatLocation.peerId else {
|
let defaultDirection: ChatControllerAnimateInnerChatSwitchDirection? = self.chatDisplayNode.chatLocationTabSwitchDirection(from: self.chatLocation.threadId, to: threadId).flatMap { direction -> ChatControllerAnimateInnerChatSwitchDirection in
|
||||||
return
|
return direction ? .right : .left
|
||||||
}
|
}
|
||||||
guard let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer else {
|
self.updateChatLocationThread(threadId: threadId, animationDirection: animationDirection ?? defaultDirection)
|
||||||
return
|
|
||||||
}
|
|
||||||
let updatedChatLocation: ChatLocation
|
|
||||||
if let threadId {
|
|
||||||
var isMonoforum = false
|
|
||||||
if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) {
|
|
||||||
isMonoforum = true
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedChatLocation = .replyThread(message: ChatReplyThreadMessage(
|
|
||||||
peerId: peerId,
|
|
||||||
threadId: threadId,
|
|
||||||
channelMessageId: nil,
|
|
||||||
isChannelPost: false,
|
|
||||||
isForumPost: true,
|
|
||||||
isMonoforumPost: isMonoforum,
|
|
||||||
maxMessage: nil,
|
|
||||||
maxReadIncomingMessageId: nil,
|
|
||||||
maxReadOutgoingMessageId: nil,
|
|
||||||
unreadCount: 0,
|
|
||||||
initialFilledHoles: IndexSet(),
|
|
||||||
initialAnchor: .automatic,
|
|
||||||
isNotAvailable: false
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
updatedChatLocation = .peer(id: peerId)
|
|
||||||
}
|
|
||||||
self.updateChatPresentationInterfaceState(animated: true, interactive: false, { presentationInterfaceState in
|
|
||||||
return presentationInterfaceState.updatedChatLocation(updatedChatLocation)
|
|
||||||
})
|
|
||||||
}, toggleChatSidebarMode: { [weak self] in
|
}, toggleChatSidebarMode: { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
|
|||||||
@ -426,6 +426,23 @@ extension ChatControllerImpl {
|
|||||||
if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: peer, mode: .forumTopic(thread: replyThreadMessage), avatarInitiallyExpanded: false, fromChat: true, requestsContext: nil) {
|
if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: peer, mode: .forumTopic(thread: replyThreadMessage), avatarInitiallyExpanded: false, fromChat: true, requestsContext: nil) {
|
||||||
self.effectiveNavigationController?.pushViewController(infoController)
|
self.effectiveNavigationController?.pushViewController(infoController)
|
||||||
}
|
}
|
||||||
|
} else if let peer = self.presentationInterfaceState.renderedPeer?.peer, case let .replyThread(replyThreadMessage) = self.chatLocation, peer.isMonoForum {
|
||||||
|
let context = self.context
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
Task { @MainActor [weak self] in
|
||||||
|
guard let peer = await context.engine.data.get(
|
||||||
|
TelegramEngine.EngineData.Item.Peer.Peer(id: PeerId(replyThreadMessage.threadId))
|
||||||
|
).get() else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: true, requestsContext: nil) {
|
||||||
|
self.effectiveNavigationController?.pushViewController(infoController)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, case let .replyThread(message) = self.chatLocation {
|
} else if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, case let .replyThread(message) = self.chatLocation {
|
||||||
if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: channel, mode: .forumTopic(thread: message), avatarInitiallyExpanded: false, fromChat: true, requestsContext: self.inviteRequestsContext) {
|
if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: channel, mode: .forumTopic(thread: message), avatarInitiallyExpanded: false, fromChat: true, requestsContext: self.inviteRequestsContext) {
|
||||||
self.effectiveNavigationController?.pushViewController(infoController)
|
self.effectiveNavigationController?.pushViewController(infoController)
|
||||||
|
|||||||
@ -437,7 +437,10 @@ func updateChatPresentationInterfaceStateImpl(
|
|||||||
selfController.presentationInterfaceState = updatedChatPresentationInterfaceState
|
selfController.presentationInterfaceState = updatedChatPresentationInterfaceState
|
||||||
|
|
||||||
if selfController.chatDisplayNode.chatLocation != selfController.presentationInterfaceState.chatLocation {
|
if selfController.chatDisplayNode.chatLocation != selfController.presentationInterfaceState.chatLocation {
|
||||||
let tabSwitchDirection = selfController.chatDisplayNode.chatLocationTabSwitchDirection(from: selfController.chatDisplayNode.chatLocation, to: selfController.presentationInterfaceState.chatLocation)
|
let defaultDirection: ChatControllerAnimateInnerChatSwitchDirection? = selfController.chatDisplayNode.chatLocationTabSwitchDirection(from: selfController.chatLocation.threadId, to: selfController.presentationInterfaceState.chatLocation.threadId).flatMap { direction -> ChatControllerAnimateInnerChatSwitchDirection in
|
||||||
|
return direction ? .right : .left
|
||||||
|
}
|
||||||
|
let tabSwitchDirection = selfController.currentChatSwitchDirection ?? defaultDirection
|
||||||
selfController.chatDisplayNode.updateChatLocation(chatLocation: selfController.presentationInterfaceState.chatLocation, transition: transition, tabSwitchDirection: tabSwitchDirection)
|
selfController.chatDisplayNode.updateChatLocation(chatLocation: selfController.presentationInterfaceState.chatLocation, transition: transition, tabSwitchDirection: tabSwitchDirection)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -488,17 +491,18 @@ func updateChatPresentationInterfaceStateImpl(
|
|||||||
animated = false
|
animated = false
|
||||||
}
|
}
|
||||||
animated = false
|
animated = false
|
||||||
selfController.navigationItem.setLeftBarButton(button.buttonItem, animated: animated)
|
selfController.navigationItem.setLeftBarButton(button.buttonItem, animated: animated && selfController.currentChatSwitchDirection == nil)
|
||||||
selfController.leftNavigationButton = button
|
selfController.leftNavigationButton = button
|
||||||
}
|
}
|
||||||
} else if let _ = selfController.leftNavigationButton {
|
} else if let _ = selfController.leftNavigationButton {
|
||||||
selfController.navigationItem.setLeftBarButton(nil, animated: transition.isAnimated)
|
selfController.navigationItem.setLeftBarButton(nil, animated: transition.isAnimated && selfController.currentChatSwitchDirection == nil)
|
||||||
selfController.leftNavigationButton = nil
|
selfController.leftNavigationButton = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if let channel = selfController.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum {
|
|
||||||
} else {*/
|
|
||||||
var buttonsAnimated = transition.isAnimated
|
var buttonsAnimated = transition.isAnimated
|
||||||
|
if selfController.currentChatSwitchDirection != nil {
|
||||||
|
buttonsAnimated = false
|
||||||
|
}
|
||||||
if let button = rightNavigationButtonForChatInterfaceState(context: selfController.context, presentationInterfaceState: updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: selfController.rightNavigationButton, target: selfController, selector: #selector(selfController.rightNavigationButtonAction), chatInfoNavigationButton: selfController.chatInfoNavigationButton, moreInfoNavigationButton: selfController.moreInfoNavigationButton) {
|
if let button = rightNavigationButtonForChatInterfaceState(context: selfController.context, presentationInterfaceState: updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: selfController.rightNavigationButton, target: selfController, selector: #selector(selfController.rightNavigationButtonAction), chatInfoNavigationButton: selfController.chatInfoNavigationButton, moreInfoNavigationButton: selfController.moreInfoNavigationButton) {
|
||||||
if selfController.rightNavigationButton != button {
|
if selfController.rightNavigationButton != button {
|
||||||
if let currentButton = selfController.rightNavigationButton?.action, currentButton == button.action {
|
if let currentButton = selfController.rightNavigationButton?.action, currentButton == button.action {
|
||||||
@ -601,7 +605,6 @@ func updateChatPresentationInterfaceStateImpl(
|
|||||||
|
|
||||||
if previousChatLocation != selfController.presentationInterfaceState.chatLocation {
|
if previousChatLocation != selfController.presentationInterfaceState.chatLocation {
|
||||||
selfController.chatLocation = selfController.presentationInterfaceState.chatLocation
|
selfController.chatLocation = selfController.presentationInterfaceState.chatLocation
|
||||||
selfController.reloadChatLocation()
|
|
||||||
selfController.reloadCachedData()
|
selfController.reloadCachedData()
|
||||||
selfController.setupChatHistoryNode()
|
selfController.setupChatHistoryNode()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -233,7 +233,7 @@ struct ScrolledToMessageId: Equatable {
|
|||||||
var allowedReplacementDirection: AllowedReplacementDirections
|
var allowedReplacementDirection: AllowedReplacementDirections
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class ChatControllerImpl: TelegramBaseController, ChatController, GalleryHiddenMediaTarget, UIDropInteractionDelegate {
|
public final class ChatControllerImpl: TelegramBaseController, ChatController, GalleryHiddenMediaTarget, UIDropInteractionDelegate {
|
||||||
var validLayout: ContainerViewLayout?
|
var validLayout: ContainerViewLayout?
|
||||||
|
|
||||||
public weak var parentController: ViewController?
|
public weak var parentController: ViewController?
|
||||||
@ -2059,7 +2059,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self?.navigateToMessage(from: nil, to: .id(id, NavigateToMessageParams(timestamp: nil, quote: nil)), forceInCurrentChat: false)
|
self?.navigateToMessage(from: nil, to: .id(id, NavigateToMessageParams(timestamp: nil, quote: nil)), forceInCurrentChat: false)
|
||||||
}, navigateToThreadMessage: { [weak self] peerId, threadId, messageId in
|
}, navigateToThreadMessage: { [weak self] peerId, threadId, messageId in
|
||||||
if let context = self?.context, let navigationController = self?.effectiveNavigationController {
|
if let context = self?.context, let navigationController = self?.effectiveNavigationController {
|
||||||
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: threadId, messageId: messageId, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .always).startStandalone()
|
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: threadId, messageId: messageId, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .always, animated: true).startStandalone()
|
||||||
}
|
}
|
||||||
}, tapMessage: nil, clickThroughMessage: { [weak self] view, location in
|
}, tapMessage: nil, clickThroughMessage: { [weak self] view, location in
|
||||||
self?.chatDisplayNode.dismissInput(view: view, location: location)
|
self?.chatDisplayNode.dismissInput(view: view, location: location)
|
||||||
@ -4828,38 +4828,31 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.displayQuickShare(id: messageId, node: node, gesture: gesture)
|
self.displayQuickShare(id: messageId, node: node, gesture: gesture)
|
||||||
}, updateChatLocationThread: { [weak self] threadId in
|
}, updateChatLocationThread: { [weak self] threadId, animationDirection in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.interfaceInteraction?.updateChatLocationThread(threadId)
|
let defaultDirection: ChatControllerAnimateInnerChatSwitchDirection? = self.chatDisplayNode.chatLocationTabSwitchDirection(from: self.chatLocation.threadId, to: self.presentationInterfaceState.chatLocation.threadId).flatMap { direction -> ChatControllerAnimateInnerChatSwitchDirection in
|
||||||
|
return direction ? .right : .left
|
||||||
|
}
|
||||||
|
self.updateChatLocationThread(threadId: threadId, animationDirection: animationDirection ?? defaultDirection)
|
||||||
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: self.stickerSettings, presentationContext: ChatPresentationContext(context: context, backgroundNode: self.chatBackgroundNode))
|
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: self.stickerSettings, presentationContext: ChatPresentationContext(context: context, backgroundNode: self.chatBackgroundNode))
|
||||||
controllerInteraction.enableFullTranslucency = context.sharedContext.energyUsageSettings.fullTranslucency
|
controllerInteraction.enableFullTranslucency = context.sharedContext.energyUsageSettings.fullTranslucency
|
||||||
|
|
||||||
self.controllerInteraction = controllerInteraction
|
self.controllerInteraction = controllerInteraction
|
||||||
|
|
||||||
//if chatLocation.threadId == nil {
|
self.navigationBar?.allowsCustomTransition = { [weak self] in
|
||||||
if let peerId = chatLocation.peerId, peerId != context.account.peerId {
|
guard let strongSelf = self else {
|
||||||
switch subject {
|
return false
|
||||||
case .pinnedMessages, .scheduledMessages, .messageOptions:
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
self.navigationBar?.userInfo = PeerInfoNavigationSourceTag(peerId: peerId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.navigationBar?.allowsCustomTransition = { [weak self] in
|
if strongSelf.navigationBar?.userInfo == nil {
|
||||||
guard let strongSelf = self else {
|
return false
|
||||||
return false
|
|
||||||
}
|
|
||||||
if strongSelf.navigationBar?.userInfo == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if strongSelf.navigationBar?.contentNode != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
//}
|
if strongSelf.navigationBar?.contentNode != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
self.chatTitleView = ChatTitleView(context: self.context, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, animationCache: controllerInteraction.presentationContext.animationCache, animationRenderer: controllerInteraction.presentationContext.animationRenderer)
|
self.chatTitleView = ChatTitleView(context: self.context, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, animationCache: controllerInteraction.presentationContext.animationCache, animationRenderer: controllerInteraction.presentationContext.animationRenderer)
|
||||||
|
|
||||||
@ -5306,7 +5299,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
self.reloadChatLocation()
|
self.reloadChatLocation(chatLocation: self.chatLocation, isReady: nil)
|
||||||
|
|
||||||
self.botCallbackAlertMessageDisposable = (self.botCallbackAlertMessage.get()
|
self.botCallbackAlertMessageDisposable = (self.botCallbackAlertMessage.get()
|
||||||
|> deliverOnMainQueue).startStrict(next: { [weak self] message in
|
|> deliverOnMainQueue).startStrict(next: { [weak self] message in
|
||||||
@ -5910,6 +5903,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self.networkSpeedEventsDisposable?.dispose()
|
self.networkSpeedEventsDisposable?.dispose()
|
||||||
self.postedScheduledMessagesEventsDisposable?.dispose()
|
self.postedScheduledMessagesEventsDisposable?.dispose()
|
||||||
self.premiumOrStarsRequiredDisposable?.dispose()
|
self.premiumOrStarsRequiredDisposable?.dispose()
|
||||||
|
self.updateChatLocationThreadDisposable?.dispose()
|
||||||
}
|
}
|
||||||
deallocate()
|
deallocate()
|
||||||
}
|
}
|
||||||
@ -7076,13 +7070,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
var suspendedNavigationBarLayout: ContainerViewLayout?
|
var suspendedNavigationBarLayout: ContainerViewLayout?
|
||||||
var additionalNavigationBarBackgroundHeight: CGFloat = 0.0
|
var additionalNavigationBarBackgroundHeight: CGFloat = 0.0
|
||||||
var additionalNavigationBarHitTestSlop: CGFloat = 0.0
|
var additionalNavigationBarHitTestSlop: CGFloat = 0.0
|
||||||
|
var additionalNavigationBarCutout: CGSize?
|
||||||
|
|
||||||
override public func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
override public func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
if self.suspendNavigationBarLayout {
|
if self.suspendNavigationBarLayout {
|
||||||
self.suspendedNavigationBarLayout = layout
|
self.suspendedNavigationBarLayout = layout
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: transition)
|
self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, additionalCutout: self.additionalNavigationBarCutout, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func preferredContentSizeForLayout(_ layout: ContainerViewLayout) -> CGSize? {
|
override public func preferredContentSizeForLayout(_ layout: ContainerViewLayout) -> CGSize? {
|
||||||
@ -7120,10 +7115,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
var navigationBarTransition = transition
|
var navigationBarTransition = transition
|
||||||
self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in
|
self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in
|
||||||
self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion)
|
self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion)
|
||||||
}, updateExtraNavigationBarBackgroundHeight: { value, hitTestSlop, extraNavigationTransition in
|
}, updateExtraNavigationBarBackgroundHeight: { value, hitTestSlop, cutout, extraNavigationTransition in
|
||||||
navigationBarTransition = extraNavigationTransition
|
navigationBarTransition = extraNavigationTransition
|
||||||
self.additionalNavigationBarBackgroundHeight = value
|
self.additionalNavigationBarBackgroundHeight = value
|
||||||
self.additionalNavigationBarHitTestSlop = hitTestSlop
|
self.additionalNavigationBarHitTestSlop = hitTestSlop
|
||||||
|
self.additionalNavigationBarCutout = cutout
|
||||||
})
|
})
|
||||||
|
|
||||||
if case .compact = layout.metrics.widthClass {
|
if case .compact = layout.metrics.widthClass {
|
||||||
@ -7142,7 +7138,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self.suspendNavigationBarLayout = false
|
self.suspendNavigationBarLayout = false
|
||||||
if let suspendedNavigationBarLayout = self.suspendedNavigationBarLayout {
|
if let suspendedNavigationBarLayout = self.suspendedNavigationBarLayout {
|
||||||
self.suspendedNavigationBarLayout = suspendedNavigationBarLayout
|
self.suspendedNavigationBarLayout = suspendedNavigationBarLayout
|
||||||
self.applyNavigationBarLayout(suspendedNavigationBarLayout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: navigationBarTransition)
|
self.applyNavigationBarLayout(suspendedNavigationBarLayout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, additionalCutout: self.additionalNavigationBarCutout, transition: navigationBarTransition)
|
||||||
}
|
}
|
||||||
self.navigationBar?.additionalContentNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: 0.0, bottom: self.additionalNavigationBarHitTestSlop, right: 0.0)
|
self.navigationBar?.additionalContentNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: 0.0, bottom: self.additionalNavigationBarHitTestSlop, right: 0.0)
|
||||||
}
|
}
|
||||||
@ -9819,6 +9815,122 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var updateChatLocationThreadDisposable: Disposable?
|
||||||
|
private var isUpdatingChatLocationThread: Bool = false
|
||||||
|
var currentChatSwitchDirection: ChatControllerAnimateInnerChatSwitchDirection?
|
||||||
|
|
||||||
|
public func updateChatLocationThread(threadId: Int64?, animationDirection: ChatControllerAnimateInnerChatSwitchDirection? = nil) {
|
||||||
|
if self.isUpdatingChatLocationThread {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let peerId = self.chatLocation.peerId else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if self.chatLocation.threadId == threadId {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let navigationSnapshot = self.chatTitleView?.prepareSnapshotState()
|
||||||
|
|
||||||
|
let rightBarButtonItemSnapshots: [(UIView, CGRect)] = (self.navigationItem.rightBarButtonItems ?? []).compactMap { item -> (UIView, CGRect)? in
|
||||||
|
guard let view = item.customDisplayNode?.view, let snapshotView = view.snapshotView(afterScreenUpdates: false) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return (snapshotView, view.convert(view.bounds, to: self.view))
|
||||||
|
}
|
||||||
|
|
||||||
|
let updatedChatLocation: ChatLocation
|
||||||
|
if let threadId {
|
||||||
|
var isMonoforum = false
|
||||||
|
if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) {
|
||||||
|
isMonoforum = true
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedChatLocation = .replyThread(message: ChatReplyThreadMessage(
|
||||||
|
peerId: peerId,
|
||||||
|
threadId: threadId,
|
||||||
|
channelMessageId: nil,
|
||||||
|
isChannelPost: false,
|
||||||
|
isForumPost: true,
|
||||||
|
isMonoforumPost: isMonoforum,
|
||||||
|
maxMessage: nil,
|
||||||
|
maxReadIncomingMessageId: nil,
|
||||||
|
maxReadOutgoingMessageId: nil,
|
||||||
|
unreadCount: 0,
|
||||||
|
initialFilledHoles: IndexSet(),
|
||||||
|
initialAnchor: .automatic,
|
||||||
|
isNotAvailable: false
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
updatedChatLocation = .peer(id: peerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
let isReady = Promise<Bool>()
|
||||||
|
self.reloadChatLocation(chatLocation: updatedChatLocation, isReady: isReady)
|
||||||
|
|
||||||
|
self.isUpdatingChatLocationThread = true
|
||||||
|
self.updateChatLocationThreadDisposable?.dispose()
|
||||||
|
self.updateChatLocationThreadDisposable = (isReady.get()
|
||||||
|
|> filter { $0 }
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).startStrict(next: { [weak self] _ in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.isUpdatingChatLocationThread = false
|
||||||
|
|
||||||
|
self.currentChatSwitchDirection = animationDirection
|
||||||
|
self.updateChatPresentationInterfaceState(animated: animationDirection != nil, interactive: false, { presentationInterfaceState in
|
||||||
|
return presentationInterfaceState.updatedChatLocation(updatedChatLocation)
|
||||||
|
})
|
||||||
|
|
||||||
|
if let navigationSnapshot, let animationDirection {
|
||||||
|
let mappedAnimationDirection: ChatTitleView.AnimateFromSnapshotDirection
|
||||||
|
switch animationDirection {
|
||||||
|
case .up:
|
||||||
|
mappedAnimationDirection = .up
|
||||||
|
case .down:
|
||||||
|
mappedAnimationDirection = .down
|
||||||
|
case .left:
|
||||||
|
mappedAnimationDirection = .left
|
||||||
|
case .right:
|
||||||
|
mappedAnimationDirection = .right
|
||||||
|
}
|
||||||
|
|
||||||
|
self.chatTitleView?.animateFromSnapshot(navigationSnapshot, direction: mappedAnimationDirection)
|
||||||
|
if let rightBarButtonItems = self.navigationItem.rightBarButtonItems {
|
||||||
|
for i in 0 ..< rightBarButtonItems.count {
|
||||||
|
let item = rightBarButtonItems[i]
|
||||||
|
if let customDisplayNode = item.customDisplayNode {
|
||||||
|
customDisplayNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
|
//customDisplayNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
|
||||||
|
let _ = rightBarButtonItemSnapshots
|
||||||
|
/*if i < rightBarButtonItemSnapshots.count {
|
||||||
|
let (snapshotItem, snapshotFrame) = rightBarButtonItemSnapshots[i]
|
||||||
|
if let targetSuperview = customDisplayNode.view.superview {
|
||||||
|
snapshotItem.frame = targetSuperview.convert(snapshotFrame, from: self.view)
|
||||||
|
targetSuperview.addSubview(snapshotItem)
|
||||||
|
|
||||||
|
snapshotItem.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, completion: { [weak snapshotItem] _ in
|
||||||
|
snapshotItem?.removeFromSuperview()
|
||||||
|
})
|
||||||
|
snapshotItem.layer.animateScale(from: 1.0, to: 0.1, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.currentChatSwitchDirection = nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
public var contentContainerNode: ASDisplayNode {
|
public var contentContainerNode: ASDisplayNode {
|
||||||
return self.chatDisplayNode.contentContainerNode
|
return self.chatDisplayNode.contentContainerNode
|
||||||
}
|
}
|
||||||
|
|||||||
@ -351,7 +351,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
|
|
||||||
if let (layout, navigationHeight) = self.validLayout {
|
if let (layout, navigationHeight) = self.validLayout {
|
||||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .immediate, listViewTransaction: { _, _, _, _ in
|
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .immediate, listViewTransaction: { _, _, _, _ in
|
||||||
}, updateExtraNavigationBarBackgroundHeight: { _, _, _ in
|
}, updateExtraNavigationBarBackgroundHeight: { _, _, _, _ in
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1033,7 +1033,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
self.wrappingNode.update(size: layout.size, cornerRadius: layout.deviceMetrics.screenCornerRadius, transition: .immediate)
|
self.wrappingNode.update(size: layout.size, cornerRadius: layout.deviceMetrics.screenCornerRadius, transition: .immediate)
|
||||||
}
|
}
|
||||||
|
|
||||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition protoTransition: ContainedViewLayoutTransition, listViewTransaction: (ListViewUpdateSizeAndInsets, CGFloat, Bool, @escaping () -> Void) -> Void, updateExtraNavigationBarBackgroundHeight: (CGFloat, CGFloat, ContainedViewLayoutTransition) -> Void) {
|
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition protoTransition: ContainedViewLayoutTransition, listViewTransaction: (ListViewUpdateSizeAndInsets, CGFloat, Bool, @escaping () -> Void) -> Void, updateExtraNavigationBarBackgroundHeight: (CGFloat, CGFloat, CGSize?, ContainedViewLayoutTransition) -> Void) {
|
||||||
let transition: ContainedViewLayoutTransition
|
let transition: ContainedViewLayoutTransition
|
||||||
if let _ = self.scheduledAnimateInAsOverlayFromNode {
|
if let _ = self.scheduledAnimateInAsOverlayFromNode {
|
||||||
transition = .immediate
|
transition = .immediate
|
||||||
@ -1302,6 +1302,28 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
self.titleTopicsAccessoryPanelNode = nil
|
self.titleTopicsAccessoryPanelNode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var defaultLeftPanelWidth: CGFloat = 72.0
|
||||||
|
defaultLeftPanelWidth += layout.safeInsets.left
|
||||||
|
let leftPanelLeftInset = defaultLeftPanelWidth - 72.0
|
||||||
|
|
||||||
|
var leftPanelSize: CGSize?
|
||||||
|
var dismissedLeftPanel: (component: AnyComponentWithIdentity<ChatSidePanelEnvironment>, view: ComponentView<ChatSidePanelEnvironment>)?
|
||||||
|
var immediatelyLayoutLeftPanelNodeAndAnimateAppearance = false
|
||||||
|
if let leftPanelComponent = sidePanelForChatPresentationInterfaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.leftPanel?.component, controllerInteraction: self.controllerInteraction, interfaceInteraction: self.interfaceInteraction, force: false) {
|
||||||
|
if self.leftPanel?.component.id != leftPanelComponent.id {
|
||||||
|
dismissedLeftPanel = self.leftPanel
|
||||||
|
self.leftPanel = (leftPanelComponent, ComponentView())
|
||||||
|
immediatelyLayoutLeftPanelNodeAndAnimateAppearance = true
|
||||||
|
} else if let leftPanel = self.leftPanel {
|
||||||
|
self.leftPanel = (leftPanelComponent, leftPanel.view)
|
||||||
|
}
|
||||||
|
|
||||||
|
leftPanelSize = CGSize(width: defaultLeftPanelWidth, height: layout.size.height)
|
||||||
|
} else if let leftPanel = self.leftPanel {
|
||||||
|
dismissedLeftPanel = leftPanel
|
||||||
|
self.leftPanel = nil
|
||||||
|
}
|
||||||
|
|
||||||
var dismissedTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode?
|
var dismissedTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode?
|
||||||
var immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance = false
|
var immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance = false
|
||||||
var titleAccessoryPanelHeight: CGFloat?
|
var titleAccessoryPanelHeight: CGFloat?
|
||||||
@ -1321,7 +1343,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let layoutResult = titleAccessoryPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState)
|
let layoutResult = titleAccessoryPanelNode.updateLayout(width: layout.size.width, leftInset: leftPanelSize?.width ?? layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState)
|
||||||
titleAccessoryPanelHeight = layoutResult.insetHeight
|
titleAccessoryPanelHeight = layoutResult.insetHeight
|
||||||
titleAccessoryPanelBackgroundHeight = layoutResult.backgroundHeight
|
titleAccessoryPanelBackgroundHeight = layoutResult.backgroundHeight
|
||||||
titleAccessoryPanelHitTestSlop = layoutResult.hitTestSlop
|
titleAccessoryPanelHitTestSlop = layoutResult.hitTestSlop
|
||||||
@ -1348,6 +1370,13 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
hasTranslationPanel = true
|
hasTranslationPanel = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
if "".isEmpty {
|
||||||
|
hasTranslationPanel = true
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if hasTranslationPanel {
|
if hasTranslationPanel {
|
||||||
let translationPanelNode: ChatTranslationPanelNode
|
let translationPanelNode: ChatTranslationPanelNode
|
||||||
if let current = self.chatTranslationPanel {
|
if let current = self.chatTranslationPanel {
|
||||||
@ -1369,7 +1398,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let height = translationPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState)
|
let height = translationPanelNode.updateLayout(width: layout.size.width, leftInset: leftPanelSize?.width ?? layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState)
|
||||||
translationPanelHeight = height
|
translationPanelHeight = height
|
||||||
if immediatelyLayoutTranslationPanelNodeAndAnimateAppearance {
|
if immediatelyLayoutTranslationPanelNodeAndAnimateAppearance {
|
||||||
translationPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
translationPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
@ -1433,7 +1462,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
self.titleAccessoryPanelContainer.addSubnode(adPanelNode)
|
self.titleAccessoryPanelContainer.addSubnode(adPanelNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
let height = adPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition, interfaceState: self.chatPresentationInterfaceState)
|
let height = adPanelNode.updateLayout(width: layout.size.width, leftInset: leftPanelSize?.width ?? layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition, interfaceState: self.chatPresentationInterfaceState)
|
||||||
if let adMessage = self.chatPresentationInterfaceState.adMessage, let opaqueId = adMessage.adAttribute?.opaqueId {
|
if let adMessage = self.chatPresentationInterfaceState.adMessage, let opaqueId = adMessage.adAttribute?.opaqueId {
|
||||||
self.historyNode.markAdAsSeen(opaqueId: opaqueId)
|
self.historyNode.markAdAsSeen(opaqueId: opaqueId)
|
||||||
}
|
}
|
||||||
@ -1489,30 +1518,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
self.feePanelNode = nil
|
self.feePanelNode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultLeftPanelWidth: CGFloat = 72.0
|
|
||||||
if case .landscapeRight = layout.metrics.orientation {
|
|
||||||
defaultLeftPanelWidth += layout.safeInsets.left
|
|
||||||
}
|
|
||||||
let leftPanelLeftInset = defaultLeftPanelWidth - 72.0
|
|
||||||
|
|
||||||
var leftPanelSize: CGSize?
|
|
||||||
var dismissedLeftPanel: (component: AnyComponentWithIdentity<ChatSidePanelEnvironment>, view: ComponentView<ChatSidePanelEnvironment>)?
|
|
||||||
var immediatelyLayoutLeftPanelNodeAndAnimateAppearance = false
|
|
||||||
if let leftPanelComponent = sidePanelForChatPresentationInterfaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.leftPanel?.component, controllerInteraction: self.controllerInteraction, interfaceInteraction: self.interfaceInteraction, force: false) {
|
|
||||||
if self.leftPanel?.component.id != leftPanelComponent.id {
|
|
||||||
dismissedLeftPanel = self.leftPanel
|
|
||||||
self.leftPanel = (leftPanelComponent, ComponentView())
|
|
||||||
immediatelyLayoutLeftPanelNodeAndAnimateAppearance = true
|
|
||||||
} else if let leftPanel = self.leftPanel {
|
|
||||||
self.leftPanel = (leftPanelComponent, leftPanel.view)
|
|
||||||
}
|
|
||||||
|
|
||||||
leftPanelSize = CGSize(width: defaultLeftPanelWidth, height: layout.size.height)
|
|
||||||
} else if let leftPanel = self.leftPanel {
|
|
||||||
dismissedLeftPanel = leftPanel
|
|
||||||
self.leftPanel = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
self.controllerInteraction.isSidePanelOpen = self.leftPanel != nil
|
self.controllerInteraction.isSidePanelOpen = self.leftPanel != nil
|
||||||
|
|
||||||
var inputPanelNodeBaseHeight: CGFloat = 0.0
|
var inputPanelNodeBaseHeight: CGFloat = 0.0
|
||||||
@ -1793,6 +1798,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
self.historyNode.isSelectionGestureEnabled = isSelectionEnabled
|
self.historyNode.isSelectionGestureEnabled = isSelectionEnabled
|
||||||
|
|
||||||
transition.updateFrame(node: self.titleAccessoryPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: 200.0)))
|
transition.updateFrame(node: self.titleAccessoryPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: 200.0)))
|
||||||
|
self.titleAccessoryPanelContainer.hitTestExcludeInsets = UIEdgeInsets(top: 0.0, left: leftPanelSize?.width ?? 0.0, bottom: 0.0, right: 0.0)
|
||||||
|
|
||||||
transition.updateFrame(node: self.inputContextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height)))
|
transition.updateFrame(node: self.inputContextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height)))
|
||||||
transition.updateFrame(node: self.inputContextOverTextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height)))
|
transition.updateFrame(node: self.inputContextOverTextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height)))
|
||||||
@ -1848,8 +1854,15 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
insets.top += panelHeight
|
insets.top += panelHeight
|
||||||
extraNavigationBarHeight += panelHeight
|
extraNavigationBarHeight += panelHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var extraNavigationBarLeftCutout: CGSize?
|
||||||
|
if let leftPanelSize {
|
||||||
|
extraNavigationBarLeftCutout = CGSize(width: leftPanelSize.width, height: navigationBarHeight)
|
||||||
|
} else {
|
||||||
|
extraNavigationBarLeftCutout = CGSize(width: 0.0, height: navigationBarHeight)
|
||||||
|
}
|
||||||
|
|
||||||
updateExtraNavigationBarBackgroundHeight(extraNavigationBarHeight, extraNavigationBarHitTestSlop, extraTransition)
|
updateExtraNavigationBarBackgroundHeight(extraNavigationBarHeight, extraNavigationBarHitTestSlop, extraNavigationBarLeftCutout, extraTransition)
|
||||||
|
|
||||||
let contentBounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width - wrappingInsets.left - wrappingInsets.right, height: layout.size.height - wrappingInsets.top - wrappingInsets.bottom)
|
let contentBounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width - wrappingInsets.left - wrappingInsets.right, height: layout.size.height - wrappingInsets.top - wrappingInsets.bottom)
|
||||||
|
|
||||||
@ -2250,28 +2263,32 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
let leftPanelContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: 0.0, height: layout.size.height))
|
let leftPanelContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: 0.0, height: layout.size.height))
|
||||||
transition.updateFrame(node: self.leftPanelContainer, frame: leftPanelContainerFrame)
|
transition.updateFrame(node: self.leftPanelContainer, frame: leftPanelContainerFrame)
|
||||||
if let leftPanel = self.leftPanel, let leftPanelSize {
|
if let leftPanel = self.leftPanel, let leftPanelSize {
|
||||||
let _ = leftPanel.view.update(
|
let leftPanelSize = leftPanel.view.update(
|
||||||
transition: immediatelyLayoutLeftPanelNodeAndAnimateAppearance ? .immediate :ComponentTransition(transition),
|
transition: immediatelyLayoutLeftPanelNodeAndAnimateAppearance ? .immediate :ComponentTransition(transition),
|
||||||
component: leftPanel.component.component,
|
component: leftPanel.component.component,
|
||||||
environment: {
|
environment: {
|
||||||
ChatSidePanelEnvironment(insets: UIEdgeInsets(
|
ChatSidePanelEnvironment(insets: UIEdgeInsets(
|
||||||
top: sidePanelTopInset,
|
top: 0.0,
|
||||||
left: leftPanelLeftInset,
|
left: leftPanelLeftInset,
|
||||||
bottom: containerInsets.bottom + contentBottomInset,
|
bottom: 0.0,
|
||||||
right: 0.0
|
right: 0.0
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
containerSize: leftPanelSize
|
containerSize: CGSize(width: leftPanelSize.width, height: leftPanelSize.height - sidePanelTopInset - (containerInsets.bottom + inputPanelsHeight))
|
||||||
)
|
)
|
||||||
let leftPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: leftPanelSize)
|
|
||||||
|
let leftPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: sidePanelTopInset), size: leftPanelSize)
|
||||||
if let leftPanelView = leftPanel.view.view {
|
if let leftPanelView = leftPanel.view.view {
|
||||||
if leftPanelView.superview == nil {
|
if leftPanelView.superview == nil {
|
||||||
self.leftPanelContainer.view.addSubview(leftPanelView)
|
self.leftPanelContainer.view.addSubview(leftPanelView)
|
||||||
}
|
}
|
||||||
if immediatelyLayoutLeftPanelNodeAndAnimateAppearance {
|
if immediatelyLayoutLeftPanelNodeAndAnimateAppearance {
|
||||||
leftPanelView.frame = leftPanelFrame.offsetBy(dx: -leftPanelSize.width, dy: 0.0)
|
leftPanelView.frame = leftPanelFrame.offsetBy(dx: -leftPanelSize.width, dy: 0.0)
|
||||||
if let leftPanelView = leftPanelView as? ChatSideTopicsPanel.View {
|
|
||||||
leftPanelView.updateGlobalOffset(globalOffset: -leftPanelSize.width, transition: ComponentTransition(transition))
|
if self.titleTopicsAccessoryPanelNode != nil || dismissedTitleTopicsAccessoryPanelNode != nil {
|
||||||
|
if let leftPanelView = leftPanelView as? ChatSideTopicsPanel.View {
|
||||||
|
leftPanelView.updateGlobalOffset(globalOffset: -leftPanelSize.width, transition: ComponentTransition(transition))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
transition.updateFrame(view: leftPanelView, frame: leftPanelFrame)
|
transition.updateFrame(view: leftPanelView, frame: leftPanelFrame)
|
||||||
@ -2286,19 +2303,21 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
component: dismissedLeftPanel.component.component,
|
component: dismissedLeftPanel.component.component,
|
||||||
environment: {
|
environment: {
|
||||||
ChatSidePanelEnvironment(insets: UIEdgeInsets(
|
ChatSidePanelEnvironment(insets: UIEdgeInsets(
|
||||||
top: sidePanelTopInset,
|
top: 0.0,
|
||||||
left: leftPanelLeftInset,
|
left: leftPanelLeftInset,
|
||||||
bottom: containerInsets.bottom + contentBottomInset,
|
bottom: 0.0,
|
||||||
right: 0.0
|
right: 0.0
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
containerSize: CGSize(width: defaultLeftPanelWidth, height: layout.size.height)
|
containerSize: CGSize(width: defaultLeftPanelWidth, height: layout.size.height - sidePanelTopInset - (containerInsets.bottom + inputPanelsHeight))
|
||||||
)
|
)
|
||||||
transition.updateFrame(view: dismissedLeftPanelView, frame: CGRect(origin: CGPoint(x: -dismissedLeftPanelSize.width, y: 0.0), size: dismissedLeftPanelSize), completion: { [weak dismissedLeftPanelView] _ in
|
transition.updateFrame(view: dismissedLeftPanelView, frame: CGRect(origin: CGPoint(x: -dismissedLeftPanelSize.width, y: sidePanelTopInset), size: dismissedLeftPanelSize), completion: { [weak dismissedLeftPanelView] _ in
|
||||||
dismissedLeftPanelView?.removeFromSuperview()
|
dismissedLeftPanelView?.removeFromSuperview()
|
||||||
})
|
})
|
||||||
if let dismissedLeftPanelView = dismissedLeftPanelView as? ChatSideTopicsPanel.View {
|
if let dismissedLeftPanelView = dismissedLeftPanelView as? ChatSideTopicsPanel.View {
|
||||||
dismissedLeftPanelView.updateGlobalOffset(globalOffset: -dismissedLeftPanelSize.width, transition: ComponentTransition(transition))
|
if self.titleTopicsAccessoryPanelNode != nil {
|
||||||
|
dismissedLeftPanelView.updateGlobalOffset(globalOffset: -dismissedLeftPanelSize.width, transition: ComponentTransition(transition))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2352,7 +2371,9 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
if let titleTopicsAccessoryPanelNode = self.titleTopicsAccessoryPanelNode, let titleTopicsAccessoryPanelFrame, (immediatelyLayoutTitleTopicsAccessoryPanelNodeAndAnimateAppearance || !titleTopicsAccessoryPanelNode.frame.equalTo(titleTopicsAccessoryPanelFrame)) {
|
if let titleTopicsAccessoryPanelNode = self.titleTopicsAccessoryPanelNode, let titleTopicsAccessoryPanelFrame, (immediatelyLayoutTitleTopicsAccessoryPanelNodeAndAnimateAppearance || !titleTopicsAccessoryPanelNode.frame.equalTo(titleTopicsAccessoryPanelFrame)) {
|
||||||
if immediatelyLayoutTitleTopicsAccessoryPanelNodeAndAnimateAppearance {
|
if immediatelyLayoutTitleTopicsAccessoryPanelNodeAndAnimateAppearance {
|
||||||
titleTopicsAccessoryPanelNode.frame = titleTopicsAccessoryPanelFrame.offsetBy(dx: 0.0, dy: -titleTopicsAccessoryPanelFrame.height)
|
titleTopicsAccessoryPanelNode.frame = titleTopicsAccessoryPanelFrame.offsetBy(dx: 0.0, dy: -titleTopicsAccessoryPanelFrame.height)
|
||||||
titleTopicsAccessoryPanelNode.updateGlobalOffset(globalOffset: -titleTopicsAccessoryPanelFrame.height, transition: .immediate)
|
if self.leftPanel != nil || dismissedLeftPanel != nil {
|
||||||
|
titleTopicsAccessoryPanelNode.updateGlobalOffset(globalOffset: -titleTopicsAccessoryPanelFrame.height, transition: .immediate)
|
||||||
|
}
|
||||||
|
|
||||||
let topPanelTransition = ComponentTransition(transition)
|
let topPanelTransition = ComponentTransition(transition)
|
||||||
/*switch topPanelTransition.animation {
|
/*switch topPanelTransition.animation {
|
||||||
@ -2378,8 +2399,10 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
let previousFrame = titleAccessoryPanelNode.frame
|
let previousFrame = titleAccessoryPanelNode.frame
|
||||||
titleAccessoryPanelNode.frame = titleAccessoryPanelFrame
|
titleAccessoryPanelNode.frame = titleAccessoryPanelFrame
|
||||||
if transition.isAnimated && previousFrame.width != titleAccessoryPanelFrame.width {
|
if transition.isAnimated && previousFrame.width != titleAccessoryPanelFrame.width {
|
||||||
} else {
|
} else if immediatelyLayoutAccessoryPanelAndAnimateAppearance {
|
||||||
transition.animatePositionAdditive(node: titleAccessoryPanelNode, offset: CGPoint(x: 0.0, y: -titleAccessoryPanelFrame.height))
|
transition.animatePositionAdditive(node: titleAccessoryPanelNode, offset: CGPoint(x: 0.0, y: -titleAccessoryPanelFrame.height))
|
||||||
|
} else if previousFrame.minY != titleAccessoryPanelFrame.minY {
|
||||||
|
transition.animatePositionAdditive(node: titleAccessoryPanelNode, offset: CGPoint(x: 0.0, y: previousFrame.minY - titleAccessoryPanelFrame.minY))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2387,14 +2410,15 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
let previousFrame = chatTranslationPanel.frame
|
let previousFrame = chatTranslationPanel.frame
|
||||||
chatTranslationPanel.frame = translationPanelFrame
|
chatTranslationPanel.frame = translationPanelFrame
|
||||||
if transition.isAnimated && previousFrame.width != translationPanelFrame.width {
|
if transition.isAnimated && previousFrame.width != translationPanelFrame.width {
|
||||||
} else {
|
} else if immediatelyLayoutTranslationPanelNodeAndAnimateAppearance {
|
||||||
transition.animatePositionAdditive(node: chatTranslationPanel, offset: CGPoint(x: 0.0, y: -translationPanelFrame.height))
|
transition.animatePositionAdditive(node: chatTranslationPanel, offset: CGPoint(x: 0.0, y: -translationPanelFrame.height))
|
||||||
|
} else if previousFrame.minY != translationPanelFrame.minY {
|
||||||
|
transition.animatePositionAdditive(node: chatTranslationPanel, offset: CGPoint(x: 0.0, y: previousFrame.minY - translationPanelFrame.minY))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let chatImportStatusPanel = self.chatImportStatusPanel, let importStatusPanelFrame, !chatImportStatusPanel.frame.equalTo(importStatusPanelFrame) {
|
if let chatImportStatusPanel = self.chatImportStatusPanel, let importStatusPanelFrame, !chatImportStatusPanel.frame.equalTo(importStatusPanelFrame) {
|
||||||
chatImportStatusPanel.frame = importStatusPanelFrame
|
chatImportStatusPanel.frame = importStatusPanelFrame
|
||||||
//transition.animatePositionAdditive(node: chatImportStatusPanel, offset: CGPoint(x: 0.0, y: -titleAccessoryPanelFrame.height))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let adPanelNode = self.adPanelNode, let adPanelFrame, !adPanelNode.frame.equalTo(adPanelFrame) {
|
if let adPanelNode = self.adPanelNode, let adPanelFrame, !adPanelNode.frame.equalTo(adPanelFrame) {
|
||||||
@ -2502,7 +2526,9 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
transition.updateFrame(node: dismissedTitleTopicsAccessoryPanelNode, frame: dismissedTopPanelFrame, completion: { [weak dismissedTitleTopicsAccessoryPanelNode] _ in
|
transition.updateFrame(node: dismissedTitleTopicsAccessoryPanelNode, frame: dismissedTopPanelFrame, completion: { [weak dismissedTitleTopicsAccessoryPanelNode] _ in
|
||||||
dismissedTitleTopicsAccessoryPanelNode?.removeFromSupernode()
|
dismissedTitleTopicsAccessoryPanelNode?.removeFromSupernode()
|
||||||
})
|
})
|
||||||
dismissedTitleTopicsAccessoryPanelNode.updateGlobalOffset(globalOffset: -dismissedTopPanelFrame.height, transition: ComponentTransition(transition))
|
if self.leftPanel != nil {
|
||||||
|
dismissedTitleTopicsAccessoryPanelNode.updateGlobalOffset(globalOffset: -dismissedTopPanelFrame.height, transition: ComponentTransition(transition))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let dismissedTitleAccessoryPanelNode {
|
if let dismissedTitleAccessoryPanelNode {
|
||||||
@ -2802,6 +2828,11 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
displayInlineSearch = true
|
displayInlineSearch = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let channel = self.chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = self.chatPresentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil {
|
||||||
|
if self.chatPresentationInterfaceState.search != nil {
|
||||||
|
displayInlineSearch = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if displayInlineSearch {
|
if displayInlineSearch {
|
||||||
let peerId = self.chatPresentationInterfaceState.chatLocation.peerId
|
let peerId = self.chatPresentationInterfaceState.chatLocation.peerId
|
||||||
@ -2827,6 +2858,8 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
} else {
|
} else {
|
||||||
mappedContents = .empty
|
mappedContents = .empty
|
||||||
}
|
}
|
||||||
|
} else if let channel = self.chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = self.chatPresentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil {
|
||||||
|
mappedContents = .monoforumChats(query: self.chatPresentationInterfaceState.search?.query ?? "")
|
||||||
} else if case .peer(self.context.account.peerId) = self.chatPresentationInterfaceState.chatLocation {
|
} else if case .peer(self.context.account.peerId) = self.chatPresentationInterfaceState.chatLocation {
|
||||||
mappedContents = .tag(MemoryBuffer())
|
mappedContents = .tag(MemoryBuffer())
|
||||||
} else {
|
} else {
|
||||||
@ -2952,30 +2985,39 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let navigationController = self.controller?.navigationController as? NavigationController else {
|
|
||||||
return
|
if let channel = self.chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum {
|
||||||
|
self.interfaceInteraction?.updateChatLocationThread(peer.id.toInt64(), nil)
|
||||||
|
|
||||||
|
self.controller?.updateChatPresentationInterfaceState(animated: true, interactive: true, { current in
|
||||||
|
return current.updatedSearch(nil)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
guard let navigationController = self.controller?.navigationController as? NavigationController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(
|
||||||
|
navigationController: navigationController,
|
||||||
|
context: self.context,
|
||||||
|
chatLocation: .replyThread(ChatReplyThreadMessage(
|
||||||
|
peerId: self.context.account.peerId,
|
||||||
|
threadId: peer.id.toInt64(),
|
||||||
|
channelMessageId: nil,
|
||||||
|
isChannelPost: false,
|
||||||
|
isForumPost: false,
|
||||||
|
isMonoforumPost: false,
|
||||||
|
maxMessage: nil,
|
||||||
|
maxReadIncomingMessageId: nil,
|
||||||
|
maxReadOutgoingMessageId: nil,
|
||||||
|
unreadCount: 0,
|
||||||
|
initialFilledHoles: IndexSet(),
|
||||||
|
initialAnchor: .automatic,
|
||||||
|
isNotAvailable: false
|
||||||
|
)),
|
||||||
|
subject: nil,
|
||||||
|
keepStack: .always
|
||||||
|
))
|
||||||
}
|
}
|
||||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(
|
|
||||||
navigationController: navigationController,
|
|
||||||
context: self.context,
|
|
||||||
chatLocation: .replyThread(ChatReplyThreadMessage(
|
|
||||||
peerId: self.context.account.peerId,
|
|
||||||
threadId: peer.id.toInt64(),
|
|
||||||
channelMessageId: nil,
|
|
||||||
isChannelPost: false,
|
|
||||||
isForumPost: false,
|
|
||||||
isMonoforumPost: false,
|
|
||||||
maxMessage: nil,
|
|
||||||
maxReadIncomingMessageId: nil,
|
|
||||||
maxReadOutgoingMessageId: nil,
|
|
||||||
unreadCount: 0,
|
|
||||||
initialFilledHoles: IndexSet(),
|
|
||||||
initialAnchor: .automatic,
|
|
||||||
isNotAvailable: false
|
|
||||||
)),
|
|
||||||
subject: nil,
|
|
||||||
keepStack: .always
|
|
||||||
))
|
|
||||||
},
|
},
|
||||||
loadTagMessages: { tag, index in
|
loadTagMessages: { tag, index in
|
||||||
let input: ChatHistoryLocationInput
|
let input: ChatHistoryLocationInput
|
||||||
@ -3048,6 +3090,89 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
return foundLocalPeers
|
return foundLocalPeers
|
||||||
},
|
},
|
||||||
|
getChats: { [weak self] query in
|
||||||
|
guard let self else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let peerId = self.chatPresentationInterfaceState.chatLocation.peerId else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let viewKey: PostboxViewKey = .savedMessagesIndex(peerId: peerId)
|
||||||
|
let interfaceStateKey: PostboxViewKey = .chatInterfaceState(peerId: peerId)
|
||||||
|
|
||||||
|
let accountPeerId = self.context.account.peerId
|
||||||
|
let threadListSignal: Signal<EngineChatList?, NoError> = self.context.account.postbox.combinedView(keys: [viewKey, interfaceStateKey])
|
||||||
|
|> map { views -> EngineChatList? in
|
||||||
|
guard let view = views.views[viewKey] as? MessageHistorySavedMessagesIndexView else {
|
||||||
|
preconditionFailure()
|
||||||
|
}
|
||||||
|
|
||||||
|
var draft: EngineChatList.Draft?
|
||||||
|
if let interfaceStateView = views.views[interfaceStateKey] as? ChatInterfaceStateView {
|
||||||
|
if let embeddedState = interfaceStateView.value, let _ = embeddedState.overrideChatTimestamp {
|
||||||
|
if let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) {
|
||||||
|
if let text = opaqueState.synchronizeableInputState?.text {
|
||||||
|
draft = EngineChatList.Draft(text: text, entities: opaqueState.synchronizeableInputState?.entities ?? [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: false),
|
||||||
|
isMuted: false,
|
||||||
|
draft: sourceId == accountPeerId ? 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 {
|
||||||
return
|
return
|
||||||
@ -4052,7 +4177,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
if let (layout, navigationHeight) = self.validLayout {
|
if let (layout, navigationHeight) = self.validLayout {
|
||||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in
|
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop, completion in
|
||||||
self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion)
|
self.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop, completion: completion)
|
||||||
}, updateExtraNavigationBarBackgroundHeight: { _, _, _ in
|
}, updateExtraNavigationBarBackgroundHeight: { _, _, _, _ in
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4900,15 +5025,15 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func chatLocationTabSwitchDirection(from fromLocation: ChatLocation, to toLocation: ChatLocation) -> Bool? {
|
func chatLocationTabSwitchDirection(from fromLocation: Int64?, to toLocation: Int64?) -> Bool? {
|
||||||
var leftIndex: Int?
|
var leftIndex: Int?
|
||||||
var rightIndex: Int?
|
var rightIndex: Int?
|
||||||
if let titleTopicsAccessoryPanelNode = self.titleTopicsAccessoryPanelNode {
|
if let titleTopicsAccessoryPanelNode = self.titleTopicsAccessoryPanelNode {
|
||||||
leftIndex = titleTopicsAccessoryPanelNode.topicIndex(threadId: fromLocation.threadId)
|
leftIndex = titleTopicsAccessoryPanelNode.topicIndex(threadId: fromLocation)
|
||||||
rightIndex = titleTopicsAccessoryPanelNode.topicIndex(threadId: toLocation.threadId)
|
rightIndex = titleTopicsAccessoryPanelNode.topicIndex(threadId: toLocation)
|
||||||
} else if let leftPanelView = self.leftPanel?.view.view as? ChatSideTopicsPanel.View {
|
} else if let leftPanelView = self.leftPanel?.view.view as? ChatSideTopicsPanel.View {
|
||||||
leftIndex = leftPanelView.topicIndex(threadId: fromLocation.threadId)
|
leftIndex = leftPanelView.topicIndex(threadId: fromLocation)
|
||||||
rightIndex = leftPanelView.topicIndex(threadId: toLocation.threadId)
|
rightIndex = leftPanelView.topicIndex(threadId: toLocation)
|
||||||
}
|
}
|
||||||
guard let leftIndex, let rightIndex else {
|
guard let leftIndex, let rightIndex else {
|
||||||
return nil
|
return nil
|
||||||
@ -4916,7 +5041,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
return leftIndex < rightIndex
|
return leftIndex < rightIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateChatLocation(chatLocation: ChatLocation, transition: ContainedViewLayoutTransition, tabSwitchDirection: Bool?) {
|
func updateChatLocation(chatLocation: ChatLocation, transition: ContainedViewLayoutTransition, tabSwitchDirection: ChatControllerAnimateInnerChatSwitchDirection?) {
|
||||||
if chatLocation == self.chatLocation {
|
if chatLocation == self.chatLocation {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -4988,10 +5113,25 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let validLayout = self.validLayout, transition.isAnimated, let tabSwitchDirection {
|
if let validLayout = self.validLayout, transition.isAnimated, let tabSwitchDirection {
|
||||||
let offsetMultiplier: CGFloat = tabSwitchDirection ? 1.0 : -1.0
|
var offsetMultiplier = CGPoint()
|
||||||
transition.animatePosition(layer: historyNode.layer, from: CGPoint(x: offsetMultiplier * validLayout.0.size.width, y: 0.0), to: CGPoint(), removeOnCompletion: true, additive: true)
|
switch tabSwitchDirection {
|
||||||
transition.animatePosition(layer: previousHistoryNode.layer, from: CGPoint(), to: CGPoint(x: -offsetMultiplier * validLayout.0.size.width, y: 0.0), removeOnCompletion: false, additive: true, completion: { [weak previousHistoryNode] _ in
|
case .up:
|
||||||
|
offsetMultiplier.y = -1.0
|
||||||
|
case .down:
|
||||||
|
offsetMultiplier.y = 1.0
|
||||||
|
case .left:
|
||||||
|
offsetMultiplier.x = -1.0
|
||||||
|
case .right:
|
||||||
|
offsetMultiplier.x = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
previousHistoryNode.clipsToBounds = true
|
||||||
|
historyNode.clipsToBounds = true
|
||||||
|
|
||||||
|
transition.animatePosition(layer: historyNode.layer, from: CGPoint(x: offsetMultiplier.x * validLayout.0.size.width, y: offsetMultiplier.y * validLayout.0.size.height), to: CGPoint(), removeOnCompletion: true, additive: true)
|
||||||
|
transition.animatePosition(layer: previousHistoryNode.layer, from: CGPoint(), to: CGPoint(x: -offsetMultiplier.x * validLayout.0.size.width, y: -offsetMultiplier.y * validLayout.0.size.height), removeOnCompletion: false, additive: true, completion: { [weak previousHistoryNode, weak historyNode] _ in
|
||||||
previousHistoryNode?.removeFromSupernode()
|
previousHistoryNode?.removeFromSupernode()
|
||||||
|
historyNode?.clipsToBounds = false
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
previousHistoryNode.removeFromSupernode()
|
previousHistoryNode.removeFromSupernode()
|
||||||
|
|||||||
@ -3,7 +3,13 @@ import UIKit
|
|||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
|
|
||||||
final class ChatControllerTitlePanelNodeContainer: ASDisplayNode {
|
final class ChatControllerTitlePanelNodeContainer: ASDisplayNode {
|
||||||
|
var hitTestExcludeInsets = UIEdgeInsets()
|
||||||
|
|
||||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
|
if point.x < self.hitTestExcludeInsets.left {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
for subview in self.view.subviews {
|
for subview in self.view.subviews {
|
||||||
if let result = subview.hitTest(self.view.convert(point, to: subview), with: event) {
|
if let result = subview.hitTest(self.view.convert(point, to: subview), with: event) {
|
||||||
return result
|
return result
|
||||||
|
|||||||
@ -232,13 +232,24 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat
|
|||||||
}
|
}
|
||||||
|
|
||||||
func titleTopicsPanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: ChatTitleAccessoryPanelNode?, controllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?, force: Bool) -> ChatTopicListTitleAccessoryPanelNode? {
|
func titleTopicsPanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: ChatTitleAccessoryPanelNode?, controllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?, force: Bool) -> ChatTopicListTitleAccessoryPanelNode? {
|
||||||
if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = chatPresentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil {
|
if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = chatPresentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil, 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 {
|
||||||
return currentPanel
|
return currentPanel
|
||||||
} else {
|
} else {
|
||||||
let panel = ChatTopicListTitleAccessoryPanelNode(context: context, peerId: peerId)
|
let panel = ChatTopicListTitleAccessoryPanelNode(context: context, peerId: peerId, isMonoforum: true)
|
||||||
|
panel.interfaceInteraction = interfaceInteraction
|
||||||
|
return panel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForum, chatPresentationInterfaceState.search == nil {
|
||||||
|
let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top
|
||||||
|
if case .top = topicListDisplayMode, let peerId = chatPresentationInterfaceState.chatLocation.peerId {
|
||||||
|
if let currentPanel = currentPanel as? ChatTopicListTitleAccessoryPanelNode {
|
||||||
|
return currentPanel
|
||||||
|
} else {
|
||||||
|
let panel = ChatTopicListTitleAccessoryPanelNode(context: context, peerId: peerId, isMonoforum: false)
|
||||||
panel.interfaceInteraction = interfaceInteraction
|
panel.interfaceInteraction = interfaceInteraction
|
||||||
return panel
|
return panel
|
||||||
}
|
}
|
||||||
@ -253,7 +264,7 @@ func sidePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = chatPresentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil {
|
if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = chatPresentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.adminRights != nil, chatPresentationInterfaceState.search == nil {
|
||||||
let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top
|
let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top
|
||||||
if case .side = topicListDisplayMode {
|
if case .side = topicListDisplayMode {
|
||||||
return AnyComponentWithIdentity(
|
return AnyComponentWithIdentity(
|
||||||
@ -263,12 +274,34 @@ func sidePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState
|
|||||||
theme: chatPresentationInterfaceState.theme,
|
theme: chatPresentationInterfaceState.theme,
|
||||||
strings: chatPresentationInterfaceState.strings,
|
strings: chatPresentationInterfaceState.strings,
|
||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
|
isMonoforum: true,
|
||||||
topicId: chatPresentationInterfaceState.chatLocation.threadId,
|
topicId: chatPresentationInterfaceState.chatLocation.threadId,
|
||||||
togglePanel: { [weak interfaceInteraction] in
|
togglePanel: { [weak interfaceInteraction] in
|
||||||
interfaceInteraction?.toggleChatSidebarMode()
|
interfaceInteraction?.toggleChatSidebarMode()
|
||||||
},
|
},
|
||||||
updateTopicId: { [weak interfaceInteraction] topicId in
|
updateTopicId: { [weak interfaceInteraction] topicId, direction in
|
||||||
interfaceInteraction?.updateChatLocationThread(topicId)
|
interfaceInteraction?.updateChatLocationThread(topicId, direction ? .down : .up)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if let channel = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForum, chatPresentationInterfaceState.search == nil {
|
||||||
|
let topicListDisplayMode = chatPresentationInterfaceState.topicListDisplayMode ?? .top
|
||||||
|
if case .side = topicListDisplayMode {
|
||||||
|
return AnyComponentWithIdentity(
|
||||||
|
id: "topics",
|
||||||
|
component: AnyComponent(ChatSideTopicsPanel(
|
||||||
|
context: context,
|
||||||
|
theme: chatPresentationInterfaceState.theme,
|
||||||
|
strings: chatPresentationInterfaceState.strings,
|
||||||
|
peerId: peerId,
|
||||||
|
isMonoforum: false,
|
||||||
|
topicId: chatPresentationInterfaceState.chatLocation.threadId,
|
||||||
|
togglePanel: { [weak interfaceInteraction] in
|
||||||
|
interfaceInteraction?.toggleChatSidebarMode()
|
||||||
|
},
|
||||||
|
updateTopicId: { [weak interfaceInteraction] topicId, direction in
|
||||||
|
interfaceInteraction?.updateChatLocationThread(topicId, direction ? .down : .up)
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
|
|||||||
@ -517,12 +517,13 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
if self.currentLayout?.0 != width || self.currentLayout?.1 != leftInset || self.currentLayout?.2 != rightInset || messageUpdated || themeUpdated || currentTranslateToLanguageUpdated {
|
if self.currentLayout?.0 != width || self.currentLayout?.1 != leftInset || self.currentLayout?.2 != rightInset || messageUpdated || themeUpdated || currentTranslateToLanguageUpdated {
|
||||||
self.currentLayout = (width, leftInset, rightInset)
|
self.currentLayout = (width, leftInset, rightInset)
|
||||||
|
|
||||||
|
let messageUpdated = self.currentMessage?.message.id != interfaceState.pinnedMessage?.message.id
|
||||||
let previousMessageWasNil = self.currentMessage == nil
|
let previousMessageWasNil = self.currentMessage == nil
|
||||||
self.currentMessage = interfaceState.pinnedMessage
|
self.currentMessage = interfaceState.pinnedMessage
|
||||||
|
|
||||||
if let currentMessage = self.currentMessage, let currentLayout = self.currentLayout {
|
if let currentMessage = self.currentMessage, let currentLayout = self.currentLayout {
|
||||||
self.dustNode?.update(revealed: false, animated: false)
|
self.dustNode?.update(revealed: false, animated: false)
|
||||||
self.enqueueTransition(width: currentLayout.0, panelHeight: panelHeight, leftInset: currentLayout.1, rightInset: currentLayout.2, transition: .immediate, animation: messageUpdatedAnimation, pinnedMessage: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, dateTimeFormat: interfaceState.dateTimeFormat, accountPeerId: self.context.account.peerId, firstTime: previousMessageWasNil, isReplyThread: isReplyThread, translateToLanguage: translateToLanguage?.toLang)
|
self.enqueueTransition(width: currentLayout.0, panelHeight: panelHeight, leftInset: currentLayout.1, rightInset: currentLayout.2, transition: messageUpdated ? .immediate : transition, animation: messageUpdatedAnimation, pinnedMessage: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, dateTimeFormat: interfaceState.dateTimeFormat, accountPeerId: self.context.account.peerId, firstTime: previousMessageWasNil, isReplyThread: isReplyThread, translateToLanguage: translateToLanguage?.toLang)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,6 +566,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
self.textNode.textNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -offset), to: CGPoint(), duration: 0.2, additive: true)
|
self.textNode.textNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -offset), to: CGPoint(), duration: 0.2, additive: true)
|
||||||
self.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
self.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
animationTransition = transition
|
||||||
}
|
}
|
||||||
|
|
||||||
let makeTitleLayout = self.titleNode.asyncLayout()
|
let makeTitleLayout = self.titleNode.asyncLayout()
|
||||||
@ -575,13 +578,6 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
let previousMediaReference = self.previousMediaReference
|
let previousMediaReference = self.previousMediaReference
|
||||||
let context = self.context
|
let context = self.context
|
||||||
|
|
||||||
let targetQueue: Queue
|
|
||||||
if firstTime {
|
|
||||||
targetQueue = Queue.mainQueue()
|
|
||||||
} else {
|
|
||||||
targetQueue = self.queue
|
|
||||||
}
|
|
||||||
|
|
||||||
let contentLeftInset: CGFloat = leftInset + 10.0
|
let contentLeftInset: CGFloat = leftInset + 10.0
|
||||||
var textLineInset: CGFloat = 10.0
|
var textLineInset: CGFloat = 10.0
|
||||||
var rightInset: CGFloat = 14.0 + rightInset
|
var rightInset: CGFloat = 14.0 + rightInset
|
||||||
@ -592,293 +588,288 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
rightInset += self.actionButton.bounds.width - 14.0
|
rightInset += self.actionButton.bounds.width - 14.0
|
||||||
}
|
}
|
||||||
|
|
||||||
targetQueue.async { [weak self] in
|
var updatedMediaReference: AnyMediaReference?
|
||||||
var updatedMediaReference: AnyMediaReference?
|
var imageDimensions: CGSize?
|
||||||
var imageDimensions: CGSize?
|
|
||||||
|
let giveaway = pinnedMessage.message.media.first(where: { $0 is TelegramMediaGiveaway }) as? TelegramMediaGiveaway
|
||||||
let giveaway = pinnedMessage.message.media.first(where: { $0 is TelegramMediaGiveaway }) as? TelegramMediaGiveaway
|
|
||||||
|
var titleStrings: [AnimatedCountLabelNode.Segment] = []
|
||||||
var titleStrings: [AnimatedCountLabelNode.Segment] = []
|
if let _ = giveaway {
|
||||||
if let _ = giveaway {
|
titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedGiveaway) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
|
||||||
titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedGiveaway) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
|
} else {
|
||||||
} else {
|
if pinnedMessage.totalCount == 2 {
|
||||||
if pinnedMessage.totalCount == 2 {
|
if pinnedMessage.index == 0 {
|
||||||
if pinnedMessage.index == 0 {
|
titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedPreviousMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
|
||||||
titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedPreviousMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
|
|
||||||
} else {
|
|
||||||
titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
|
|
||||||
}
|
|
||||||
} else if pinnedMessage.totalCount > 1 && pinnedMessage.index != pinnedMessage.totalCount - 1 {
|
|
||||||
titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage)", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
|
|
||||||
titleStrings.append(.text(1, NSAttributedString(string: " #", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
|
|
||||||
titleStrings.append(.number(pinnedMessage.index + 1, NSAttributedString(string: "\(pinnedMessage.index + 1)", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
|
|
||||||
} else {
|
} else {
|
||||||
titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
|
titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
|
||||||
}
|
}
|
||||||
|
} else if pinnedMessage.totalCount > 1 && pinnedMessage.index != pinnedMessage.totalCount - 1 {
|
||||||
|
titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage)", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
|
||||||
|
titleStrings.append(.text(1, NSAttributedString(string: " #", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
|
||||||
|
titleStrings.append(.number(pinnedMessage.index + 1, NSAttributedString(string: "\(pinnedMessage.index + 1)", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
|
||||||
|
} else {
|
||||||
|
titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if !message.containsSecretMedia {
|
|
||||||
for media in message.media {
|
if !message.containsSecretMedia {
|
||||||
if let image = media as? TelegramMediaImage {
|
for media in message.media {
|
||||||
updatedMediaReference = .message(message: MessageReference(message), media: image)
|
if let image = media as? TelegramMediaImage {
|
||||||
if let representation = largestRepresentationForPhoto(image) {
|
updatedMediaReference = .message(message: MessageReference(message), media: image)
|
||||||
imageDimensions = representation.dimensions.cgSize
|
if let representation = largestRepresentationForPhoto(image) {
|
||||||
}
|
imageDimensions = representation.dimensions.cgSize
|
||||||
break
|
}
|
||||||
} else if let file = media as? TelegramMediaFile {
|
break
|
||||||
updatedMediaReference = .message(message: MessageReference(message), media: file)
|
} else if let file = media as? TelegramMediaFile {
|
||||||
if !file.isInstantVideo && !file.isSticker, let representation = largestImageRepresentation(file.previewRepresentations) {
|
updatedMediaReference = .message(message: MessageReference(message), media: file)
|
||||||
imageDimensions = representation.dimensions.cgSize
|
if !file.isInstantVideo && !file.isSticker, let representation = largestImageRepresentation(file.previewRepresentations) {
|
||||||
} else if file.isAnimated, let dimensions = file.dimensions {
|
imageDimensions = representation.dimensions.cgSize
|
||||||
|
} else if file.isAnimated, let dimensions = file.dimensions {
|
||||||
|
imageDimensions = dimensions.cgSize
|
||||||
|
}
|
||||||
|
break
|
||||||
|
} else if let paidContent = media as? TelegramMediaPaidContent, let firstMedia = paidContent.extendedMedia.first {
|
||||||
|
switch firstMedia {
|
||||||
|
case let .preview(dimensions, immediateThumbnailData, _):
|
||||||
|
let thumbnailMedia = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [], immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
|
||||||
|
if let dimensions {
|
||||||
imageDimensions = dimensions.cgSize
|
imageDimensions = dimensions.cgSize
|
||||||
}
|
}
|
||||||
break
|
updatedMediaReference = .standalone(media: thumbnailMedia)
|
||||||
} else if let paidContent = media as? TelegramMediaPaidContent, let firstMedia = paidContent.extendedMedia.first {
|
case let .full(fullMedia):
|
||||||
switch firstMedia {
|
updatedMediaReference = .message(message: MessageReference(message), media: fullMedia)
|
||||||
case let .preview(dimensions, immediateThumbnailData, _):
|
if let image = fullMedia as? TelegramMediaImage {
|
||||||
let thumbnailMedia = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [], immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
|
if let representation = largestRepresentationForPhoto(image) {
|
||||||
if let dimensions {
|
imageDimensions = representation.dimensions.cgSize
|
||||||
|
}
|
||||||
|
break
|
||||||
|
} else if let file = fullMedia as? TelegramMediaFile {
|
||||||
|
if let dimensions = file.dimensions {
|
||||||
imageDimensions = dimensions.cgSize
|
imageDimensions = dimensions.cgSize
|
||||||
}
|
}
|
||||||
updatedMediaReference = .standalone(media: thumbnailMedia)
|
|
||||||
case let .full(fullMedia):
|
|
||||||
updatedMediaReference = .message(message: MessageReference(message), media: fullMedia)
|
|
||||||
if let image = fullMedia as? TelegramMediaImage {
|
|
||||||
if let representation = largestRepresentationForPhoto(image) {
|
|
||||||
imageDimensions = representation.dimensions.cgSize
|
|
||||||
}
|
|
||||||
break
|
|
||||||
} else if let file = fullMedia as? TelegramMediaFile {
|
|
||||||
if let dimensions = file.dimensions {
|
|
||||||
imageDimensions = dimensions.cgSize
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isReplyThread {
|
|
||||||
let titleString: String
|
|
||||||
if let author = message.effectiveAuthor {
|
|
||||||
titleString = EnginePeer(author).displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
|
||||||
} else {
|
|
||||||
titleString = ""
|
|
||||||
}
|
|
||||||
titleStrings = [.text(0, NSAttributedString(string: titleString, font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))]
|
|
||||||
} else {
|
|
||||||
for media in message.media {
|
|
||||||
if let media = media as? TelegramMediaInvoice {
|
|
||||||
titleStrings = [.text(0, NSAttributedString(string: media.title, font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var applyImage: (() -> Void)?
|
|
||||||
if let imageDimensions = imageDimensions {
|
|
||||||
let boundingSize = CGSize(width: 35.0, height: 35.0)
|
|
||||||
applyImage = imageNodeLayout(TransformImageArguments(corners: ImageCorners(radius: 2.0), imageSize: imageDimensions.aspectFilled(boundingSize), boundingSize: boundingSize, intrinsicInsets: UIEdgeInsets()))
|
|
||||||
|
|
||||||
textLineInset += 9.0 + 35.0
|
|
||||||
}
|
|
||||||
|
|
||||||
var mediaUpdated = false
|
|
||||||
if let updatedMediaReference = updatedMediaReference, let previousMediaReference = previousMediaReference {
|
|
||||||
mediaUpdated = !updatedMediaReference.media.isEqual(to: previousMediaReference.media)
|
|
||||||
} else if (updatedMediaReference != nil) != (previousMediaReference != nil) {
|
|
||||||
mediaUpdated = true
|
|
||||||
}
|
|
||||||
|
|
||||||
let hasSpoiler = message.attributes.contains(where: { $0 is MediaSpoilerMessageAttribute })
|
|
||||||
|
|
||||||
var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
|
|
||||||
var updatedFetchMediaSignal: Signal<FetchResourceSourceType, FetchResourceError>?
|
|
||||||
if mediaUpdated {
|
|
||||||
if let updatedMediaReference = updatedMediaReference, imageDimensions != nil {
|
|
||||||
if let imageReference = updatedMediaReference.concrete(TelegramMediaImage.self) {
|
|
||||||
if imageReference.media.representations.isEmpty {
|
|
||||||
updateImageSignal = chatSecretPhoto(account: context.account, userLocation: .peer(message.id.peerId), photoReference: imageReference, ignoreFullSize: true, synchronousLoad: true)
|
|
||||||
} else {
|
|
||||||
updateImageSignal = chatMessagePhotoThumbnail(account: context.account, userLocation: .peer(message.id.peerId), photoReference: imageReference, blurred: hasSpoiler)
|
|
||||||
}
|
|
||||||
} else if let fileReference = updatedMediaReference.concrete(TelegramMediaFile.self) {
|
|
||||||
if fileReference.media.isAnimatedSticker {
|
|
||||||
let dimensions = fileReference.media.dimensions ?? PixelDimensions(width: 512, height: 512)
|
|
||||||
updateImageSignal = chatMessageAnimatedSticker(postbox: context.account.postbox, userLocation: .peer(message.id.peerId), file: fileReference.media, small: false, size: dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0)))
|
|
||||||
updatedFetchMediaSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(message.id.peerId), userContentType: MediaResourceUserContentType(file: fileReference.media), reference: fileReference.resourceReference(fileReference.media.resource))
|
|
||||||
} else if fileReference.media.isVideo || fileReference.media.isAnimated {
|
|
||||||
updateImageSignal = chatMessageVideoThumbnail(account: context.account, userLocation: .peer(message.id.peerId), fileReference: fileReference, blurred: hasSpoiler)
|
|
||||||
} else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
|
|
||||||
updateImageSignal = chatWebpageSnippetFile(account: context.account, userLocation: .peer(message.id.peerId), mediaReference: fileReference.abstract, representation: iconImageRepresentation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updateImageSignal = .single({ _ in return nil })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let (titleLayout, titleApply) = makeTitleLayout(CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), .zero, titleStrings)
|
|
||||||
|
|
||||||
let (textString, _, isText) = descriptionStringForMessage(contentSettings: context.currentContentSettings.with { $0 }, message: EngineMessage(message), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, accountPeerId: accountPeerId)
|
|
||||||
|
|
||||||
let messageText: NSAttributedString
|
|
||||||
let textFont = Font.regular(15.0)
|
|
||||||
if let giveaway {
|
|
||||||
let dateString = stringForDateWithoutYear(date: Date(timeIntervalSince1970: TimeInterval(giveaway.untilDate)), timeZone: .current, strings: strings)
|
|
||||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
|
||||||
let isFinished = currentTime >= giveaway.untilDate
|
|
||||||
let text: String
|
|
||||||
if isFinished {
|
|
||||||
let winnersString = strings.Conversation_PinnedGiveaway_Finished_Winners(giveaway.quantity)
|
|
||||||
text = strings.Conversation_PinnedGiveaway_Finished(winnersString, dateString).string
|
|
||||||
} else {
|
|
||||||
let winnersString = strings.Conversation_PinnedGiveaway_Ongoing_Winners(giveaway.quantity)
|
|
||||||
text = strings.Conversation_PinnedGiveaway_Ongoing(winnersString, dateString).string
|
|
||||||
}
|
|
||||||
messageText = NSAttributedString(string: text, font: textFont, textColor: theme.chat.inputPanel.primaryTextColor)
|
|
||||||
} else if isText {
|
|
||||||
var text = message.text
|
|
||||||
var messageEntities = message.textEntitiesAttribute?.entities ?? []
|
|
||||||
|
|
||||||
if let translateToLanguage = translateToLanguage, !text.isEmpty {
|
|
||||||
for attribute in message.attributes {
|
|
||||||
if let attribute = attribute as? TranslationMessageAttribute, !attribute.text.isEmpty, attribute.toLang == translateToLanguage {
|
|
||||||
text = attribute.text
|
|
||||||
messageEntities = attribute.entities
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let entities = messageEntities.filter { entity in
|
|
||||||
switch entity.type {
|
|
||||||
case .Spoiler, .CustomEmoji:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let textColor = theme.chat.inputPanel.primaryTextColor
|
|
||||||
if entities.count > 0 {
|
|
||||||
messageText = stringWithAppliedEntities(trimToLineCount(text, lineCount: 1), entities: entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message)
|
|
||||||
} else {
|
|
||||||
messageText = NSAttributedString(string: foldLineBreaks(text), font: textFont, textColor: textColor)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
messageText = NSAttributedString(string: foldLineBreaks(textString.string), font: textFont, textColor: message.media.isEmpty || message.media.first is TelegramMediaWebpage ? theme.chat.inputPanel.primaryTextColor : theme.chat.inputPanel.secondaryTextColor)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let textConstrainedSize = CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude)
|
|
||||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: messageText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0)))
|
if isReplyThread {
|
||||||
|
let titleString: String
|
||||||
let spoilerTextLayoutAndApply: (TextNodeLayout, (TextNodeWithEntities.Arguments?) -> TextNodeWithEntities)?
|
if let author = message.effectiveAuthor {
|
||||||
if !textLayout.spoilers.isEmpty {
|
titleString = EnginePeer(author).displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
||||||
spoilerTextLayoutAndApply = makeSpoilerTextLayout(TextNodeLayoutArguments(attributedString: messageText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0), displaySpoilers: true, displayEmbeddedItemsUnderSpoilers: true))
|
|
||||||
} else {
|
} else {
|
||||||
spoilerTextLayoutAndApply = nil
|
titleString = ""
|
||||||
}
|
}
|
||||||
|
titleStrings = [.text(0, NSAttributedString(string: titleString, font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))]
|
||||||
|
} else {
|
||||||
|
for media in message.media {
|
||||||
|
if let media = media as? TelegramMediaInvoice {
|
||||||
|
titleStrings = [.text(0, NSAttributedString(string: media.title, font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var applyImage: (() -> Void)?
|
||||||
|
if let imageDimensions = imageDimensions {
|
||||||
|
let boundingSize = CGSize(width: 35.0, height: 35.0)
|
||||||
|
applyImage = imageNodeLayout(TransformImageArguments(corners: ImageCorners(radius: 2.0), imageSize: imageDimensions.aspectFilled(boundingSize), boundingSize: boundingSize, intrinsicInsets: UIEdgeInsets()))
|
||||||
|
|
||||||
Queue.mainQueue().async {
|
textLineInset += 9.0 + 35.0
|
||||||
if let strongSelf = self {
|
}
|
||||||
let _ = titleApply(animation != nil)
|
|
||||||
|
var mediaUpdated = false
|
||||||
var textArguments: TextNodeWithEntities.Arguments?
|
if let updatedMediaReference = updatedMediaReference, let previousMediaReference = previousMediaReference {
|
||||||
if let cache = strongSelf.animationCache, let renderer = strongSelf.animationRenderer {
|
mediaUpdated = !updatedMediaReference.media.isEqual(to: previousMediaReference.media)
|
||||||
textArguments = TextNodeWithEntities.Arguments(
|
} else if (updatedMediaReference != nil) != (previousMediaReference != nil) {
|
||||||
context: strongSelf.context,
|
mediaUpdated = true
|
||||||
cache: cache,
|
}
|
||||||
renderer: renderer,
|
|
||||||
placeholderColor: theme.list.mediaPlaceholderColor,
|
let hasSpoiler = message.attributes.contains(where: { $0 is MediaSpoilerMessageAttribute })
|
||||||
attemptSynchronous: false
|
|
||||||
)
|
var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
|
||||||
}
|
var updatedFetchMediaSignal: Signal<FetchResourceSourceType, FetchResourceError>?
|
||||||
let _ = textApply(textArguments)
|
if mediaUpdated {
|
||||||
|
if let updatedMediaReference = updatedMediaReference, imageDimensions != nil {
|
||||||
strongSelf.previousMediaReference = updatedMediaReference
|
if let imageReference = updatedMediaReference.concrete(TelegramMediaImage.self) {
|
||||||
|
if imageReference.media.representations.isEmpty {
|
||||||
animationTransition.updateFrameAdditive(node: strongSelf.contentTextContainer, frame: CGRect(origin: CGPoint(x: contentLeftInset + textLineInset, y: 0.0), size: CGSize(width: width, height: panelHeight)))
|
updateImageSignal = chatSecretPhoto(account: context.account, userLocation: .peer(message.id.peerId), photoReference: imageReference, ignoreFullSize: true, synchronousLoad: true)
|
||||||
|
|
||||||
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 5.0), size: titleLayout.size)
|
|
||||||
|
|
||||||
let textFrame = CGRect(origin: CGPoint(x: 0.0, y: 23.0), size: textLayout.size)
|
|
||||||
strongSelf.textNode.textNode.frame = textFrame
|
|
||||||
|
|
||||||
if let (_, spoilerTextApply) = spoilerTextLayoutAndApply {
|
|
||||||
let spoilerTextNode = spoilerTextApply(textArguments)
|
|
||||||
if strongSelf.spoilerTextNode == nil {
|
|
||||||
spoilerTextNode.textNode.alpha = 0.0
|
|
||||||
spoilerTextNode.textNode.isUserInteractionEnabled = false
|
|
||||||
spoilerTextNode.textNode.contentMode = .topLeft
|
|
||||||
spoilerTextNode.textNode.contentsScale = UIScreenScale
|
|
||||||
spoilerTextNode.textNode.displaysAsynchronously = false
|
|
||||||
strongSelf.contentTextContainer.insertSubnode(spoilerTextNode.textNode, aboveSubnode: strongSelf.textNode.textNode)
|
|
||||||
|
|
||||||
strongSelf.spoilerTextNode = spoilerTextNode
|
|
||||||
}
|
|
||||||
|
|
||||||
strongSelf.spoilerTextNode?.textNode.frame = textFrame
|
|
||||||
|
|
||||||
let dustNode: InvisibleInkDustNode
|
|
||||||
if let current = strongSelf.dustNode {
|
|
||||||
dustNode = current
|
|
||||||
} else {
|
|
||||||
dustNode = InvisibleInkDustNode(textNode: spoilerTextNode.textNode, enableAnimations: strongSelf.context.sharedContext.energyUsageSettings.fullTranslucency)
|
|
||||||
strongSelf.dustNode = dustNode
|
|
||||||
strongSelf.contentTextContainer.insertSubnode(dustNode, aboveSubnode: spoilerTextNode.textNode)
|
|
||||||
}
|
|
||||||
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
|
||||||
dustNode.update(size: dustNode.frame.size, color: theme.chat.inputPanel.secondaryTextColor, textColor: theme.chat.inputPanel.primaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
|
|
||||||
} else if let spoilerTextNode = strongSelf.spoilerTextNode {
|
|
||||||
strongSelf.spoilerTextNode = nil
|
|
||||||
spoilerTextNode.textNode.removeFromSupernode()
|
|
||||||
|
|
||||||
if let dustNode = strongSelf.dustNode {
|
|
||||||
strongSelf.dustNode = nil
|
|
||||||
dustNode.removeFromSupernode()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
strongSelf.textNode.visibilityRect = CGRect.infinite
|
|
||||||
strongSelf.spoilerTextNode?.visibilityRect = CGRect.infinite
|
|
||||||
|
|
||||||
let lineFrame = CGRect(origin: CGPoint(x: contentLeftInset, y: 0.0), size: CGSize(width: 2.0, height: panelHeight))
|
|
||||||
animationTransition.updateFrame(node: strongSelf.lineNode, frame: lineFrame)
|
|
||||||
strongSelf.lineNode.update(
|
|
||||||
colors: AnimatedNavigationStripeNode.Colors(
|
|
||||||
foreground: theme.chat.inputPanel.panelControlAccentColor,
|
|
||||||
background: theme.chat.inputPanel.panelControlAccentColor.withAlphaComponent(0.5),
|
|
||||||
clearBackground: theme.chat.inputPanel.panelBackgroundColor
|
|
||||||
),
|
|
||||||
configuration: AnimatedNavigationStripeNode.Configuration(
|
|
||||||
height: panelHeight,
|
|
||||||
index: pinnedMessage.index,
|
|
||||||
count: pinnedMessage.totalCount
|
|
||||||
),
|
|
||||||
transition: animationTransition
|
|
||||||
)
|
|
||||||
|
|
||||||
strongSelf.imageNodeContainer.frame = CGRect(origin: CGPoint(x: contentLeftInset + 9.0, y: 7.0), size: CGSize(width: 35.0, height: 35.0))
|
|
||||||
strongSelf.imageNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 35.0, height: 35.0))
|
|
||||||
|
|
||||||
if let applyImage = applyImage {
|
|
||||||
applyImage()
|
|
||||||
|
|
||||||
animationTransition.updateSublayerTransformScale(node: strongSelf.imageNodeContainer, scale: 1.0)
|
|
||||||
animationTransition.updateAlpha(node: strongSelf.imageNodeContainer, alpha: 1.0, beginWithCurrentState: true)
|
|
||||||
} else {
|
} else {
|
||||||
animationTransition.updateSublayerTransformScale(node: strongSelf.imageNodeContainer, scale: 0.1)
|
updateImageSignal = chatMessagePhotoThumbnail(account: context.account, userLocation: .peer(message.id.peerId), photoReference: imageReference, blurred: hasSpoiler)
|
||||||
animationTransition.updateAlpha(node: strongSelf.imageNodeContainer, alpha: 0.0, beginWithCurrentState: true)
|
|
||||||
}
|
}
|
||||||
|
} else if let fileReference = updatedMediaReference.concrete(TelegramMediaFile.self) {
|
||||||
if let updateImageSignal = updateImageSignal {
|
if fileReference.media.isAnimatedSticker {
|
||||||
strongSelf.imageNode.setSignal(updateImageSignal)
|
let dimensions = fileReference.media.dimensions ?? PixelDimensions(width: 512, height: 512)
|
||||||
|
updateImageSignal = chatMessageAnimatedSticker(postbox: context.account.postbox, userLocation: .peer(message.id.peerId), file: fileReference.media, small: false, size: dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0)))
|
||||||
|
updatedFetchMediaSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(message.id.peerId), userContentType: MediaResourceUserContentType(file: fileReference.media), reference: fileReference.resourceReference(fileReference.media.resource))
|
||||||
|
} else if fileReference.media.isVideo || fileReference.media.isAnimated {
|
||||||
|
updateImageSignal = chatMessageVideoThumbnail(account: context.account, userLocation: .peer(message.id.peerId), fileReference: fileReference, blurred: hasSpoiler)
|
||||||
|
} else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
|
||||||
|
updateImageSignal = chatWebpageSnippetFile(account: context.account, userLocation: .peer(message.id.peerId), mediaReference: fileReference.abstract, representation: iconImageRepresentation)
|
||||||
}
|
}
|
||||||
if let updatedFetchMediaSignal = updatedFetchMediaSignal {
|
}
|
||||||
strongSelf.fetchDisposable.set(updatedFetchMediaSignal.startStrict())
|
} else {
|
||||||
|
updateImageSignal = .single({ _ in return nil })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (titleLayout, titleApply) = makeTitleLayout(CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), .zero, titleStrings)
|
||||||
|
|
||||||
|
let (textString, _, isText) = descriptionStringForMessage(contentSettings: context.currentContentSettings.with { $0 }, message: EngineMessage(message), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, accountPeerId: accountPeerId)
|
||||||
|
|
||||||
|
let messageText: NSAttributedString
|
||||||
|
let textFont = Font.regular(15.0)
|
||||||
|
if let giveaway {
|
||||||
|
let dateString = stringForDateWithoutYear(date: Date(timeIntervalSince1970: TimeInterval(giveaway.untilDate)), timeZone: .current, strings: strings)
|
||||||
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
|
let isFinished = currentTime >= giveaway.untilDate
|
||||||
|
let text: String
|
||||||
|
if isFinished {
|
||||||
|
let winnersString = strings.Conversation_PinnedGiveaway_Finished_Winners(giveaway.quantity)
|
||||||
|
text = strings.Conversation_PinnedGiveaway_Finished(winnersString, dateString).string
|
||||||
|
} else {
|
||||||
|
let winnersString = strings.Conversation_PinnedGiveaway_Ongoing_Winners(giveaway.quantity)
|
||||||
|
text = strings.Conversation_PinnedGiveaway_Ongoing(winnersString, dateString).string
|
||||||
|
}
|
||||||
|
messageText = NSAttributedString(string: text, font: textFont, textColor: theme.chat.inputPanel.primaryTextColor)
|
||||||
|
} else if isText {
|
||||||
|
var text = message.text
|
||||||
|
var messageEntities = message.textEntitiesAttribute?.entities ?? []
|
||||||
|
|
||||||
|
if let translateToLanguage = translateToLanguage, !text.isEmpty {
|
||||||
|
for attribute in message.attributes {
|
||||||
|
if let attribute = attribute as? TranslationMessageAttribute, !attribute.text.isEmpty, attribute.toLang == translateToLanguage {
|
||||||
|
text = attribute.text
|
||||||
|
messageEntities = attribute.entities
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let entities = messageEntities.filter { entity in
|
||||||
|
switch entity.type {
|
||||||
|
case .Spoiler, .CustomEmoji:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let textColor = theme.chat.inputPanel.primaryTextColor
|
||||||
|
if entities.count > 0 {
|
||||||
|
messageText = stringWithAppliedEntities(trimToLineCount(text, lineCount: 1), entities: entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message)
|
||||||
|
} else {
|
||||||
|
messageText = NSAttributedString(string: foldLineBreaks(text), font: textFont, textColor: textColor)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
messageText = NSAttributedString(string: foldLineBreaks(textString.string), font: textFont, textColor: message.media.isEmpty || message.media.first is TelegramMediaWebpage ? theme.chat.inputPanel.primaryTextColor : theme.chat.inputPanel.secondaryTextColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
let textConstrainedSize = CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude)
|
||||||
|
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: messageText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0)))
|
||||||
|
|
||||||
|
let spoilerTextLayoutAndApply: (TextNodeLayout, (TextNodeWithEntities.Arguments?) -> TextNodeWithEntities)?
|
||||||
|
if !textLayout.spoilers.isEmpty {
|
||||||
|
spoilerTextLayoutAndApply = makeSpoilerTextLayout(TextNodeLayoutArguments(attributedString: messageText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0), displaySpoilers: true, displayEmbeddedItemsUnderSpoilers: true))
|
||||||
|
} else {
|
||||||
|
spoilerTextLayoutAndApply = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let strongSelf = self
|
||||||
|
let _ = titleApply(animation != nil)
|
||||||
|
|
||||||
|
var textArguments: TextNodeWithEntities.Arguments?
|
||||||
|
if let cache = strongSelf.animationCache, let renderer = strongSelf.animationRenderer {
|
||||||
|
textArguments = TextNodeWithEntities.Arguments(
|
||||||
|
context: strongSelf.context,
|
||||||
|
cache: cache,
|
||||||
|
renderer: renderer,
|
||||||
|
placeholderColor: theme.list.mediaPlaceholderColor,
|
||||||
|
attemptSynchronous: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let _ = textApply(textArguments)
|
||||||
|
|
||||||
|
strongSelf.previousMediaReference = updatedMediaReference
|
||||||
|
|
||||||
|
animationTransition.updateFrameAdditive(node: strongSelf.contentTextContainer, frame: CGRect(origin: CGPoint(x: contentLeftInset + textLineInset, y: 0.0), size: CGSize(width: width, height: panelHeight)))
|
||||||
|
|
||||||
|
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 5.0), size: titleLayout.size)
|
||||||
|
|
||||||
|
let textFrame = CGRect(origin: CGPoint(x: 0.0, y: 23.0), size: textLayout.size)
|
||||||
|
strongSelf.textNode.textNode.frame = textFrame
|
||||||
|
|
||||||
|
if let (_, spoilerTextApply) = spoilerTextLayoutAndApply {
|
||||||
|
let spoilerTextNode = spoilerTextApply(textArguments)
|
||||||
|
if strongSelf.spoilerTextNode == nil {
|
||||||
|
spoilerTextNode.textNode.alpha = 0.0
|
||||||
|
spoilerTextNode.textNode.isUserInteractionEnabled = false
|
||||||
|
spoilerTextNode.textNode.contentMode = .topLeft
|
||||||
|
spoilerTextNode.textNode.contentsScale = UIScreenScale
|
||||||
|
spoilerTextNode.textNode.displaysAsynchronously = false
|
||||||
|
strongSelf.contentTextContainer.insertSubnode(spoilerTextNode.textNode, aboveSubnode: strongSelf.textNode.textNode)
|
||||||
|
|
||||||
|
strongSelf.spoilerTextNode = spoilerTextNode
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.spoilerTextNode?.textNode.frame = textFrame
|
||||||
|
|
||||||
|
let dustNode: InvisibleInkDustNode
|
||||||
|
if let current = strongSelf.dustNode {
|
||||||
|
dustNode = current
|
||||||
|
} else {
|
||||||
|
dustNode = InvisibleInkDustNode(textNode: spoilerTextNode.textNode, enableAnimations: strongSelf.context.sharedContext.energyUsageSettings.fullTranslucency)
|
||||||
|
strongSelf.dustNode = dustNode
|
||||||
|
strongSelf.contentTextContainer.insertSubnode(dustNode, aboveSubnode: spoilerTextNode.textNode)
|
||||||
|
}
|
||||||
|
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
||||||
|
dustNode.update(size: dustNode.frame.size, color: theme.chat.inputPanel.secondaryTextColor, textColor: theme.chat.inputPanel.primaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
|
||||||
|
} else if let spoilerTextNode = strongSelf.spoilerTextNode {
|
||||||
|
strongSelf.spoilerTextNode = nil
|
||||||
|
spoilerTextNode.textNode.removeFromSupernode()
|
||||||
|
|
||||||
|
if let dustNode = strongSelf.dustNode {
|
||||||
|
strongSelf.dustNode = nil
|
||||||
|
dustNode.removeFromSupernode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.textNode.visibilityRect = CGRect.infinite
|
||||||
|
strongSelf.spoilerTextNode?.visibilityRect = CGRect.infinite
|
||||||
|
|
||||||
|
let lineFrame = CGRect(origin: CGPoint(x: contentLeftInset, y: 0.0), size: CGSize(width: 2.0, height: panelHeight))
|
||||||
|
animationTransition.updateFrame(node: strongSelf.lineNode, frame: lineFrame)
|
||||||
|
strongSelf.lineNode.update(
|
||||||
|
colors: AnimatedNavigationStripeNode.Colors(
|
||||||
|
foreground: theme.chat.inputPanel.panelControlAccentColor,
|
||||||
|
background: theme.chat.inputPanel.panelControlAccentColor.withAlphaComponent(0.5),
|
||||||
|
clearBackground: theme.chat.inputPanel.panelBackgroundColor
|
||||||
|
),
|
||||||
|
configuration: AnimatedNavigationStripeNode.Configuration(
|
||||||
|
height: panelHeight,
|
||||||
|
index: pinnedMessage.index,
|
||||||
|
count: pinnedMessage.totalCount
|
||||||
|
),
|
||||||
|
transition: animationTransition
|
||||||
|
)
|
||||||
|
|
||||||
|
strongSelf.imageNodeContainer.frame = CGRect(origin: CGPoint(x: contentLeftInset + 9.0, y: 7.0), size: CGSize(width: 35.0, height: 35.0))
|
||||||
|
strongSelf.imageNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 35.0, height: 35.0))
|
||||||
|
|
||||||
|
if let applyImage = applyImage {
|
||||||
|
applyImage()
|
||||||
|
|
||||||
|
animationTransition.updateSublayerTransformScale(node: strongSelf.imageNodeContainer, scale: 1.0)
|
||||||
|
animationTransition.updateAlpha(node: strongSelf.imageNodeContainer, alpha: 1.0, beginWithCurrentState: true)
|
||||||
|
} else {
|
||||||
|
animationTransition.updateSublayerTransformScale(node: strongSelf.imageNodeContainer, scale: 0.1)
|
||||||
|
animationTransition.updateAlpha(node: strongSelf.imageNodeContainer, alpha: 0.0, beginWithCurrentState: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let updateImageSignal = updateImageSignal {
|
||||||
|
strongSelf.imageNode.setSignal(updateImageSignal)
|
||||||
|
}
|
||||||
|
if let updatedFetchMediaSignal = updatedFetchMediaSignal {
|
||||||
|
strongSelf.fetchDisposable.set(updatedFetchMediaSignal.startStrict())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -246,7 +246,7 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
|
|
||||||
private let containerButton: HighlightTrackingButton
|
private let containerButton: HighlightTrackingButton
|
||||||
|
|
||||||
private let icon = ComponentView<Empty>()
|
private var icon: ComponentView<Empty>?
|
||||||
private var avatarNode: AvatarNode?
|
private var avatarNode: AvatarNode?
|
||||||
private let title = ComponentView<Empty>()
|
private let title = ComponentView<Empty>()
|
||||||
private var badge: ComponentView<Empty>?
|
private var badge: ComponentView<Empty>?
|
||||||
@ -319,33 +319,60 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
let spacing: CGFloat = 3.0
|
let spacing: CGFloat = 3.0
|
||||||
let badgeSpacing: CGFloat = 4.0
|
let badgeSpacing: CGFloat = 4.0
|
||||||
|
|
||||||
let avatarIconContent: EmojiStatusComponent.Content
|
let iconSize = CGSize(width: 18.0, height: 18.0)
|
||||||
if case let .forum(topicId) = item.item.id, topicId != 1, let threadData = item.item.threadData {
|
|
||||||
if let fileId = threadData.info.icon, fileId != 0 {
|
var avatarIconContent: EmojiStatusComponent.Content?
|
||||||
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 18.0, height: 18.0), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .count(0))
|
if case let .forum(topicId) = item.item.id {
|
||||||
|
if topicId != 1, let threadData = item.item.threadData {
|
||||||
|
if let fileId = threadData.info.icon, fileId != 0 {
|
||||||
|
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: iconSize, placeholderColor: theme.list.mediaPlaceholderColor, themeColor: theme.list.itemAccentColor, loopMode: .count(0))
|
||||||
|
} else {
|
||||||
|
avatarIconContent = .topic(title: String(threadData.info.title.prefix(1)), color: threadData.info.iconColor, size: iconSize)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
avatarIconContent = .topic(title: String(threadData.info.title.prefix(1)), color: threadData.info.iconColor, size: CGSize(width: 18.0, height: 18.0))
|
avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicIcon(theme), tintColor: theme.rootController.navigationBar.secondaryTextColor)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
avatarIconContent = .image(image: PresentationResourcesChatList.generalTopicIcon(theme))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let avatarIconComponent = EmojiStatusComponent(
|
if let avatarIconContent {
|
||||||
context: context,
|
let avatarIconComponent = EmojiStatusComponent(
|
||||||
animationCache: context.animationCache,
|
context: context,
|
||||||
animationRenderer: context.animationRenderer,
|
animationCache: context.animationCache,
|
||||||
content: avatarIconContent,
|
animationRenderer: context.animationRenderer,
|
||||||
isVisibleForAnimations: false,
|
content: avatarIconContent,
|
||||||
action: nil
|
isVisibleForAnimations: false,
|
||||||
)
|
action: nil
|
||||||
let iconSize = self.icon.update(
|
)
|
||||||
transition: .immediate,
|
let icon: ComponentView<Empty>
|
||||||
component: AnyComponent(avatarIconComponent),
|
if let current = self.icon {
|
||||||
environment: {},
|
icon = current
|
||||||
containerSize: CGSize(width: 18.0, height: 18.0)
|
} else {
|
||||||
)
|
icon = ComponentView()
|
||||||
|
self.icon = icon
|
||||||
|
}
|
||||||
|
let _ = icon.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(avatarIconComponent),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: 18.0, height: 18.0)
|
||||||
|
)
|
||||||
|
} else if let icon = self.icon {
|
||||||
|
self.icon = nil
|
||||||
|
icon.view?.removeFromSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
let titleText: String = item.item.renderedPeer.chatMainPeer?.compactDisplayTitle ?? " "
|
let titleText: String
|
||||||
|
if case let .forum(topicId) = item.item.id {
|
||||||
|
let _ = topicId
|
||||||
|
if let threadData = item.item.threadData {
|
||||||
|
titleText = threadData.info.title
|
||||||
|
} else {
|
||||||
|
//TODO:localize
|
||||||
|
titleText = "General"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
titleText = item.item.renderedPeer.chatMainPeer?.compactDisplayTitle ?? " "
|
||||||
|
}
|
||||||
let titleSize = self.title.update(
|
let titleSize = self.title.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(MultilineTextComponent(
|
component: AnyComponent(MultilineTextComponent(
|
||||||
@ -391,38 +418,37 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
let iconFrame = CGRect(origin: CGPoint(x: 0.0, y: 5.0 + floor((size.height - iconSize.height) * 0.5)), size: iconSize)
|
let iconFrame = CGRect(origin: CGPoint(x: 0.0, y: 5.0 + floor((size.height - iconSize.height) * 0.5)), size: iconSize)
|
||||||
let titleFrame = CGRect(origin: CGPoint(x: iconFrame.maxX + spacing, y: 5.0 + floor((size.height - titleSize.height) * 0.5)), size: titleSize)
|
let titleFrame = CGRect(origin: CGPoint(x: iconFrame.maxX + spacing, y: 5.0 + floor((size.height - titleSize.height) * 0.5)), size: titleSize)
|
||||||
|
|
||||||
if let iconView = self.icon.view {
|
if let icon = self.icon {
|
||||||
if iconView.superview == nil {
|
if let iconView = icon.view {
|
||||||
iconView.isUserInteractionEnabled = false
|
if iconView.superview == nil {
|
||||||
self.containerButton.addSubview(iconView)
|
iconView.isUserInteractionEnabled = false
|
||||||
|
self.containerButton.addSubview(iconView)
|
||||||
|
}
|
||||||
|
iconView.frame = iconFrame
|
||||||
}
|
}
|
||||||
iconView.frame = iconFrame
|
|
||||||
|
|
||||||
if "".isEmpty {
|
if let avatarNode = self.avatarNode {
|
||||||
iconView.isHidden = true
|
|
||||||
|
|
||||||
let avatarNode: AvatarNode
|
|
||||||
if let current = self.avatarNode {
|
|
||||||
avatarNode = current
|
|
||||||
} else {
|
|
||||||
avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 7.0))
|
|
||||||
self.avatarNode = avatarNode
|
|
||||||
self.containerButton.addSubview(avatarNode.view)
|
|
||||||
}
|
|
||||||
avatarNode.frame = iconFrame
|
|
||||||
avatarNode.updateSize(size: iconFrame.size)
|
|
||||||
|
|
||||||
if let peer = item.item.renderedPeer.chatMainPeer {
|
|
||||||
if peer.smallProfileImage != nil {
|
|
||||||
avatarNode.setPeerV2(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size)
|
|
||||||
} else {
|
|
||||||
avatarNode.setPeer(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if let avatarNode = self.avatarNode {
|
|
||||||
self.avatarNode = nil
|
self.avatarNode = nil
|
||||||
avatarNode.view.removeFromSuperview()
|
avatarNode.view.removeFromSuperview()
|
||||||
iconView.isHidden = false
|
}
|
||||||
|
} else {
|
||||||
|
let avatarNode: AvatarNode
|
||||||
|
if let current = self.avatarNode {
|
||||||
|
avatarNode = current
|
||||||
|
} else {
|
||||||
|
avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 7.0))
|
||||||
|
self.avatarNode = avatarNode
|
||||||
|
self.containerButton.addSubview(avatarNode.view)
|
||||||
|
}
|
||||||
|
avatarNode.frame = iconFrame
|
||||||
|
avatarNode.updateSize(size: iconFrame.size)
|
||||||
|
|
||||||
|
if let peer = item.item.renderedPeer.chatMainPeer {
|
||||||
|
if peer.smallProfileImage != nil {
|
||||||
|
avatarNode.setPeerV2(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size)
|
||||||
|
} else {
|
||||||
|
avatarNode.setPeer(context: context, theme: theme, peer: peer, overrideImage: nil, emptyColor: .gray, clipStyle: .round, synchronousLoad: false, displayDimensions: iconFrame.size)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -707,6 +733,7 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
}
|
}
|
||||||
|
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
|
private let isMonoforum: Bool
|
||||||
|
|
||||||
private let scrollView: ScrollView
|
private let scrollView: ScrollView
|
||||||
|
|
||||||
@ -722,8 +749,9 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
|
|
||||||
private var appliedScrollToId: ScrollId?
|
private var appliedScrollToId: ScrollId?
|
||||||
|
|
||||||
init(context: AccountContext, peerId: EnginePeer.Id) {
|
init(context: AccountContext, peerId: EnginePeer.Id, isMonoforum: Bool) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
self.isMonoforum = isMonoforum
|
||||||
|
|
||||||
self.selectedLineView = UIImageView()
|
self.selectedLineView = UIImageView()
|
||||||
|
|
||||||
@ -751,78 +779,7 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
|
|
||||||
self.scrollView.disablesInteractiveTransitionGestureRecognizer = true
|
self.scrollView.disablesInteractiveTransitionGestureRecognizer = true
|
||||||
|
|
||||||
let viewKey: PostboxViewKey = .savedMessagesIndex(peerId: peerId)
|
let threadListSignal: Signal<EngineChatList, NoError> = context.sharedContext.subscribeChatListData(context: context, location: isMonoforum ? .savedMessagesChats(peerId: peerId) : .forum(peerId: peerId))
|
||||||
let interfaceStateKey: PostboxViewKey = .chatInterfaceState(peerId: peerId)
|
|
||||||
|
|
||||||
let accountPeerId = context.account.peerId
|
|
||||||
let threadListSignal: Signal<EngineChatList, NoError> = context.account.postbox.combinedView(keys: [viewKey, interfaceStateKey])
|
|
||||||
|> map { views -> EngineChatList in
|
|
||||||
guard let view = views.views[viewKey] as? MessageHistorySavedMessagesIndexView else {
|
|
||||||
preconditionFailure()
|
|
||||||
}
|
|
||||||
|
|
||||||
var draft: EngineChatList.Draft?
|
|
||||||
if let interfaceStateView = views.views[interfaceStateKey] as? ChatInterfaceStateView {
|
|
||||||
if let embeddedState = interfaceStateView.value, let _ = embeddedState.overrideChatTimestamp {
|
|
||||||
if let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) {
|
|
||||||
if let text = opaqueState.synchronizeableInputState?.text {
|
|
||||||
draft = EngineChatList.Draft(text: text, entities: opaqueState.synchronizeableInputState?.entities ?? [])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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: false),
|
|
||||||
isMuted: false,
|
|
||||||
draft: sourceId == accountPeerId ? 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
|
|
||||||
}
|
|
||||||
|
|
||||||
self.itemsDisposable = (threadListSignal
|
self.itemsDisposable = (threadListSignal
|
||||||
|> deliverOnMainQueue).startStrict(next: { [weak self] chatList in
|
|> deliverOnMainQueue).startStrict(next: { [weak self] chatList in
|
||||||
@ -951,7 +908,7 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.interfaceInteraction?.updateChatLocationThread(nil)
|
self.interfaceInteraction?.updateChatLocationThread(nil, .left)
|
||||||
})
|
})
|
||||||
self.allItemView = itemView
|
self.allItemView = itemView
|
||||||
self.scrollView.addSubview(itemView)
|
self.scrollView.addSubview(itemView)
|
||||||
@ -1001,8 +958,20 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let topicId = chatListItem.renderedPeer.peerId.toInt64()
|
|
||||||
self.interfaceInteraction?.updateChatLocationThread(topicId)
|
let topicId: Int64
|
||||||
|
if case let .forum(topicIdValue) = chatListItem.id {
|
||||||
|
topicId = topicIdValue
|
||||||
|
} else {
|
||||||
|
topicId = chatListItem.renderedPeer.peerId.toInt64()
|
||||||
|
}
|
||||||
|
|
||||||
|
var direction = true
|
||||||
|
if let params = self.params, let lhsIndex = self.topicIndex(threadId: params.interfaceState.chatLocation.threadId), let rhsIndex = self.topicIndex(threadId: topicId) {
|
||||||
|
direction = lhsIndex < rhsIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
self.interfaceInteraction?.updateChatLocationThread(topicId, direction ? .right : .left)
|
||||||
}, contextGesture: { gesture, sourceNode in
|
}, contextGesture: { gesture, sourceNode in
|
||||||
})
|
})
|
||||||
self.itemViews[itemId] = itemView
|
self.itemViews[itemId] = itemView
|
||||||
@ -1010,8 +979,10 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
}
|
}
|
||||||
|
|
||||||
var isSelected = false
|
var isSelected = false
|
||||||
if params.interfaceState.chatLocation.threadId == item.item.renderedPeer.peerId.toInt64() {
|
if case let .forum(topicId) = item.item.id {
|
||||||
isSelected = true
|
isSelected = params.interfaceState.chatLocation.threadId == topicId
|
||||||
|
} else {
|
||||||
|
isSelected = params.interfaceState.chatLocation.threadId == item.item.renderedPeer.peerId.toInt64()
|
||||||
}
|
}
|
||||||
let itemSize = itemView.update(context: self.context, item: item, isSelected: isSelected, theme: params.interfaceState.theme, height: panelHeight, transition: .immediate)
|
let itemSize = itemView.update(context: self.context, item: item, isSelected: isSelected, 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: contentSize.width, y: -5.0), size: itemSize)
|
||||||
@ -1100,7 +1071,15 @@ final class ChatTopicListTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, C
|
|||||||
|
|
||||||
public func topicIndex(threadId: Int64?) -> Int? {
|
public func topicIndex(threadId: Int64?) -> Int? {
|
||||||
if let threadId {
|
if let threadId {
|
||||||
if let value = self.items.firstIndex(where: { $0.id == .chatList(PeerId(threadId)) }) {
|
if let value = self.items.firstIndex(where: { item in
|
||||||
|
if item.id == .chatList(PeerId(threadId)) {
|
||||||
|
return true
|
||||||
|
} else if item.id == .forum(threadId) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}) {
|
||||||
return value + 1
|
return value + 1
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -805,7 +805,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
|||||||
self.suspendedNavigationBarLayout = layout
|
self.suspendedNavigationBarLayout = layout
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: transition)
|
self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, additionalCutout: nil, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
@ -818,7 +818,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
|||||||
self.suspendNavigationBarLayout = false
|
self.suspendNavigationBarLayout = false
|
||||||
if let suspendedNavigationBarLayout = self.suspendedNavigationBarLayout {
|
if let suspendedNavigationBarLayout = self.suspendedNavigationBarLayout {
|
||||||
self.suspendedNavigationBarLayout = suspendedNavigationBarLayout
|
self.suspendedNavigationBarLayout = suspendedNavigationBarLayout
|
||||||
self.applyNavigationBarLayout(suspendedNavigationBarLayout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: transition)
|
self.applyNavigationBarLayout(suspendedNavigationBarLayout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, additionalCutout: nil, transition: transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -138,7 +138,15 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
|
|||||||
isFirst = false
|
isFirst = false
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if controller.chatLocation.peerId == params.chatLocation.asChatLocation.peerId && controller.chatLocation.threadId == params.chatLocation.asChatLocation.threadId && (controller.subject != .scheduledMessages || controller.subject == params.subject) {
|
|
||||||
|
var canMatchThread = controller.chatLocation.threadId == params.chatLocation.asChatLocation.threadId
|
||||||
|
var switchToThread = false
|
||||||
|
if !canMatchThread && controller.chatLocation.peerId == params.chatLocation.asChatLocation.peerId && controller.subject == nil {
|
||||||
|
canMatchThread = true
|
||||||
|
switchToThread = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if controller.chatLocation.peerId == params.chatLocation.asChatLocation.peerId && canMatchThread && (controller.subject != .scheduledMessages || controller.subject == params.subject) {
|
||||||
if let updateTextInputState = params.updateTextInputState {
|
if let updateTextInputState = params.updateTextInputState {
|
||||||
controller.updateTextInputState(updateTextInputState)
|
controller.updateTextInputState(updateTextInputState)
|
||||||
}
|
}
|
||||||
@ -164,6 +172,10 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
|
|||||||
controller.beginReportSelection(reason: reportReason)
|
controller.beginReportSelection(reason: reportReason)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if switchToThread {
|
||||||
|
controller.updateChatLocationThread(threadId: params.chatLocation.threadId, animationDirection: nil)
|
||||||
|
}
|
||||||
|
|
||||||
if popAndComplete {
|
if popAndComplete {
|
||||||
if let _ = params.navigationController.viewControllers.last as? AttachmentController, let controller = params.navigationController.viewControllers[params.navigationController.viewControllers.count - 2] as? ChatControllerImpl, controller.chatLocation == params.chatLocation.asChatLocation {
|
if let _ = params.navigationController.viewControllers.last as? AttachmentController, let controller = params.navigationController.viewControllers[params.navigationController.viewControllers.count - 2] as? ChatControllerImpl, controller.chatLocation == params.chatLocation.asChatLocation {
|
||||||
|
|
||||||
@ -376,7 +388,7 @@ public func isOverlayControllerForChatNotificationOverlayPresentation(_ controll
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
public func navigateToForumThreadImpl(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64, messageId: EngineMessage.Id?, navigationController: NavigationController, activateInput: ChatControllerActivateInput?, scrollToEndIfExists: Bool, keepStack: NavigateToChatKeepStack) -> Signal<Never, NoError> {
|
public func navigateToForumThreadImpl(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64, messageId: EngineMessage.Id?, navigationController: NavigationController, activateInput: ChatControllerActivateInput?, scrollToEndIfExists: Bool, keepStack: NavigateToChatKeepStack, animated: Bool) -> Signal<Never, NoError> {
|
||||||
return fetchAndPreloadReplyThreadInfo(context: context, subject: .groupMessage(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId))), atMessageId: messageId, preload: false)
|
return fetchAndPreloadReplyThreadInfo(context: context, subject: .groupMessage(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId))), atMessageId: messageId, preload: false)
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> beforeNext { [weak context, weak navigationController] result in
|
|> beforeNext { [weak context, weak navigationController] result in
|
||||||
@ -399,7 +411,7 @@ public func navigateToForumThreadImpl(context: AccountContext, peerId: EnginePee
|
|||||||
activateInput: actualActivateInput,
|
activateInput: actualActivateInput,
|
||||||
keepStack: keepStack,
|
keepStack: keepStack,
|
||||||
scrollToEndIfExists: scrollToEndIfExists,
|
scrollToEndIfExists: scrollToEndIfExists,
|
||||||
animated: !scrollToEndIfExists
|
animated: !scrollToEndIfExists && animated
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -220,7 +220,7 @@ func openResolvedUrlImpl(
|
|||||||
}
|
}
|
||||||
case let .replyThread(messageId):
|
case let .replyThread(messageId):
|
||||||
if let navigationController = navigationController {
|
if let navigationController = navigationController {
|
||||||
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: messageId.peerId, threadId: Int64(messageId.id), messageId: nil, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .always).startStandalone()
|
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: messageId.peerId, threadId: Int64(messageId.id), messageId: nil, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .always, animated: true).startStandalone()
|
||||||
}
|
}
|
||||||
case let .stickerPack(name, _):
|
case let .stickerPack(name, _):
|
||||||
dismissInput()
|
dismissInput()
|
||||||
|
|||||||
@ -193,7 +193,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
}, forceUpdateWarpContents: {
|
}, forceUpdateWarpContents: {
|
||||||
}, playShakeAnimation: {
|
}, playShakeAnimation: {
|
||||||
}, displayQuickShare: { _, _ ,_ in
|
}, displayQuickShare: { _, _ ,_ in
|
||||||
}, updateChatLocationThread: { _ in
|
}, updateChatLocationThread: { _, _ in
|
||||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil))
|
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil))
|
||||||
|
|
||||||
self.dimNode = ASDisplayNode()
|
self.dimNode = ASDisplayNode()
|
||||||
|
|||||||
@ -2127,8 +2127,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
navigateToForumChannelImpl(context: context, peerId: peerId, navigationController: navigationController)
|
navigateToForumChannelImpl(context: context, peerId: peerId, navigationController: navigationController)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func navigateToForumThread(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64, messageId: EngineMessage.Id?, navigationController: NavigationController, activateInput: ChatControllerActivateInput?, scrollToEndIfExists: Bool, keepStack: NavigateToChatKeepStack) -> Signal<Never, NoError> {
|
public func navigateToForumThread(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64, messageId: EngineMessage.Id?, navigationController: NavigationController, activateInput: ChatControllerActivateInput?, scrollToEndIfExists: Bool, keepStack: NavigateToChatKeepStack, animated: Bool) -> Signal<Never, NoError> {
|
||||||
return navigateToForumThreadImpl(context: context, peerId: peerId, threadId: threadId, messageId: messageId, navigationController: navigationController, activateInput: activateInput, scrollToEndIfExists: scrollToEndIfExists, keepStack: keepStack)
|
return navigateToForumThreadImpl(context: context, peerId: peerId, threadId: threadId, messageId: messageId, navigationController: navigationController, activateInput: activateInput, scrollToEndIfExists: scrollToEndIfExists, keepStack: keepStack, animated: animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func chatControllerForForumThread(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64) -> Signal<ChatController, NoError> {
|
public func chatControllerForForumThread(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64) -> Signal<ChatController, NoError> {
|
||||||
@ -2239,6 +2239,13 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func subscribeChatListData(context: AccountContext, location: ChatListControllerLocation) -> Signal<EngineChatList, NoError> {
|
||||||
|
return chatListViewForLocation(chatListLocation: location, location: .initial(count: 100, filter: nil), account: context.account, shouldLoadCanMessagePeer: false)
|
||||||
|
|> map { update -> EngineChatList in
|
||||||
|
return update.list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func makePeerSharedMediaController(context: AccountContext, peerId: PeerId) -> ViewController? {
|
public func makePeerSharedMediaController(context: AccountContext, peerId: PeerId) -> ViewController? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -2389,7 +2396,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}, forceUpdateWarpContents: {
|
}, forceUpdateWarpContents: {
|
||||||
}, playShakeAnimation: {
|
}, playShakeAnimation: {
|
||||||
}, displayQuickShare: { _, _ ,_ in
|
}, displayQuickShare: { _, _ ,_ in
|
||||||
}, updateChatLocationThread: { _ in
|
}, updateChatLocationThread: { _, _ in
|
||||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||||
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: backgroundNode as? WallpaperBackgroundNode))
|
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: backgroundNode as? WallpaperBackgroundNode))
|
||||||
|
|
||||||
@ -2410,7 +2417,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader {
|
public func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader {
|
||||||
return ChatMessageDateHeader(timestamp: timestamp, separableThreadId: nil, scheduled: false, displayPeer: nil, presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, chatBubbleCorners: chatBubbleCorners, animatedEmojiScale: 1.0, isPreview: true), controllerInteraction: nil, context: context)
|
return ChatMessageDateHeader(timestamp: timestamp, separableThreadId: nil, scheduled: false, displayHeader: nil, presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), fontSize: fontSize, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameOrder, disableAnimations: false, largeEmoji: false, chatBubbleCorners: chatBubbleCorners, animatedEmojiScale: 1.0, isPreview: true), controllerInteraction: nil, context: context)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeChatMessageAvatarHeaderItem(context: AccountContext, timestamp: Int32, peer: Peer, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader {
|
public func makeChatMessageAvatarHeaderItem(context: AccountContext, timestamp: Int32, peer: Peer, message: Message, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader {
|
||||||
|
|||||||
@ -91,7 +91,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: EnginePeer.Id?, n
|
|||||||
}
|
}
|
||||||
case let .replyThread(messageId):
|
case let .replyThread(messageId):
|
||||||
if let navigationController = controller.navigationController as? NavigationController {
|
if let navigationController = controller.navigationController as? NavigationController {
|
||||||
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: messageId.peerId, threadId: Int64(messageId.id), messageId: nil, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .always).start()
|
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: messageId.peerId, threadId: Int64(messageId.id), messageId: nil, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .always, animated: true).start()
|
||||||
}
|
}
|
||||||
case let .stickerPack(name, _):
|
case let .stickerPack(name, _):
|
||||||
let packReference: StickerPackReference = .name(name)
|
let packReference: StickerPackReference = .name(name)
|
||||||
|
|||||||
@ -1151,7 +1151,7 @@ public class TranslateScreen: ViewController {
|
|||||||
|
|
||||||
layout.statusBarHeight = nil
|
layout.statusBarHeight = nil
|
||||||
|
|
||||||
self.applyNavigationBarLayout(layout, navigationLayout: navigationLayout, additionalBackgroundHeight: 0.0, transition: transition)
|
self.applyNavigationBarLayout(layout, navigationLayout: navigationLayout, additionalBackgroundHeight: 0.0, additionalCutout: nil, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user