mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Topics
This commit is contained in:
parent
533085c77a
commit
62697d4ffb
@ -271,11 +271,11 @@ public enum StickerPackUrlType {
|
||||
public enum ResolvedUrl {
|
||||
case externalUrl(String)
|
||||
case urlAuth(String)
|
||||
case peer(PeerId?, ChatControllerInteractionNavigateToPeer)
|
||||
case peer(Peer?, ChatControllerInteractionNavigateToPeer)
|
||||
case inaccessiblePeer
|
||||
case botStart(peerId: PeerId, payload: String)
|
||||
case botStart(peer: Peer, payload: String)
|
||||
case groupBotStart(peerId: PeerId, payload: String, adminRights: ResolvedBotAdminRights?)
|
||||
case channelMessage(peerId: PeerId, messageId: MessageId, timecode: Double?)
|
||||
case channelMessage(peer: Peer, messageId: MessageId, timecode: Double?)
|
||||
case replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage, messageId: MessageId)
|
||||
case stickerPack(name: String, type: StickerPackUrlType)
|
||||
case instantView(TelegramMediaWebpage, String?)
|
||||
@ -718,7 +718,7 @@ public protocol SharedAccountContext: AnyObject {
|
||||
func makeDeviceContactInfoController(context: AccountContext, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController
|
||||
func makePeersNearbyController(context: AccountContext) -> ViewController
|
||||
func makeComposeController(context: AccountContext) -> ViewController
|
||||
func makeChatListController(context: AccountContext, groupId: PeerGroupId, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool, previewing: Bool, enableDebugActions: Bool) -> ChatListController
|
||||
func makeChatListController(context: AccountContext, location: ChatListControllerLocation, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool, previewing: Bool, enableDebugActions: Bool) -> ChatListController
|
||||
func makeChatController(context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, botStart: ChatControllerInitialBotStart?, mode: ChatControllerPresentationMode) -> ChatController
|
||||
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: (() -> Void)?, backgroundNode: ASDisplayNode?, availableReactions: AvailableReactions?, isCentered: Bool) -> ListViewItem
|
||||
func makeChatMessageDateHeaderItem(context: AccountContext, timestamp: Int32, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, chatBubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameOrder: PresentationPersonNameOrder) -> ListViewItemHeader
|
||||
@ -740,7 +740,7 @@ public protocol SharedAccountContext: AnyObject {
|
||||
func chatAvailableMessageActions(engine: TelegramEngine, accountPeerId: EnginePeer.Id, messageIds: Set<EngineMessage.Id>) -> Signal<ChatAvailableMessageActions, NoError>
|
||||
func chatAvailableMessageActions(engine: TelegramEngine, accountPeerId: EnginePeer.Id, messageIds: Set<EngineMessage.Id>, messages: [EngineMessage.Id: EngineMessage], peers: [EnginePeer.Id: EnginePeer]) -> Signal<ChatAvailableMessageActions, NoError>
|
||||
func resolveUrl(context: AccountContext, peerId: PeerId?, url: String, skipUrlAuth: Bool) -> Signal<ResolvedUrl, NoError>
|
||||
func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, forceExternal: Bool, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?)
|
||||
func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, forceExternal: Bool, openPeer: @escaping (EnginePeer, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?)
|
||||
func openAddContact(context: AccountContext, firstName: String, lastName: String, phoneNumber: String, label: String, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, completed: @escaping () -> Void)
|
||||
func openAddPersonContact(context: AccountContext, peerId: PeerId, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void)
|
||||
func presentContactsWarningSuppression(context: AccountContext, present: (ViewController, Any?) -> Void)
|
||||
|
@ -14,14 +14,14 @@ public final class GalleryControllerActionInteraction {
|
||||
public let openUrl: (String, Bool) -> Void
|
||||
public let openUrlIn: (String) -> Void
|
||||
public let openPeerMention: (String) -> Void
|
||||
public let openPeer: (PeerId) -> Void
|
||||
public let openPeer: (EnginePeer) -> Void
|
||||
public let openHashtag: (String?, String) -> Void
|
||||
public let openBotCommand: (String) -> Void
|
||||
public let addContact: (String) -> Void
|
||||
public let storeMediaPlaybackState: (MessageId, Double?, Double) -> Void
|
||||
public let editMedia: (MessageId, [UIView], @escaping () -> Void) -> Void
|
||||
|
||||
public init(openUrl: @escaping (String, Bool) -> Void, openUrlIn: @escaping (String) -> Void, openPeerMention: @escaping (String) -> Void, openPeer: @escaping (PeerId) -> Void, openHashtag: @escaping (String?, String) -> Void, openBotCommand: @escaping (String) -> Void, addContact: @escaping (String) -> Void, storeMediaPlaybackState: @escaping (MessageId, Double?, Double) -> Void, editMedia: @escaping (MessageId, [UIView], @escaping () -> Void) -> Void) {
|
||||
public init(openUrl: @escaping (String, Bool) -> Void, openUrlIn: @escaping (String) -> Void, openPeerMention: @escaping (String) -> Void, openPeer: @escaping (EnginePeer) -> Void, openHashtag: @escaping (String?, String) -> Void, openBotCommand: @escaping (String) -> Void, addContact: @escaping (String) -> Void, storeMediaPlaybackState: @escaping (MessageId, Double?, Double) -> Void, editMedia: @escaping (MessageId, [UIView], @escaping () -> Void) -> Void) {
|
||||
self.openUrl = openUrl
|
||||
self.openUrlIn = openUrlIn
|
||||
self.openPeerMention = openPeerMention
|
||||
|
@ -70,7 +70,7 @@ public enum AvatarNodeExplicitIcon {
|
||||
|
||||
private enum AvatarNodeState: Equatable {
|
||||
case empty
|
||||
case peerAvatar(EnginePeer.Id, [String], TelegramMediaImageRepresentation?)
|
||||
case peerAvatar(EnginePeer.Id, [String], TelegramMediaImageRepresentation?, AvatarNodeClipStyle)
|
||||
case custom(letter: [String], explicitColorIndex: Int?, explicitIcon: AvatarNodeExplicitIcon?)
|
||||
}
|
||||
|
||||
@ -78,8 +78,8 @@ private func ==(lhs: AvatarNodeState, rhs: AvatarNodeState) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.empty, .empty):
|
||||
return true
|
||||
case let (.peerAvatar(lhsPeerId, lhsLetters, lhsPhotoRepresentations), .peerAvatar(rhsPeerId, rhsLetters, rhsPhotoRepresentations)):
|
||||
return lhsPeerId == rhsPeerId && lhsLetters == rhsLetters && lhsPhotoRepresentations == rhsPhotoRepresentations
|
||||
case let (.peerAvatar(lhsPeerId, lhsLetters, lhsPhotoRepresentations, lhsClipStyle), .peerAvatar(rhsPeerId, rhsLetters, rhsPhotoRepresentations, rhsClipStyle)):
|
||||
return lhsPeerId == rhsPeerId && lhsLetters == rhsLetters && lhsPhotoRepresentations == rhsPhotoRepresentations && lhsClipStyle == rhsClipStyle
|
||||
case let (.custom(lhsLetters, lhsIndex, lhsIcon), .custom(rhsLetters, rhsIndex, rhsIcon)):
|
||||
return lhsLetters == rhsLetters && lhsIndex == rhsIndex && lhsIcon == rhsIcon
|
||||
default:
|
||||
@ -321,7 +321,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
} else if peer?.restrictionText(platform: "ios", contentSettings: genericContext.currentContentSettings.with { $0 }) == nil {
|
||||
representation = peer?.smallProfileImage
|
||||
}
|
||||
let updatedState: AvatarNodeState = .peerAvatar(peer?.id ?? EnginePeer.Id(0), peer?.displayLetters ?? [], representation)
|
||||
let updatedState: AvatarNodeState = .peerAvatar(peer?.id ?? EnginePeer.Id(0), peer?.displayLetters ?? [], representation, clipStyle)
|
||||
if updatedState != self.state || overrideImage != self.overrideImage || theme !== self.theme {
|
||||
self.state = updatedState
|
||||
self.overrideImage = overrideImage
|
||||
@ -588,9 +588,8 @@ public final class AvatarNode: ASDisplayNode {
|
||||
let currentState = node?.state
|
||||
let createNode = node == nil
|
||||
return { [weak node] context, peer, font in
|
||||
let state: AvatarNodeState = .peerAvatar(peer.id, peer.displayLetters, peer.smallProfileImage)
|
||||
let state: AvatarNodeState = .peerAvatar(peer.id, peer.displayLetters, peer.smallProfileImage, .round)
|
||||
if currentState != state {
|
||||
|
||||
}
|
||||
var createdNode: AvatarNode?
|
||||
if createNode {
|
||||
|
@ -79,6 +79,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/EmojiStatusSelectionComponent",
|
||||
"//submodules/TelegramUI/Components/EntityKeyboard",
|
||||
"//submodules/TelegramUI/Components/ForumCreateTopicScreen:ForumCreateTopicScreen",
|
||||
"//submodules/TelegramUI/Components/ChatTitleView",
|
||||
"//submodules/AnimationUI:AnimationUI",
|
||||
],
|
||||
visibility = [
|
||||
|
@ -38,6 +38,7 @@ import EntityKeyboard
|
||||
import TelegramStringFormatting
|
||||
import ForumCreateTopicScreen
|
||||
import AnimationUI
|
||||
import ChatTitleView
|
||||
|
||||
private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBarSearchContentNode) -> Bool {
|
||||
if listNode.scroller.isDragging {
|
||||
@ -110,22 +111,22 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent
|
||||
}
|
||||
}
|
||||
|
||||
private final class MoreHeaderButton: HighlightableButtonNode {
|
||||
enum Content {
|
||||
public final class MoreHeaderButton: HighlightableButtonNode {
|
||||
public enum Content {
|
||||
case image(UIImage?)
|
||||
case more(UIImage?)
|
||||
}
|
||||
|
||||
let referenceNode: ContextReferenceContentNode
|
||||
let containerNode: ContextControllerSourceNode
|
||||
public let referenceNode: ContextReferenceContentNode
|
||||
public let containerNode: ContextControllerSourceNode
|
||||
private let iconNode: ASImageNode
|
||||
private var animationNode: AnimationNode?
|
||||
|
||||
var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||
public var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||
|
||||
private var color: UIColor
|
||||
|
||||
init(color: UIColor) {
|
||||
public init(color: UIColor) {
|
||||
self.color = color
|
||||
|
||||
self.referenceNode = ContextReferenceContentNode()
|
||||
@ -158,7 +159,7 @@ private final class MoreHeaderButton: HighlightableButtonNode {
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 26.0, height: 44.0))
|
||||
self.referenceNode.frame = self.containerNode.bounds
|
||||
|
||||
self.iconNode.image = optionsCircleImage(color: color)
|
||||
self.iconNode.image = MoreHeaderButton.optionsCircleImage(color: color)
|
||||
if let image = self.iconNode.image {
|
||||
self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size)
|
||||
}
|
||||
@ -167,7 +168,7 @@ private final class MoreHeaderButton: HighlightableButtonNode {
|
||||
}
|
||||
|
||||
private var content: Content?
|
||||
func setContent(_ content: Content, animated: Bool = false) {
|
||||
public func setContent(_ content: Content, animated: Bool = false) {
|
||||
if case .more = content, self.animationNode == nil {
|
||||
let iconColor = self.color
|
||||
let animationNode = AnimationNode(animation: "anim_profilemore", colors: ["Point 2.Group 1.Fill 1": iconColor,
|
||||
@ -236,33 +237,33 @@ private final class MoreHeaderButton: HighlightableButtonNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
override public func didLoad() {
|
||||
super.didLoad()
|
||||
self.view.isOpaque = false
|
||||
}
|
||||
|
||||
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||
override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||
return CGSize(width: 22.0, height: 44.0)
|
||||
}
|
||||
|
||||
func onLayout() {
|
||||
public func onLayout() {
|
||||
}
|
||||
|
||||
func play() {
|
||||
public func play() {
|
||||
self.animationNode?.playOnce()
|
||||
}
|
||||
}
|
||||
|
||||
public static func optionsCircleImage(color: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 22.0, height: 22.0), contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
private func optionsCircleImage(color: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 22.0, height: 22.0), contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setStrokeColor(color.cgColor)
|
||||
let lineWidth: CGFloat = 1.3
|
||||
context.setLineWidth(lineWidth)
|
||||
|
||||
context.setStrokeColor(color.cgColor)
|
||||
let lineWidth: CGFloat = 1.3
|
||||
context.setLineWidth(lineWidth)
|
||||
|
||||
context.strokeEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: lineWidth, dy: lineWidth))
|
||||
})
|
||||
context.strokeEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: lineWidth, dy: lineWidth))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public class ChatListControllerImpl: TelegramBaseController, ChatListController {
|
||||
@ -284,11 +285,15 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
return super.displayNode as! ChatListControllerNode
|
||||
}
|
||||
|
||||
private let titleView: ChatListTitleView
|
||||
private var titleView: ChatListTitleView?
|
||||
private var chatTitleView: ChatTitleView?
|
||||
private let infoReady = Promise<Bool>()
|
||||
|
||||
private var proxyUnavailableTooltipController: TooltipController?
|
||||
private var didShowProxyUnavailableTooltipController = false
|
||||
|
||||
private var titleDisposable: Disposable?
|
||||
private var chatTitleDisposable: Disposable?
|
||||
private var badgeDisposable: Disposable?
|
||||
private var badgeIconDisposable: Disposable?
|
||||
|
||||
@ -358,17 +363,22 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
self.animationCache = context.animationCache
|
||||
self.animationRenderer = context.animationRenderer
|
||||
|
||||
self.titleView = ChatListTitleView(
|
||||
context: context,
|
||||
theme: self.presentationData.theme,
|
||||
strings: self.presentationData.strings,
|
||||
animationCache: self.animationCache,
|
||||
animationRenderer: self.animationRenderer
|
||||
)
|
||||
switch self.location {
|
||||
case .chatList:
|
||||
self.titleView = ChatListTitleView(
|
||||
context: context,
|
||||
theme: self.presentationData.theme,
|
||||
strings: self.presentationData.strings,
|
||||
animationCache: self.animationCache,
|
||||
animationRenderer: self.animationRenderer
|
||||
)
|
||||
case .forum:
|
||||
self.chatTitleView = ChatTitleView(context: self.context, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, animationCache: self.context.animationCache, animationRenderer: self.context.animationRenderer)
|
||||
}
|
||||
|
||||
self.moreBarButton = MoreHeaderButton(color: self.presentationData.theme.rootController.navigationBar.buttonColor)
|
||||
self.moreBarButton.isUserInteractionEnabled = true
|
||||
self.moreBarButton.setContent(.more(optionsCircleImage(color: self.presentationData.theme.rootController.navigationBar.buttonColor)))
|
||||
self.moreBarButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: self.presentationData.theme.rootController.navigationBar.buttonColor)))
|
||||
|
||||
self.moreBarButtonItem = UIBarButtonItem(customDisplayNode: self.moreBarButton)!
|
||||
|
||||
@ -401,16 +411,96 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
guard case let .forum(peerId) = self.location else {
|
||||
return
|
||||
}
|
||||
ChatListControllerImpl.openMoreMenu(context: self.context, peerId: peerId, sourceController: self, sourceView: sourceNode.view, gesture: gesture)
|
||||
ChatListControllerImpl.openMoreMenu(context: self.context, peerId: peerId, sourceController: self, isViewingAsTopics: true, sourceView: sourceNode.view, gesture: gesture)
|
||||
}
|
||||
self.moreBarButton.addTarget(self, action: #selector(self.moreButtonPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
self.titleView.title = NetworkStatusTitle(text: title, activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false, peerStatus: nil)
|
||||
self.navigationItem.titleView = self.titleView
|
||||
|
||||
self.titleView.openStatusSetup = { [weak self] sourceView in
|
||||
self?.openStatusSetup(sourceView: sourceView)
|
||||
switch self.location {
|
||||
case .chatList:
|
||||
if let titleView = self.titleView {
|
||||
titleView.title = NetworkStatusTitle(text: title, activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false, peerStatus: nil)
|
||||
self.navigationItem.titleView = titleView
|
||||
|
||||
titleView.openStatusSetup = { [weak self] sourceView in
|
||||
self?.openStatusSetup(sourceView: sourceView)
|
||||
}
|
||||
}
|
||||
self.infoReady.set(.single(true))
|
||||
case let .forum(peerId):
|
||||
if let chatTitleView = self.chatTitleView {
|
||||
self.navigationItem.titleView = chatTitleView
|
||||
|
||||
chatTitleView.pressed = { [weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
let _ = (self.context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let self = self, let peer = peer, let controller = context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) else {
|
||||
return
|
||||
}
|
||||
(self.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
})
|
||||
}
|
||||
|
||||
let peerView = Promise<PeerView>()
|
||||
peerView.set(context.account.viewTracker.peerView(peerId))
|
||||
|
||||
var onlineMemberCount: Signal<Int32?, NoError> = .single(nil)
|
||||
|
||||
let recentOnlineSignal: Signal<Int32?, NoError> = peerView.get()
|
||||
|> map { view -> Bool? in
|
||||
if let cachedData = view.cachedData as? CachedChannelData, let peer = peerViewMainPeer(view) as? TelegramChannel {
|
||||
if case .broadcast = peer.info {
|
||||
return nil
|
||||
} else if let memberCount = cachedData.participantsSummary.memberCount, memberCount > 50 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> mapToSignal { isLarge -> Signal<Int32?, NoError> in
|
||||
if let isLarge = isLarge {
|
||||
if isLarge {
|
||||
return context.peerChannelMemberCategoriesContextsManager.recentOnline(account: context.account, accountPeerId: context.account.peerId, peerId: peerId)
|
||||
|> map(Optional.init)
|
||||
} else {
|
||||
return context.peerChannelMemberCategoriesContextsManager.recentOnlineSmall(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId)
|
||||
|> map(Optional.init)
|
||||
}
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
onlineMemberCount = recentOnlineSignal
|
||||
|
||||
self.chatTitleDisposable = (combineLatest(queue: Queue.mainQueue(),
|
||||
peerView.get(),
|
||||
onlineMemberCount
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peerView, onlineMemberCount in
|
||||
guard let strongSelf = self, let chatTitleView = strongSelf.chatTitleView else {
|
||||
return
|
||||
}
|
||||
|
||||
chatTitleView.titleContent = .peer(peerView: peerView, customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: false)
|
||||
strongSelf.infoReady.set(.single(true))
|
||||
|
||||
if let channel = peerView.peers[peerView.peerId] as? TelegramChannel, !channel.flags.contains(.isForum) {
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(previewing: false))
|
||||
navigationController.replaceController(strongSelf, with: chatController, animated: true)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if !previewing {
|
||||
@ -581,7 +671,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
animated = true
|
||||
}
|
||||
}
|
||||
strongSelf.titleView.setTitle(NetworkStatusTitle(text: title, activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false, peerStatus: peerStatus), animated: animated)
|
||||
strongSelf.titleView?.setTitle(NetworkStatusTitle(text: title, activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false, peerStatus: peerStatus), animated: animated)
|
||||
} else if isReorderingTabs {
|
||||
if case .chatList(.root) = strongSelf.location {
|
||||
strongSelf.navigationItem.setRightBarButton(nil, animated: true)
|
||||
@ -592,17 +682,17 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
let (_, connectsViaProxy) = proxy
|
||||
switch networkState {
|
||||
case .waitingForNetwork:
|
||||
strongSelf.titleView.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_WaitingForNetwork, activity: true, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: false, isManuallyLocked: false, peerStatus: peerStatus)
|
||||
strongSelf.titleView?.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_WaitingForNetwork, activity: true, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: false, isManuallyLocked: false, peerStatus: peerStatus)
|
||||
case let .connecting(proxy):
|
||||
var text = strongSelf.presentationData.strings.State_Connecting
|
||||
if let layout = strongSelf.validLayout, proxy != nil && layout.metrics.widthClass != .regular && layout.size.width > 320.0 {
|
||||
text = strongSelf.presentationData.strings.State_ConnectingToProxy
|
||||
}
|
||||
strongSelf.titleView.title = NetworkStatusTitle(text: text, activity: true, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: false, isManuallyLocked: false, peerStatus: peerStatus)
|
||||
strongSelf.titleView?.title = NetworkStatusTitle(text: text, activity: true, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: false, isManuallyLocked: false, peerStatus: peerStatus)
|
||||
case .updating:
|
||||
strongSelf.titleView.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_Updating, activity: true, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: false, isManuallyLocked: false, peerStatus: peerStatus)
|
||||
strongSelf.titleView?.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_Updating, activity: true, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: false, isManuallyLocked: false, peerStatus: peerStatus)
|
||||
case .online:
|
||||
strongSelf.titleView.title = NetworkStatusTitle(text: defaultTitle, activity: false, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: false, isManuallyLocked: false, peerStatus: peerStatus)
|
||||
strongSelf.titleView?.title = NetworkStatusTitle(text: defaultTitle, activity: false, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: false, isManuallyLocked: false, peerStatus: peerStatus)
|
||||
}
|
||||
} else {
|
||||
var isRoot = false
|
||||
@ -654,7 +744,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
var checkProxy = false
|
||||
switch networkState {
|
||||
case .waitingForNetwork:
|
||||
strongSelf.titleView.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_WaitingForNetwork, activity: true, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked, peerStatus: peerStatus)
|
||||
strongSelf.titleView?.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_WaitingForNetwork, activity: true, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked, peerStatus: peerStatus)
|
||||
case let .connecting(proxy):
|
||||
var text = strongSelf.presentationData.strings.State_Connecting
|
||||
if let layout = strongSelf.validLayout, proxy != nil && layout.metrics.widthClass != .regular && layout.size.width > 320.0 {
|
||||
@ -663,11 +753,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
if let proxy = proxy, proxy.hasConnectionIssues {
|
||||
checkProxy = true
|
||||
}
|
||||
strongSelf.titleView.title = NetworkStatusTitle(text: text, activity: true, hasProxy: isRoot && hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked, peerStatus: peerStatus)
|
||||
strongSelf.titleView?.title = NetworkStatusTitle(text: text, activity: true, hasProxy: isRoot && hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked, peerStatus: peerStatus)
|
||||
case .updating:
|
||||
strongSelf.titleView.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_Updating, activity: true, hasProxy: isRoot && hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked, peerStatus: peerStatus)
|
||||
strongSelf.titleView?.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_Updating, activity: true, hasProxy: isRoot && hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked, peerStatus: peerStatus)
|
||||
case .online:
|
||||
strongSelf.titleView.setTitle(NetworkStatusTitle(text: defaultTitle, activity: false, hasProxy: isRoot && hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked, peerStatus: peerStatus), animated: (previousEditingAndNetworkState?.0 ?? false) != stateAndFilterId.state.editing)
|
||||
strongSelf.titleView?.setTitle(NetworkStatusTitle(text: defaultTitle, activity: false, hasProxy: isRoot && hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked, peerStatus: peerStatus), animated: (previousEditingAndNetworkState?.0 ?? false) != stateAndFilterId.state.editing)
|
||||
}
|
||||
if case .chatList(.root) = location, checkProxy {
|
||||
if strongSelf.proxyUnavailableTooltipController == nil && !strongSelf.didShowProxyUnavailableTooltipController && strongSelf.isNodeLoaded && strongSelf.displayNode.view.window != nil && strongSelf.navigationController?.topViewController === self {
|
||||
@ -680,8 +770,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}
|
||||
}
|
||||
strongSelf.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceViewAndRect: {
|
||||
if let strongSelf = self, let rect = strongSelf.titleView.proxyButtonFrame {
|
||||
return (strongSelf.titleView, rect.insetBy(dx: 0.0, dy: -4.0))
|
||||
if let strongSelf = self, let titleView = strongSelf.titleView, let rect = titleView.proxyButtonFrame {
|
||||
return (titleView, rect.insetBy(dx: 0.0, dy: -4.0))
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
@ -708,13 +798,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}
|
||||
})
|
||||
|
||||
self.titleView.toggleIsLocked = { [weak self] in
|
||||
self.titleView?.toggleIsLocked = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.context.sharedContext.appLockContext.lock()
|
||||
}
|
||||
}
|
||||
|
||||
self.titleView.openProxySettings = { [weak self] in
|
||||
self.titleView?.openProxySettings = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(context.sharedContext.makeProxySettingsController(context: context))
|
||||
}
|
||||
@ -1044,6 +1134,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
deinit {
|
||||
self.openMessageFromSearchDisposable.dispose()
|
||||
self.titleDisposable?.dispose()
|
||||
self.chatTitleDisposable?.dispose()
|
||||
self.badgeDisposable?.dispose()
|
||||
self.badgeIconDisposable?.dispose()
|
||||
self.passcodeLockTooltipDisposable.dispose()
|
||||
@ -1064,7 +1155,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
var selectedItems = Set<MediaId>()
|
||||
var topStatusTitle = self.presentationData.strings.PeerStatusSetup_NoTimerTitle
|
||||
var currentSelection: Int64?
|
||||
if let peerStatus = self.titleView.title.peerStatus, case let .emoji(emojiStatus) = peerStatus {
|
||||
if let peerStatus = self.titleView?.title.peerStatus, case let .emoji(emojiStatus) = peerStatus {
|
||||
selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId))
|
||||
currentSelection = emojiStatus.fileId
|
||||
|
||||
@ -1141,8 +1232,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
self.navigationItem.rightBarButtonItem = editItem
|
||||
}
|
||||
|
||||
self.titleView.theme = self.presentationData.theme
|
||||
self.titleView.strings = self.presentationData.strings
|
||||
self.titleView?.theme = self.presentationData.theme
|
||||
self.titleView?.strings = self.presentationData.strings
|
||||
|
||||
self.chatTitleView?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings, hasEmbeddedTitleContent: false)
|
||||
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
@ -1825,7 +1918,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
if case .chatList(.root) = self.location {
|
||||
self.ready.set(.never())
|
||||
} else {
|
||||
self.ready.set(self.chatListDisplayNode.containerNode.ready)
|
||||
self.ready.set(combineLatest([
|
||||
self.chatListDisplayNode.containerNode.ready,
|
||||
self.infoReady.get()
|
||||
])
|
||||
|> map { values -> Bool in
|
||||
return !values.contains(where: { !$0 })
|
||||
}
|
||||
|> filter { $0 })
|
||||
}
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
@ -1898,7 +1998,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
})
|
||||
#endif
|
||||
|
||||
if let lockViewFrame = self.titleView.lockViewFrame, !self.didShowPasscodeLockTooltipController {
|
||||
if let lockViewFrame = self.titleView?.lockViewFrame, !self.didShowPasscodeLockTooltipController {
|
||||
self.passcodeLockTooltipDisposable.set(combineLatest(queue: .mainQueue(), ApplicationSpecificNotice.getPasscodeLockTips(accountManager: self.context.sharedContext.accountManager), self.context.sharedContext.accountManager.accessChallengeData() |> take(1)).start(next: { [weak self] tooltipValue, passcodeView in
|
||||
if let strongSelf = self {
|
||||
if !tooltipValue {
|
||||
@ -1908,8 +2008,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
|
||||
let tooltipController = TooltipController(content: .text(strongSelf.presentationData.strings.DialogList_PasscodeLockHelp), baseFontSize: strongSelf.presentationData.listsFontSize.baseDisplaySize, dismissByTapOutside: true)
|
||||
strongSelf.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceViewAndRect: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
return (strongSelf.titleView, lockViewFrame.offsetBy(dx: 4.0, dy: 14.0))
|
||||
if let strongSelf = self, let titleView = strongSelf.titleView {
|
||||
return (titleView, lockViewFrame.offsetBy(dx: 4.0, dy: 14.0))
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
@ -2322,9 +2422,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
self.moreBarButton.contextAction?(self.moreBarButton.containerNode, nil)
|
||||
}
|
||||
|
||||
public static func openMoreMenu(context: AccountContext, peerId: EnginePeer.Id, sourceController: ViewController, sourceView: UIView, gesture: ContextGesture?) {
|
||||
let isViewingAsTopics: Bool = true
|
||||
|
||||
public static func openMoreMenu(context: AccountContext, peerId: EnginePeer.Id, sourceController: ViewController, isViewingAsTopics: Bool, sourceView: UIView, gesture: ContextGesture?) {
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: "View as Topics", icon: { theme in
|
||||
@ -2332,8 +2430,15 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
return nil
|
||||
}
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { _, a in
|
||||
}, action: { [weak sourceController] _, a in
|
||||
a(.default)
|
||||
|
||||
guard let sourceController = sourceController, let navigationController = sourceController.navigationController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
|
||||
let chatController = context.sharedContext.makeChatListController(context: context, location: .forum(peerId: peerId), controlsHistoryPreload: false, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: false)
|
||||
navigationController.replaceController(sourceController, with: chatController, animated: false)
|
||||
})))
|
||||
items.append(.action(ContextMenuActionItem(text: "View as Messages", icon: { theme in
|
||||
if isViewingAsTopics {
|
||||
@ -2394,7 +2499,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}, action: { action in
|
||||
action.dismissWithResult(.default)
|
||||
|
||||
let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: "Topic#\(Int.random(in: 0 ..< 100000))", iconFileId: nil)
|
||||
let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: "Topic#\(Int.random(in: 0 ..< 100000)) very long title to fill two lines", iconFileId: nil)
|
||||
|> deliverOnMainQueue).start(next: { topicId in
|
||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [.message(text: "First Message", attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(topicId)), localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]).start()
|
||||
})
|
||||
@ -3673,8 +3778,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}
|
||||
|
||||
public var lockViewFrame: CGRect? {
|
||||
if let lockViewFrame = self.titleView.lockViewFrame {
|
||||
return self.titleView.convert(lockViewFrame, to: self.view)
|
||||
if let titleView = self.titleView, let lockViewFrame = titleView.lockViewFrame {
|
||||
return titleView.convert(lockViewFrame, to: self.view)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
@ -29,6 +29,16 @@ import ComponentFlow
|
||||
import EmojiStatusComponent
|
||||
|
||||
public enum ChatListItemContent {
|
||||
public struct ThreadInfo: Equatable {
|
||||
public var id: Int64
|
||||
public var info: EngineMessageHistoryThread.Info
|
||||
|
||||
public init(id: Int64, info: EngineMessageHistoryThread.Info) {
|
||||
self.id = id
|
||||
self.info = info
|
||||
}
|
||||
}
|
||||
|
||||
public final class DraftState: Equatable {
|
||||
let text: String
|
||||
let entities: [MessageTextEntity]
|
||||
@ -49,7 +59,7 @@ public enum ChatListItemContent {
|
||||
}
|
||||
}
|
||||
|
||||
case peer(messages: [EngineMessage], peer: EngineRenderedPeer, threadInfo: EngineMessageHistoryThread.Info?, combinedReadState: EnginePeerReadCounters?, isRemovedFromTotalUnreadCount: Bool, presence: EnginePeer.Presence?, hasUnseenMentions: Bool, hasUnseenReactions: Bool, draftState: DraftState?, inputActivities: [(EnginePeer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool, forumThreadTitle: String?)
|
||||
case peer(messages: [EngineMessage], peer: EngineRenderedPeer, threadInfo: ThreadInfo?, combinedReadState: EnginePeerReadCounters?, isRemovedFromTotalUnreadCount: Bool, presence: EnginePeer.Presence?, hasUnseenMentions: Bool, hasUnseenReactions: Bool, draftState: DraftState?, inputActivities: [(EnginePeer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool, forumThreadTitle: String?)
|
||||
case groupReference(groupId: EngineChatList.Group, peers: [EngineChatList.GroupItem.Item], message: EngineMessage?, unreadCount: Int, hiddenByDefault: Bool)
|
||||
|
||||
public var chatLocation: ChatLocation? {
|
||||
@ -1011,7 +1021,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let promoInfo: ChatListNodeEntryPromoInfo?
|
||||
let displayAsMessage: Bool
|
||||
let hasFailedMessages: Bool
|
||||
var threadInfo: EngineMessageHistoryThread.Info?
|
||||
var threadInfo: ChatListItemContent.ThreadInfo?
|
||||
var forumThreadTitle: String?
|
||||
|
||||
var groupHiddenByDefault = false
|
||||
@ -1150,7 +1160,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let leftInset: CGFloat = params.leftInset + avatarLeftInset
|
||||
|
||||
enum ContentData {
|
||||
case chat(itemPeer: EngineRenderedPeer, threadInfo: EngineMessageHistoryThread.Info?, peer: EnginePeer?, hideAuthor: Bool, messageText: String, spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?)
|
||||
case chat(itemPeer: EngineRenderedPeer, threadInfo: ChatListItemContent.ThreadInfo?, peer: EnginePeer?, hideAuthor: Bool, messageText: String, spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?)
|
||||
case group(peers: [EngineChatList.GroupItem.Item])
|
||||
}
|
||||
|
||||
@ -1238,8 +1248,13 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
if let forumThreadTitle = forumThreadTitle, let peerTextValue = peerText {
|
||||
peerText = "\(peerTextValue) → \(forumThreadTitle)"
|
||||
if let peerTextValue = peerText, case let .channel(channel) = itemPeer.chatMainPeer, channel.flags.contains(.isForum), threadInfo == nil {
|
||||
if let forumThreadTitle = forumThreadTitle {
|
||||
peerText = "\(peerTextValue) → \(forumThreadTitle)"
|
||||
} else {
|
||||
//TODO:localize
|
||||
peerText = "\(peerTextValue) → General"
|
||||
}
|
||||
}
|
||||
|
||||
let messageText: String
|
||||
@ -1432,7 +1447,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
switch contentData {
|
||||
case let .chat(itemPeer, threadInfo, _, _, _, _, _):
|
||||
if let threadInfo = threadInfo {
|
||||
titleAttributedString = NSAttributedString(string: threadInfo.title, font: titleFont, textColor: theme.titleColor)
|
||||
titleAttributedString = NSAttributedString(string: threadInfo.info.title, font: titleFont, textColor: theme.titleColor)
|
||||
} else if let message = messages.last, case let .user(author) = message.author, displayAsMessage {
|
||||
titleAttributedString = NSAttributedString(string: author.id == account.peerId ? item.presentationData.strings.DialogList_You : EnginePeer.user(author).displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder), font: titleFont, textColor: theme.titleColor)
|
||||
} else if isPeerGroup {
|
||||
@ -1687,9 +1702,17 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
textCutout = TextNodeCutout(topLeft: CGSize(width: textLeftCutout, height: 10.0), topRight: nil, bottomRight: nil)
|
||||
}
|
||||
let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: textAttributedString, backgroundColor: nil, maximumNumberOfLines: authorAttributedString == nil ? 2 : 1, truncationType: .end, constrainedSize: CGSize(width: rawContentWidth - badgeSize, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: textCutout, insets: UIEdgeInsets(top: 2.0, left: 1.0, bottom: 2.0, right: 1.0)))
|
||||
|
||||
let maxTitleLines: Int
|
||||
switch item.index {
|
||||
case .forum:
|
||||
maxTitleLines = 2
|
||||
case .chatList:
|
||||
maxTitleLines = 1
|
||||
}
|
||||
|
||||
let titleRectWidth = rawContentWidth - dateLayout.size.width - 10.0 - statusWidth - titleIconsWidth
|
||||
let (titleLayout, titleApply) = titleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: titleRectWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (titleLayout, titleApply) = titleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: maxTitleLines, truncationType: .end, constrainedSize: CGSize(width: titleRectWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
var inputActivitiesSize: CGSize?
|
||||
var inputActivitiesApply: (() -> Void)?
|
||||
@ -1777,6 +1800,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let titleSpacing: CGFloat = -1.0
|
||||
let authorSpacing: CGFloat = -3.0
|
||||
var itemHeight: CGFloat = 8.0 * 2.0 + 1.0
|
||||
itemHeight -= 21.0
|
||||
itemHeight += titleLayout.size.height
|
||||
itemHeight += measureLayout.size.height * 3.0
|
||||
itemHeight += titleSpacing
|
||||
itemHeight += authorSpacing
|
||||
@ -1892,7 +1917,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
transition.updateFrame(node: strongSelf.avatarNode, frame: avatarFrame)
|
||||
strongSelf.updateVideoVisibility()
|
||||
|
||||
if let iconFileId = threadInfo?.icon {
|
||||
if let threadInfo = threadInfo {
|
||||
let avatarIconView: ComponentHostView<Empty>
|
||||
if let current = strongSelf.avatarIconView {
|
||||
avatarIconView = current
|
||||
@ -1902,11 +1927,18 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.contextContainer.view.addSubview(avatarIconView)
|
||||
}
|
||||
|
||||
let avatarIconContent: EmojiStatusComponent.Content
|
||||
if let fileId = threadInfo.info.icon {
|
||||
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 40.0, height: 40.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: nil, loopMode: .forever)
|
||||
} else {
|
||||
avatarIconContent = .topic(title: String(threadInfo.info.title.prefix(1)), colorIndex: Int(clamping: abs(threadInfo.id)))
|
||||
}
|
||||
|
||||
let avatarIconComponent = EmojiStatusComponent(
|
||||
context: item.context,
|
||||
animationCache: item.interaction.animationCache,
|
||||
animationRenderer: item.interaction.animationRenderer,
|
||||
content: .animation(content: .customEmoji(fileId: iconFileId), size: CGSize(width: 40.0, height: 40.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: nil, loopMode: .forever),
|
||||
content: avatarIconContent,
|
||||
isVisibleForAnimations: strongSelf.visibilityStatus,
|
||||
action: nil
|
||||
)
|
||||
|
@ -55,7 +55,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
isRemovedFromTotalUnreadCount: Bool,
|
||||
draftState: ChatListItemContent.DraftState?,
|
||||
peer: EngineRenderedPeer,
|
||||
threadInfo: EngineMessageHistoryThread.Info?,
|
||||
threadInfo: ChatListItemContent.ThreadInfo?,
|
||||
presence: EnginePeer.Presence?,
|
||||
hasUnseenMentions: Bool,
|
||||
hasUnseenReactions: Bool,
|
||||
@ -377,8 +377,16 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState
|
||||
if let peerId {
|
||||
inputActivities = state.peerInputActivities?.activities[peerId]
|
||||
}
|
||||
|
||||
var threadId: Int64 = 0
|
||||
switch entry.index {
|
||||
case let .forum(_, threadIdValue, _, _):
|
||||
threadId = threadIdValue
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
result.append(.PeerEntry(index: offsetPinnedIndex(entry.index, offset: pinnedIndexOffset), presentationData: state.presentationData, messages: updatedMessages, readState: updatedCombinedReadState, isRemovedFromTotalUnreadCount: entry.isMuted, draftState: draftState, peer: entry.renderedPeer, threadInfo: entry.threadInfo, presence: entry.presence, hasUnseenMentions: entry.hasUnseenMentions, hasUnseenReactions: entry.hasUnseenReactions, editing: state.editing, hasActiveRevealControls: hasActiveRevealControls, selected: isSelected, inputActivities: inputActivities, promoInfo: nil, hasFailedMessages: entry.hasFailed, isContact: entry.isContact, forumThreadTitle: entry.forumTopicTitle))
|
||||
result.append(.PeerEntry(index: offsetPinnedIndex(entry.index, offset: pinnedIndexOffset), presentationData: state.presentationData, messages: updatedMessages, readState: updatedCombinedReadState, isRemovedFromTotalUnreadCount: entry.isMuted, draftState: draftState, peer: entry.renderedPeer, threadInfo: entry.threadInfo.flatMap { ChatListItemContent.ThreadInfo(id: threadId, info: $0) }, presence: entry.presence, hasUnseenMentions: entry.hasUnseenMentions, hasUnseenReactions: entry.hasUnseenReactions, editing: state.editing, hasActiveRevealControls: hasActiveRevealControls, selected: isSelected, inputActivities: inputActivities, promoInfo: nil, hasFailedMessages: entry.hasFailed, isContact: entry.isContact, forumThreadTitle: entry.forumTopicTitle))
|
||||
}
|
||||
if !view.hasLater {
|
||||
var pinningIndex: UInt16 = UInt16(pinnedIndexOffset == 0 ? 0 : (pinnedIndexOffset - 1))
|
||||
@ -440,6 +448,14 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState
|
||||
let peerId = index.messageIndex.id.peerId
|
||||
let isSelected = state.selectedPeerIds.contains(peerId)
|
||||
|
||||
var threadId: Int64 = 0
|
||||
switch item.item.index {
|
||||
case let .forum(_, threadIdValue, _, _):
|
||||
threadId = threadIdValue
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
result.append(.PeerEntry(
|
||||
index: .chatList(EngineChatList.Item.Index.ChatList(pinningIndex: pinningIndex, messageIndex: index.messageIndex)),
|
||||
presentationData: state.presentationData,
|
||||
@ -448,7 +464,7 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState
|
||||
isRemovedFromTotalUnreadCount: item.item.isMuted,
|
||||
draftState: draftState,
|
||||
peer: item.item.renderedPeer,
|
||||
threadInfo: item.item.threadInfo,
|
||||
threadInfo: item.item.threadInfo.flatMap { ChatListItemContent.ThreadInfo(id: threadId, info: $0) },
|
||||
presence: item.item.presence,
|
||||
hasUnseenMentions: item.item.hasUnseenMentions,
|
||||
hasUnseenReactions: item.item.hasUnseenReactions,
|
||||
|
@ -667,7 +667,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
let reaction: MessageReaction.Reaction?
|
||||
private let requestUpdate: (ReactionsTabNode, ContainedViewLayoutTransition) -> Void
|
||||
private let requestUpdateApparentHeight: (ReactionsTabNode, ContainedViewLayoutTransition) -> Void
|
||||
private let openPeer: (PeerId) -> Void
|
||||
private let openPeer: (EnginePeer) -> Void
|
||||
|
||||
private var hasMore: Bool = false
|
||||
|
||||
@ -699,7 +699,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
readStats: MessageReadStats?,
|
||||
requestUpdate: @escaping (ReactionsTabNode, ContainedViewLayoutTransition) -> Void,
|
||||
requestUpdateApparentHeight: @escaping (ReactionsTabNode, ContainedViewLayoutTransition) -> Void,
|
||||
openPeer: @escaping (PeerId) -> Void
|
||||
openPeer: @escaping (EnginePeer) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.availableReactions = availableReactions
|
||||
@ -800,9 +800,9 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
itemNode = current
|
||||
} else {
|
||||
let openPeer = self.openPeer
|
||||
let peerId = item.peer.id
|
||||
let peer = item.peer
|
||||
itemNode = ItemNode(context: self.context, availableReactions: self.availableReactions, animationCache: self.animationCache, animationRenderer: self.animationRenderer, action: {
|
||||
openPeer(peerId)
|
||||
openPeer(peer)
|
||||
})
|
||||
self.itemNodes[index] = itemNode
|
||||
self.scrollNode.addSubnode(itemNode)
|
||||
@ -972,7 +972,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
}
|
||||
private var interactiveTransitionState: InteractiveTransitionState?
|
||||
|
||||
private let openPeer: (PeerId) -> Void
|
||||
private let openPeer: (EnginePeer) -> Void
|
||||
|
||||
private(set) var apparentHeight: CGFloat = 0.0
|
||||
|
||||
@ -987,7 +987,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
requestUpdate: @escaping (ContainedViewLayoutTransition) -> Void,
|
||||
requestUpdateApparentHeight: @escaping (ContainedViewLayoutTransition) -> Void,
|
||||
back: (() -> Void)?,
|
||||
openPeer: @escaping (PeerId) -> Void
|
||||
openPeer: @escaping (EnginePeer) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.availableReactions = availableReactions
|
||||
@ -1334,7 +1334,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
let reaction: MessageReaction.Reaction?
|
||||
let readStats: MessageReadStats?
|
||||
let back: (() -> Void)?
|
||||
let openPeer: (PeerId) -> Void
|
||||
let openPeer: (EnginePeer) -> Void
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
@ -1345,7 +1345,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
reaction: MessageReaction.Reaction?,
|
||||
readStats: MessageReadStats?,
|
||||
back: (() -> Void)?,
|
||||
openPeer: @escaping (PeerId) -> Void
|
||||
openPeer: @escaping (EnginePeer) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.availableReactions = availableReactions
|
||||
|
@ -669,7 +669,12 @@ public class GalleryController: ViewController, StandalonePresentableController,
|
||||
case let .textMention(mention):
|
||||
strongSelf.actionInteraction?.openPeerMention(mention)
|
||||
case let .peerMention(peerId, _):
|
||||
strongSelf.actionInteraction?.openPeer(peerId)
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
strongSelf.actionInteraction?.openPeer(peer)
|
||||
}
|
||||
})
|
||||
case let .botCommand(command):
|
||||
strongSelf.actionInteraction?.openBotCommand(command)
|
||||
case let .hashtag(peerName, hashtag):
|
||||
@ -775,7 +780,13 @@ public class GalleryController: ViewController, StandalonePresentableController,
|
||||
actionSheet?.dismissAnimated()
|
||||
if let strongSelf = self {
|
||||
strongSelf.dismiss(forceAway: false)
|
||||
strongSelf.actionInteraction?.openPeer(peerId)
|
||||
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
strongSelf.actionInteraction?.openPeer(peer)
|
||||
}
|
||||
})
|
||||
}
|
||||
}))
|
||||
if !mention.isEmpty {
|
||||
|
@ -28,7 +28,7 @@ final class InstantPageAnchorItem: InstantPageItem {
|
||||
func drawInTile(context: CGContext) {
|
||||
}
|
||||
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ final class InstantPageArticleItem: InstantPageItem {
|
||||
self.hasRTL = hasRTL
|
||||
}
|
||||
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
return InstantPageArticleNode(context: context, item: self, webPage: self.webPage, strings: strings, theme: theme, contentItems: self.contentItems, contentSize: self.contentSize, cover: self.cover, url: self.url, webpageId: self.webpageId, openUrl: openUrl)
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ final class InstantPageAudioItem: InstantPageItem {
|
||||
self.medias = [media]
|
||||
}
|
||||
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
return InstantPageAudioNode(context: context, strings: strings, theme: theme, webPage: self.webpage, media: self.media, openMedia: openMedia)
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ final class InstantPageContentNode : ASDisplayNode {
|
||||
|
||||
private let openMedia: (InstantPageMedia) -> Void
|
||||
private let longPressMedia: (InstantPageMedia) -> Void
|
||||
private let openPeer: (PeerId) -> Void
|
||||
private let openPeer: (EnginePeer) -> Void
|
||||
private let openUrl: (InstantPageUrlItem) -> Void
|
||||
|
||||
var currentLayoutTiles: [InstantPageTile] = []
|
||||
@ -40,7 +40,7 @@ final class InstantPageContentNode : ASDisplayNode {
|
||||
|
||||
private var previousVisibleBounds: CGRect?
|
||||
|
||||
init(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, sourcePeerType: MediaAutoDownloadPeerType, theme: InstantPageTheme, items: [InstantPageItem], contentSize: CGSize, inOverlayPanel: Bool = false, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void) {
|
||||
init(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, sourcePeerType: MediaAutoDownloadPeerType, theme: InstantPageTheme, items: [InstantPageItem], contentSize: CGSize, inOverlayPanel: Bool = false, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void) {
|
||||
self.context = context
|
||||
self.strings = strings
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
|
@ -151,9 +151,9 @@ public final class InstantPageController: ViewController {
|
||||
self?.present(c, in: .window(.root), with: a, blockInteraction: true)
|
||||
}, pushController: { [weak self] c in
|
||||
(self?.navigationController as? NavigationController)?.pushViewController(c)
|
||||
}, openPeer: { [weak self] peerId in
|
||||
}, openPeer: { [weak self] peer in
|
||||
if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), animated: true))
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), animated: true))
|
||||
}
|
||||
}, navigateBack: { [weak self] in
|
||||
if let strongSelf = self, let controllers = strongSelf.navigationController?.viewControllers.reversed() {
|
||||
|
@ -34,7 +34,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private let getNavigationController: () -> NavigationController?
|
||||
private let present: (ViewController, Any?) -> Void
|
||||
private let pushController: (ViewController) -> Void
|
||||
private let openPeer: (PeerId) -> Void
|
||||
private let openPeer: (EnginePeer) -> Void
|
||||
|
||||
private var webPage: TelegramMediaWebpage?
|
||||
private var initialAnchor: String?
|
||||
@ -92,7 +92,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
return InstantPageStoredState(contentOffset: Double(self.scrollNode.view.contentOffset.y), details: details)
|
||||
}
|
||||
|
||||
init(controller: InstantPageController, context: AccountContext, settings: InstantPagePresentationSettings?, themeSettings: PresentationThemeSettings?, presentationTheme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, autoNightModeTriggered: Bool, statusBar: StatusBar, sourcePeerType: MediaAutoDownloadPeerType, getNavigationController: @escaping () -> NavigationController?, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, openPeer: @escaping (PeerId) -> Void, navigateBack: @escaping () -> Void) {
|
||||
init(controller: InstantPageController, context: AccountContext, settings: InstantPagePresentationSettings?, themeSettings: PresentationThemeSettings?, presentationTheme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, autoNightModeTriggered: Bool, statusBar: StatusBar, sourcePeerType: MediaAutoDownloadPeerType, getNavigationController: @escaping () -> NavigationController?, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, openPeer: @escaping (EnginePeer) -> Void, navigateBack: @escaping () -> Void) {
|
||||
self.controller = controller
|
||||
self.context = context
|
||||
self.presentationTheme = presentationTheme
|
||||
@ -1326,22 +1326,22 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
default:
|
||||
strongSelf.loadProgress.set(1.0)
|
||||
strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.getNavigationController(), forceExternal: false, openPeer: { peerId, navigation in
|
||||
strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.getNavigationController(), forceExternal: false, openPeer: { peer, navigation in
|
||||
switch navigation {
|
||||
case let .chat(_, subject, peekData):
|
||||
if let navigationController = strongSelf.getNavigationController() {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), subject: subject, peekData: peekData))
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), subject: subject, peekData: peekData))
|
||||
}
|
||||
case let .withBotStartPayload(botStart):
|
||||
if let navigationController = strongSelf.getNavigationController() {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), botStart: botStart, keepStack: .always))
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), botStart: botStart, keepStack: .always))
|
||||
}
|
||||
case let .withAttachBot(attachBotStart):
|
||||
if let navigationController = strongSelf.getNavigationController() {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), attachBotStart: attachBotStart))
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), attachBotStart: attachBotStart))
|
||||
}
|
||||
case .info:
|
||||
let _ = (strongSelf.context.account.postbox.loadedPeerWithId(peerId)
|
||||
let _ = (strongSelf.context.account.postbox.loadedPeerWithId(peer.id)
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self {
|
||||
if let controller = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
||||
|
@ -40,7 +40,7 @@ final class InstantPageDetailsItem: InstantPageItem {
|
||||
self.index = index
|
||||
}
|
||||
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
var expanded: Bool?
|
||||
if let expandedDetails = currentExpandedDetails, let currentlyExpanded = expandedDetails[self.index] {
|
||||
expanded = currentlyExpanded
|
||||
|
@ -35,7 +35,7 @@ final class InstantPageDetailsNode: ASDisplayNode, InstantPageNode {
|
||||
|
||||
var requestLayoutUpdate: ((Bool) -> Void)?
|
||||
|
||||
init(context: AccountContext, sourcePeerType: MediaAutoDownloadPeerType, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, item: InstantPageDetailsItem, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, currentlyExpanded: Bool?, updateDetailsExpanded: @escaping (Bool) -> Void) {
|
||||
init(context: AccountContext, sourcePeerType: MediaAutoDownloadPeerType, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, item: InstantPageDetailsItem, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, currentlyExpanded: Bool?, updateDetailsExpanded: @escaping (Bool) -> Void) {
|
||||
self.context = context
|
||||
self.strings = strings
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
|
@ -21,7 +21,7 @@ final class InstantPageFeedbackItem: InstantPageItem {
|
||||
self.webPage = webPage
|
||||
}
|
||||
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
return InstantPageFeedbackNode(context: context, strings: strings, theme: theme, webPage: self.webPage, openUrl: openUrl)
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ final class InstantPageImageItem: InstantPageItem {
|
||||
self.fit = fit
|
||||
}
|
||||
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
return InstantPageImageNode(context: context, sourcePeerType: sourcePeerType, theme: theme, webPage: self.webPage, media: self.media, attributes: self.attributes, interactive: self.interactive, roundCorners: self.roundCorners, fit: self.fit, openMedia: openMedia, longPressMedia: longPressMedia, activatePinchPreview: activatePinchPreview, pinchPreviewFinished: pinchPreviewFinished)
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ protocol InstantPageItem {
|
||||
|
||||
func matchesAnchor(_ anchor: String) -> Bool
|
||||
func drawInTile(context: CGContext)
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode?
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode?
|
||||
func matchesNode(_ node: InstantPageNode) -> Bool
|
||||
func linkSelectionRects(at point: CGPoint) -> [CGRect]
|
||||
|
||||
|
@ -27,7 +27,7 @@ final class InstantPagePeerReferenceItem: InstantPageItem {
|
||||
self.rtl = rtl
|
||||
}
|
||||
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
return InstantPagePeerReferenceNode(context: context, strings: strings, nameDisplayOrder: nameDisplayOrder, theme: theme, initialPeer: self.initialPeer, safeInset: self.safeInset, transparent: self.transparent, rtl: self.rtl, openPeer: openPeer)
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ final class InstantPagePeerReferenceNode: ASDisplayNode, InstantPageNode {
|
||||
private var strings: PresentationStrings
|
||||
private var nameDisplayOrder: PresentationPersonNameOrder
|
||||
private var theme: InstantPageTheme
|
||||
private let openPeer: (PeerId) -> Void
|
||||
private let openPeer: (EnginePeer) -> Void
|
||||
|
||||
private let highlightedBackgroundNode: ASDisplayNode
|
||||
private let buttonNode: HighlightableButtonNode
|
||||
@ -70,7 +70,7 @@ final class InstantPagePeerReferenceNode: ASDisplayNode, InstantPageNode {
|
||||
private let joinDisposable = MetaDisposable()
|
||||
private var joinState: JoinState = .none
|
||||
|
||||
init(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, initialPeer: Peer, safeInset: CGFloat, transparent: Bool, rtl: Bool, openPeer: @escaping (PeerId) -> Void) {
|
||||
init(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, initialPeer: Peer, safeInset: CGFloat, transparent: Bool, rtl: Bool, openPeer: @escaping (EnginePeer) -> Void) {
|
||||
self.context = context
|
||||
self.strings = strings
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
@ -300,7 +300,7 @@ final class InstantPagePeerReferenceNode: ASDisplayNode, InstantPageNode {
|
||||
|
||||
@objc func buttonPressed() {
|
||||
if let peer = self.peer {
|
||||
self.openPeer(peer.id)
|
||||
self.openPeer(EnginePeer(peer))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ final class InstantPagePlayableVideoItem: InstantPageItem {
|
||||
self.interactive = interactive
|
||||
}
|
||||
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
return InstantPagePlayableVideoNode(context: context, webPage: self.webPage, theme: theme, media: self.media, interactive: self.interactive, openMedia: openMedia)
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ final class InstantPageShapeItem: InstantPageItem {
|
||||
return false
|
||||
}
|
||||
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ final class InstantPageSlideshowItem: InstantPageItem {
|
||||
self.medias = medias
|
||||
}
|
||||
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
return InstantPageSlideshowNode(context: context, sourcePeerType: sourcePeerType, theme: theme, webPage: webPage, medias: self.medias, openMedia: openMedia, longPressMedia: longPressMedia)
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ final class InstantPageSubContentNode : ASDisplayNode {
|
||||
|
||||
private let openMedia: (InstantPageMedia) -> Void
|
||||
private let longPressMedia: (InstantPageMedia) -> Void
|
||||
private let openPeer: (PeerId) -> Void
|
||||
private let openPeer: (EnginePeer) -> Void
|
||||
private let openUrl: (InstantPageUrlItem) -> Void
|
||||
|
||||
var currentLayoutTiles: [InstantPageTile] = []
|
||||
@ -40,7 +40,7 @@ final class InstantPageSubContentNode : ASDisplayNode {
|
||||
|
||||
private var previousVisibleBounds: CGRect?
|
||||
|
||||
init(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, sourcePeerType: MediaAutoDownloadPeerType, theme: InstantPageTheme, items: [InstantPageItem], contentSize: CGSize, inOverlayPanel: Bool = false, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void) {
|
||||
init(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, sourcePeerType: MediaAutoDownloadPeerType, theme: InstantPageTheme, items: [InstantPageItem], contentSize: CGSize, inOverlayPanel: Bool = false, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void) {
|
||||
self.context = context
|
||||
self.strings = strings
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
|
@ -200,7 +200,7 @@ final class InstantPageTableItem: InstantPageScrollableItem {
|
||||
return false
|
||||
}
|
||||
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
var additionalNodes: [InstantPageNode] = []
|
||||
for cell in self.cells {
|
||||
for item in cell.additionalItems {
|
||||
|
@ -436,7 +436,7 @@ final class InstantPageTextItem: InstantPageItem {
|
||||
return false
|
||||
}
|
||||
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -485,7 +485,7 @@ final class InstantPageScrollableTextItem: InstantPageScrollableItem {
|
||||
context.restoreGState()
|
||||
}
|
||||
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
var additionalNodes: [InstantPageNode] = []
|
||||
for item in additionalItems {
|
||||
if item.wantsNode {
|
||||
|
@ -25,7 +25,7 @@ final class InstantPageWebEmbedItem: InstantPageItem {
|
||||
self.enableScrolling = enableScrolling
|
||||
}
|
||||
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
func node(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, sourcePeerType: MediaAutoDownloadPeerType, openMedia: @escaping (InstantPageMedia) -> Void, longPressMedia: @escaping (InstantPageMedia) -> Void, activatePinchPreview: ((PinchSourceContainerNode) -> Void)?, pinchPreviewFinished: ((InstantPageNode) -> Void)?, openPeer: @escaping (EnginePeer) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void, currentExpandedDetails: [Int : Bool]?) -> InstantPageNode? {
|
||||
return InstantPageWebEmbedNode(frame: self.frame, url: self.url, html: self.html, enableScrolling: self.enableScrolling, updateWebEmbedHeight: updateWebEmbedHeight)
|
||||
}
|
||||
|
||||
|
@ -22,14 +22,14 @@ public final class JoinLinkPreviewController: ViewController {
|
||||
private let link: String
|
||||
private var isRequest = false
|
||||
private var isGroup = false
|
||||
private let navigateToPeer: (EnginePeer.Id, ChatPeekTimeout?) -> Void
|
||||
private let navigateToPeer: (EnginePeer, ChatPeekTimeout?) -> Void
|
||||
private let parentNavigationController: NavigationController?
|
||||
private var resolvedState: ExternalJoiningChatState?
|
||||
private var presentationData: PresentationData
|
||||
|
||||
private let disposable = MetaDisposable()
|
||||
|
||||
public init(context: AccountContext, link: String, navigateToPeer: @escaping (EnginePeer.Id, ChatPeekTimeout?) -> Void, parentNavigationController: NavigationController?, resolvedState: ExternalJoiningChatState? = nil) {
|
||||
public init(context: AccountContext, link: String, navigateToPeer: @escaping (EnginePeer, ChatPeekTimeout?) -> Void, parentNavigationController: NavigationController?, resolvedState: ExternalJoiningChatState? = nil) {
|
||||
self.context = context
|
||||
self.link = link
|
||||
self.navigateToPeer = navigateToPeer
|
||||
@ -87,11 +87,11 @@ public final class JoinLinkPreviewController: ViewController {
|
||||
let data = JoinLinkPreviewData(isGroup: invite.participants != nil, isJoined: false)
|
||||
strongSelf.controllerNode.setInvitePeer(image: invite.photoRepresentation, title: invite.title, memberCount: invite.participantsCount, members: invite.participants?.map({ $0 }) ?? [], data: data)
|
||||
}
|
||||
case let .alreadyJoined(peerId):
|
||||
strongSelf.navigateToPeer(peerId, nil)
|
||||
case let .alreadyJoined(peer):
|
||||
strongSelf.navigateToPeer(peer, nil)
|
||||
strongSelf.dismiss()
|
||||
case let .peek(peerId, deadline):
|
||||
strongSelf.navigateToPeer(peerId, ChatPeekTimeout(deadline: deadline, linkData: strongSelf.link))
|
||||
case let .peek(peer, deadline):
|
||||
strongSelf.navigateToPeer(peer, ChatPeekTimeout(deadline: deadline, linkData: strongSelf.link))
|
||||
strongSelf.dismiss()
|
||||
case .invalidHash:
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
@ -139,13 +139,13 @@ public final class JoinLinkPreviewController: ViewController {
|
||||
}
|
||||
|
||||
private func join() {
|
||||
self.disposable.set((self.context.engine.peers.joinChatInteractively(with: self.link) |> deliverOnMainQueue).start(next: { [weak self] peerId in
|
||||
self.disposable.set((self.context.engine.peers.joinChatInteractively(with: self.link) |> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
if let strongSelf = self {
|
||||
if strongSelf.isRequest {
|
||||
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .inviteRequestSent(title: strongSelf.presentationData.strings.MemberRequests_RequestToJoinSent, text: strongSelf.isGroup ? strongSelf.presentationData.strings.MemberRequests_RequestToJoinSentDescriptionGroup : strongSelf.presentationData.strings.MemberRequests_RequestToJoinSentDescriptionChannel ), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
|
||||
} else {
|
||||
if let peerId = peerId {
|
||||
strongSelf.navigateToPeer(peerId, nil)
|
||||
if let peer = peer {
|
||||
strongSelf.navigateToPeer(peer, nil)
|
||||
}
|
||||
}
|
||||
strongSelf.dismiss()
|
||||
|
@ -692,11 +692,11 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie
|
||||
guard let navigationController = self.controller?.navigationController as? NavigationController else {
|
||||
return false
|
||||
}
|
||||
self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { [weak self] peerId, navigation in
|
||||
self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { [weak self] peer, navigation in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), subject: nil, keepStack: .always, peekData: nil, completion: { [weak navigationController] _ in
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), subject: nil, keepStack: .always, peekData: nil, completion: { [weak navigationController] _ in
|
||||
if let navigationController = navigationController {
|
||||
var viewControllers = navigationController.viewControllers
|
||||
viewControllers = viewControllers.filter { controller in
|
||||
|
@ -18,9 +18,9 @@ private final class SelectivePrivacyPeersControllerArguments {
|
||||
let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
|
||||
let removePeer: (PeerId) -> Void
|
||||
let addPeer: () -> Void
|
||||
let openPeer: (PeerId) -> Void
|
||||
let openPeer: (EnginePeer) -> Void
|
||||
|
||||
init(context: AccountContext, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, addPeer: @escaping () -> Void, openPeer: @escaping (PeerId) -> Void) {
|
||||
init(context: AccountContext, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, addPeer: @escaping () -> Void, openPeer: @escaping (EnginePeer) -> Void) {
|
||||
self.context = context
|
||||
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
|
||||
self.removePeer = removePeer
|
||||
@ -141,7 +141,7 @@ private enum SelectivePrivacyPeersEntry: ItemListNodeEntry {
|
||||
}
|
||||
}
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: EnginePeer(peer.peer), presence: nil, text: text, label: .none, editing: editing, switchValue: nil, enabled: enabled, selectable: true, sectionId: self.section, action: {
|
||||
arguments.openPeer(peer.peer.id)
|
||||
arguments.openPeer(EnginePeer(peer.peer))
|
||||
}, setPeerIdWithRevealedOptions: { previousId, id in
|
||||
arguments.setPeerIdWithRevealedOptions(previousId, id)
|
||||
}, removePeer: { peerId in
|
||||
@ -323,14 +323,11 @@ public func selectivePrivacyPeersController(context: AccountContext, title: Stri
|
||||
controller?.dismiss()
|
||||
}))
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}, openPeer: { peerId in
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
guard let peer = peer, let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) else {
|
||||
return
|
||||
}
|
||||
pushControllerImpl?(controller)
|
||||
})
|
||||
}, openPeer: { peer in
|
||||
guard let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) else {
|
||||
return
|
||||
}
|
||||
pushControllerImpl?(controller)
|
||||
})
|
||||
|
||||
var previousPeers: [SelectivePrivacyPeer]?
|
||||
|
@ -22,7 +22,7 @@ private let maxUsersDisplayedHighLimit: Int32 = 12
|
||||
private final class GroupStatsControllerArguments {
|
||||
let context: AccountContext
|
||||
let loadDetailedGraph: (StatsGraph, Int64) -> Signal<StatsGraph?, NoError>
|
||||
let openPeer: (PeerId) -> Void
|
||||
let openPeer: (EnginePeer) -> Void
|
||||
let openPeerHistory: (PeerId) -> Void
|
||||
let openPeerAdminActions: (PeerId) -> Void
|
||||
let promotePeer: (PeerId) -> Void
|
||||
@ -33,7 +33,7 @@ private final class GroupStatsControllerArguments {
|
||||
let setAdminsPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
|
||||
let setInvitersPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
|
||||
|
||||
init(context: AccountContext, loadDetailedGraph: @escaping (StatsGraph, Int64) -> Signal<StatsGraph?, NoError>, openPeer: @escaping (PeerId) -> Void, openPeerHistory: @escaping (PeerId) -> Void, openPeerAdminActions: @escaping (PeerId) -> Void, promotePeer: @escaping (PeerId) -> Void, expandTopPosters: @escaping () -> Void, expandTopAdmins: @escaping () -> Void, expandTopInviters: @escaping () -> Void, setPostersPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setAdminsPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setInvitersPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void) {
|
||||
init(context: AccountContext, loadDetailedGraph: @escaping (StatsGraph, Int64) -> Signal<StatsGraph?, NoError>, openPeer: @escaping (EnginePeer) -> Void, openPeerHistory: @escaping (PeerId) -> Void, openPeerAdminActions: @escaping (PeerId) -> Void, promotePeer: @escaping (PeerId) -> Void, expandTopPosters: @escaping () -> Void, expandTopAdmins: @escaping () -> Void, expandTopInviters: @escaping () -> Void, setPostersPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setAdminsPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setInvitersPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void) {
|
||||
self.context = context
|
||||
self.loadDetailedGraph = loadDetailedGraph
|
||||
self.openPeer = openPeer
|
||||
@ -412,7 +412,7 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
}
|
||||
}
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: EnginePeer(peer), height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", "), .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: {
|
||||
arguments.openPeer(peer.id)
|
||||
arguments.openPeer(EnginePeer(peer))
|
||||
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||
arguments.setPostersPeerIdWithRevealedOptions(peerId, fromPeerId)
|
||||
}, removePeer: { _ in })
|
||||
@ -443,7 +443,7 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
}
|
||||
}
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: EnginePeer(peer), height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", "), .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: {
|
||||
arguments.openPeer(peer.id)
|
||||
arguments.openPeer(EnginePeer(peer))
|
||||
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||
arguments.setAdminsPeerIdWithRevealedOptions(peerId, fromPeerId)
|
||||
}, removePeer: { _ in })
|
||||
@ -466,7 +466,7 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
}
|
||||
}
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: .firstLast, context: arguments.context, peer: EnginePeer(peer), height: .generic, aliasHandling: .standard, nameColor: .primary, nameStyle: .plain, presence: nil, text: .text(textComponents.joined(separator: ", "), .secondary), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, highlighted: false, selectable: arguments.context.account.peerId != peer.id, sectionId: self.section, action: {
|
||||
arguments.openPeer(peer.id)
|
||||
arguments.openPeer(EnginePeer(peer))
|
||||
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
|
||||
arguments.setInvitersPeerIdWithRevealedOptions(peerId, fromPeerId)
|
||||
}, removePeer: { _ in })
|
||||
@ -721,7 +721,7 @@ public func groupStatsController(context: AccountContext, updatedPresentationDat
|
||||
|
||||
let datacenterId: Int32 = statsDatacenterId ?? 0
|
||||
|
||||
var openPeerImpl: ((PeerId) -> Void)?
|
||||
var openPeerImpl: ((EnginePeer) -> Void)?
|
||||
var openPeerHistoryImpl: ((PeerId) -> Void)?
|
||||
var openPeerAdminActionsImpl: ((PeerId) -> Void)?
|
||||
var promotePeerImpl: ((PeerId) -> Void)?
|
||||
@ -779,8 +779,8 @@ public func groupStatsController(context: AccountContext, updatedPresentationDat
|
||||
|
||||
let arguments = GroupStatsControllerArguments(context: context, loadDetailedGraph: { graph, x -> Signal<StatsGraph?, NoError> in
|
||||
return statsContext.loadDetailedGraph(graph, x: x)
|
||||
}, openPeer: { peerId in
|
||||
openPeerImpl?(peerId)
|
||||
}, openPeer: { peer in
|
||||
openPeerImpl?(peer)
|
||||
}, openPeerHistory: { peerId in
|
||||
openPeerHistoryImpl?(peerId)
|
||||
}, openPeerAdminActions: { peerId in
|
||||
@ -864,9 +864,9 @@ public func groupStatsController(context: AccountContext, updatedPresentationDat
|
||||
controller.didDisappear = { [weak controller] _ in
|
||||
controller?.clearItemNodesHighlight(animated: true)
|
||||
}
|
||||
openPeerImpl = { [weak controller] peerId in
|
||||
openPeerImpl = { [weak controller] peer in
|
||||
if let navigationController = controller?.navigationController as? NavigationController {
|
||||
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|
||||
let _ = (context.account.postbox.loadedPeerWithId(peer.id)
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
||||
|
@ -4,7 +4,6 @@ public enum Api {
|
||||
public enum auth {}
|
||||
public enum channels {}
|
||||
public enum contacts {}
|
||||
public enum feed {}
|
||||
public enum help {}
|
||||
public enum messages {}
|
||||
public enum payments {}
|
||||
@ -22,7 +21,6 @@ public enum Api {
|
||||
public enum bots {}
|
||||
public enum channels {}
|
||||
public enum contacts {}
|
||||
public enum feed {}
|
||||
public enum folders {}
|
||||
public enum help {}
|
||||
public enum langpack {}
|
||||
@ -221,11 +219,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[179611673] = { return Api.ExportedChatInvite.parse_chatInviteExported($0) }
|
||||
dict[-317687113] = { return Api.ExportedChatInvite.parse_chatInvitePublicJoinRequests($0) }
|
||||
dict[1571494644] = { return Api.ExportedMessageLink.parse_exportedMessageLink($0) }
|
||||
dict[1348066419] = { return Api.FeedPosition.parse_feedPosition($0) }
|
||||
dict[-207944868] = { return Api.FileHash.parse_fileHash($0) }
|
||||
dict[-11252123] = { return Api.Folder.parse_folder($0) }
|
||||
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
|
||||
dict[1885902651] = { return Api.ForumTopic.parse_forumTopic($0) }
|
||||
dict[792599046] = { return Api.ForumTopic.parse_forumTopic($0) }
|
||||
dict[-1107729093] = { return Api.Game.parse_game($0) }
|
||||
dict[-1297942941] = { return Api.GeoPoint.parse_geoPoint($0) }
|
||||
dict[286776671] = { return Api.GeoPoint.parse_geoPointEmpty($0) }
|
||||
@ -772,7 +769,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-761649164] = { return Api.Update.parse_updateChannelMessageForwards($0) }
|
||||
dict[-232346616] = { return Api.Update.parse_updateChannelMessageViews($0) }
|
||||
dict[-1738720581] = { return Api.Update.parse_updateChannelParticipant($0) }
|
||||
dict[1153291573] = { return Api.Update.parse_updateChannelReadMessagesContents($0) }
|
||||
dict[-366410403] = { return Api.Update.parse_updateChannelReadMessagesContents($0) }
|
||||
dict[277713951] = { return Api.Update.parse_updateChannelTooLong($0) }
|
||||
dict[-1937192669] = { return Api.Update.parse_updateChannelUserTyping($0) }
|
||||
dict[791390623] = { return Api.Update.parse_updateChannelWebPage($0) }
|
||||
@ -841,7 +838,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1218471511] = { return Api.Update.parse_updateReadChannelOutbox($0) }
|
||||
dict[-78886548] = { return Api.Update.parse_updateReadFeaturedEmojiStickers($0) }
|
||||
dict[1461528386] = { return Api.Update.parse_updateReadFeaturedStickers($0) }
|
||||
dict[1951948721] = { return Api.Update.parse_updateReadFeed($0) }
|
||||
dict[-1667805217] = { return Api.Update.parse_updateReadHistoryInbox($0) }
|
||||
dict[791617983] = { return Api.Update.parse_updateReadHistoryOutbox($0) }
|
||||
dict[1757493555] = { return Api.Update.parse_updateReadMessagesContents($0) }
|
||||
@ -961,8 +957,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1891070632] = { return Api.contacts.TopPeers.parse_topPeers($0) }
|
||||
dict[-1255369827] = { return Api.contacts.TopPeers.parse_topPeersDisabled($0) }
|
||||
dict[-567906571] = { return Api.contacts.TopPeers.parse_topPeersNotModified($0) }
|
||||
dict[-587770695] = { return Api.feed.FeedMessages.parse_feedMessages($0) }
|
||||
dict[-619039485] = { return Api.feed.FeedMessages.parse_feedMessagesNotModified($0) }
|
||||
dict[-860107216] = { return Api.help.AppUpdate.parse_appUpdate($0) }
|
||||
dict[-1000708810] = { return Api.help.AppUpdate.parse_noAppUpdate($0) }
|
||||
dict[-2016381538] = { return Api.help.CountriesList.parse_countriesList($0) }
|
||||
@ -1287,8 +1281,6 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.ExportedMessageLink:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.FeedPosition:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.FileHash:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.Folder:
|
||||
@ -1727,8 +1719,6 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.contacts.TopPeers:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.feed.FeedMessages:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.help.AppUpdate:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.help.CountriesList:
|
||||
|
@ -671,7 +671,7 @@ public extension Api {
|
||||
case updateChannelMessageForwards(channelId: Int64, id: Int32, forwards: Int32)
|
||||
case updateChannelMessageViews(channelId: Int64, id: Int32, views: Int32)
|
||||
case updateChannelParticipant(flags: Int32, channelId: Int64, date: Int32, actorId: Int64, userId: Int64, prevParticipant: Api.ChannelParticipant?, newParticipant: Api.ChannelParticipant?, invite: Api.ExportedChatInvite?, qts: Int32)
|
||||
case updateChannelReadMessagesContents(channelId: Int64, messages: [Int32])
|
||||
case updateChannelReadMessagesContents(flags: Int32, channelId: Int64, topMsgId: Int32?, messages: [Int32])
|
||||
case updateChannelTooLong(flags: Int32, channelId: Int64, pts: Int32?)
|
||||
case updateChannelUserTyping(flags: Int32, channelId: Int64, topMsgId: Int32?, fromId: Api.Peer, action: Api.SendMessageAction)
|
||||
case updateChannelWebPage(channelId: Int64, webpage: Api.WebPage, pts: Int32, ptsCount: Int32)
|
||||
@ -740,7 +740,6 @@ public extension Api {
|
||||
case updateReadChannelOutbox(channelId: Int64, maxId: Int32)
|
||||
case updateReadFeaturedEmojiStickers
|
||||
case updateReadFeaturedStickers
|
||||
case updateReadFeed(flags: Int32, filterId: Int32, maxPosition: Api.FeedPosition, unreadCount: Int32?, unreadMutedCount: Int32?)
|
||||
case updateReadHistoryInbox(flags: Int32, folderId: Int32?, peer: Api.Peer, maxId: Int32, stillUnreadCount: Int32, pts: Int32, ptsCount: Int32)
|
||||
case updateReadHistoryOutbox(peer: Api.Peer, maxId: Int32, pts: Int32, ptsCount: Int32)
|
||||
case updateReadMessagesContents(messages: [Int32], pts: Int32, ptsCount: Int32)
|
||||
@ -925,11 +924,13 @@ public extension Api {
|
||||
if Int(flags) & Int(1 << 2) != 0 {invite!.serialize(buffer, true)}
|
||||
serializeInt32(qts, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .updateChannelReadMessagesContents(let channelId, let messages):
|
||||
case .updateChannelReadMessagesContents(let flags, let channelId, let topMsgId, let messages):
|
||||
if boxed {
|
||||
buffer.appendInt32(1153291573)
|
||||
buffer.appendInt32(-366410403)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(channelId, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(messages.count))
|
||||
for item in messages {
|
||||
@ -1510,16 +1511,6 @@ public extension Api {
|
||||
buffer.appendInt32(1461528386)
|
||||
}
|
||||
|
||||
break
|
||||
case .updateReadFeed(let flags, let filterId, let maxPosition, let unreadCount, let unreadMutedCount):
|
||||
if boxed {
|
||||
buffer.appendInt32(1951948721)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(filterId, buffer: buffer, boxed: false)
|
||||
maxPosition.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(unreadCount!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(unreadMutedCount!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .updateReadHistoryInbox(let flags, let folderId, let peer, let maxId, let stillUnreadCount, let pts, let ptsCount):
|
||||
if boxed {
|
||||
@ -1731,8 +1722,8 @@ public extension Api {
|
||||
return ("updateChannelMessageViews", [("channelId", String(describing: channelId)), ("id", String(describing: id)), ("views", String(describing: views))])
|
||||
case .updateChannelParticipant(let flags, let channelId, let date, let actorId, let userId, let prevParticipant, let newParticipant, let invite, let qts):
|
||||
return ("updateChannelParticipant", [("flags", String(describing: flags)), ("channelId", String(describing: channelId)), ("date", String(describing: date)), ("actorId", String(describing: actorId)), ("userId", String(describing: userId)), ("prevParticipant", String(describing: prevParticipant)), ("newParticipant", String(describing: newParticipant)), ("invite", String(describing: invite)), ("qts", String(describing: qts))])
|
||||
case .updateChannelReadMessagesContents(let channelId, let messages):
|
||||
return ("updateChannelReadMessagesContents", [("channelId", String(describing: channelId)), ("messages", String(describing: messages))])
|
||||
case .updateChannelReadMessagesContents(let flags, let channelId, let topMsgId, let messages):
|
||||
return ("updateChannelReadMessagesContents", [("flags", String(describing: flags)), ("channelId", String(describing: channelId)), ("topMsgId", String(describing: topMsgId)), ("messages", String(describing: messages))])
|
||||
case .updateChannelTooLong(let flags, let channelId, let pts):
|
||||
return ("updateChannelTooLong", [("flags", String(describing: flags)), ("channelId", String(describing: channelId)), ("pts", String(describing: pts))])
|
||||
case .updateChannelUserTyping(let flags, let channelId, let topMsgId, let fromId, let action):
|
||||
@ -1869,8 +1860,6 @@ public extension Api {
|
||||
return ("updateReadFeaturedEmojiStickers", [])
|
||||
case .updateReadFeaturedStickers:
|
||||
return ("updateReadFeaturedStickers", [])
|
||||
case .updateReadFeed(let flags, let filterId, let maxPosition, let unreadCount, let unreadMutedCount):
|
||||
return ("updateReadFeed", [("flags", String(describing: flags)), ("filterId", String(describing: filterId)), ("maxPosition", String(describing: maxPosition)), ("unreadCount", String(describing: unreadCount)), ("unreadMutedCount", String(describing: unreadMutedCount))])
|
||||
case .updateReadHistoryInbox(let flags, let folderId, let peer, let maxId, let stillUnreadCount, let pts, let ptsCount):
|
||||
return ("updateReadHistoryInbox", [("flags", String(describing: flags)), ("folderId", String(describing: folderId)), ("peer", String(describing: peer)), ("maxId", String(describing: maxId)), ("stillUnreadCount", String(describing: stillUnreadCount)), ("pts", String(describing: pts)), ("ptsCount", String(describing: ptsCount))])
|
||||
case .updateReadHistoryOutbox(let peer, let maxId, let pts, let ptsCount):
|
||||
@ -2294,16 +2283,22 @@ public extension Api {
|
||||
}
|
||||
}
|
||||
public static func parse_updateChannelReadMessagesContents(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: [Int32]?
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
|
||||
var _4: [Int32]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||
_4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.Update.updateChannelReadMessagesContents(channelId: _1!, messages: _2!)
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.Update.updateChannelReadMessagesContents(flags: _1!, channelId: _2!, topMsgId: _3, messages: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -3452,31 +3447,6 @@ public extension Api {
|
||||
public static func parse_updateReadFeaturedStickers(_ reader: BufferReader) -> Update? {
|
||||
return Api.Update.updateReadFeaturedStickers
|
||||
}
|
||||
public static func parse_updateReadFeed(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Api.FeedPosition?
|
||||
if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.FeedPosition
|
||||
}
|
||||
var _4: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt32() }
|
||||
var _5: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.Update.updateReadFeed(flags: _1!, filterId: _2!, maxPosition: _3!, unreadCount: _4, unreadMutedCount: _5)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateReadHistoryInbox(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
|
@ -1,99 +1,3 @@
|
||||
public extension Api.feed {
|
||||
enum FeedMessages: TypeConstructorDescription {
|
||||
case feedMessages(flags: Int32, maxPosition: Api.FeedPosition?, minPosition: Api.FeedPosition?, readMaxPosition: Api.FeedPosition?, messages: [Api.Message], chats: [Api.Chat], users: [Api.User])
|
||||
case feedMessagesNotModified
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .feedMessages(let flags, let maxPosition, let minPosition, let readMaxPosition, let messages, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-587770695)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {maxPosition!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {minPosition!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {readMaxPosition!.serialize(buffer, true)}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(messages.count))
|
||||
for item in messages {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .feedMessagesNotModified:
|
||||
if boxed {
|
||||
buffer.appendInt32(-619039485)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .feedMessages(let flags, let maxPosition, let minPosition, let readMaxPosition, let messages, let chats, let users):
|
||||
return ("feedMessages", [("flags", String(describing: flags)), ("maxPosition", String(describing: maxPosition)), ("minPosition", String(describing: minPosition)), ("readMaxPosition", String(describing: readMaxPosition)), ("messages", String(describing: messages)), ("chats", String(describing: chats)), ("users", String(describing: users))])
|
||||
case .feedMessagesNotModified:
|
||||
return ("feedMessagesNotModified", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_feedMessages(_ reader: BufferReader) -> FeedMessages? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.FeedPosition?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.FeedPosition
|
||||
} }
|
||||
var _3: Api.FeedPosition?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.FeedPosition
|
||||
} }
|
||||
var _4: Api.FeedPosition?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.FeedPosition
|
||||
} }
|
||||
var _5: [Api.Message]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self)
|
||||
}
|
||||
var _6: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _7: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.feed.FeedMessages.feedMessages(flags: _1!, maxPosition: _2, minPosition: _3, readMaxPosition: _4, messages: _5!, chats: _6!, users: _7!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_feedMessagesNotModified(_ reader: BufferReader) -> FeedMessages? {
|
||||
return Api.feed.FeedMessages.feedMessagesNotModified
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.help {
|
||||
enum AppUpdate: TypeConstructorDescription {
|
||||
case appUpdate(flags: Int32, id: Int32, version: String, text: String, entities: [Api.MessageEntity], document: Api.Document?, url: String?, sticker: Api.Document?)
|
||||
@ -1316,3 +1220,125 @@ public extension Api.messages {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum BotCallbackAnswer: TypeConstructorDescription {
|
||||
case botCallbackAnswer(flags: Int32, message: String?, url: String?, cacheTime: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .botCallbackAnswer(let flags, let message, let url, let cacheTime):
|
||||
if boxed {
|
||||
buffer.appendInt32(911761060)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(message!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)}
|
||||
serializeInt32(cacheTime, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .botCallbackAnswer(let flags, let message, let url, let cacheTime):
|
||||
return ("botCallbackAnswer", [("flags", String(describing: flags)), ("message", String(describing: message)), ("url", String(describing: url)), ("cacheTime", String(describing: cacheTime))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_botCallbackAnswer(_ reader: BufferReader) -> BotCallbackAnswer? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_2 = parseString(reader) }
|
||||
var _3: String?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {_3 = parseString(reader) }
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.messages.BotCallbackAnswer.botCallbackAnswer(flags: _1!, message: _2, url: _3, cacheTime: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum BotResults: TypeConstructorDescription {
|
||||
case botResults(flags: Int32, queryId: Int64, nextOffset: String?, switchPm: Api.InlineBotSwitchPM?, results: [Api.BotInlineResult], cacheTime: Int32, users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .botResults(let flags, let queryId, let nextOffset, let switchPm, let results, let cacheTime, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1803769784)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(queryId, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {switchPm!.serialize(buffer, true)}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(results.count))
|
||||
for item in results {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
serializeInt32(cacheTime, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .botResults(let flags, let queryId, let nextOffset, let switchPm, let results, let cacheTime, let users):
|
||||
return ("botResults", [("flags", String(describing: flags)), ("queryId", String(describing: queryId)), ("nextOffset", String(describing: nextOffset)), ("switchPm", String(describing: switchPm)), ("results", String(describing: results)), ("cacheTime", String(describing: cacheTime)), ("users", String(describing: users))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_botResults(_ reader: BufferReader) -> BotResults? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: String?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) }
|
||||
var _4: Api.InlineBotSwitchPM?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.InlineBotSwitchPM
|
||||
} }
|
||||
var _5: [Api.BotInlineResult]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotInlineResult.self)
|
||||
}
|
||||
var _6: Int32?
|
||||
_6 = reader.readInt32()
|
||||
var _7: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.messages.BotResults.botResults(flags: _1!, queryId: _2!, nextOffset: _3, switchPm: _4, results: _5!, cacheTime: _6!, users: _7!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,125 +1,3 @@
|
||||
public extension Api.messages {
|
||||
enum BotCallbackAnswer: TypeConstructorDescription {
|
||||
case botCallbackAnswer(flags: Int32, message: String?, url: String?, cacheTime: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .botCallbackAnswer(let flags, let message, let url, let cacheTime):
|
||||
if boxed {
|
||||
buffer.appendInt32(911761060)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(message!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeString(url!, buffer: buffer, boxed: false)}
|
||||
serializeInt32(cacheTime, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .botCallbackAnswer(let flags, let message, let url, let cacheTime):
|
||||
return ("botCallbackAnswer", [("flags", String(describing: flags)), ("message", String(describing: message)), ("url", String(describing: url)), ("cacheTime", String(describing: cacheTime))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_botCallbackAnswer(_ reader: BufferReader) -> BotCallbackAnswer? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_2 = parseString(reader) }
|
||||
var _3: String?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {_3 = parseString(reader) }
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.messages.BotCallbackAnswer.botCallbackAnswer(flags: _1!, message: _2, url: _3, cacheTime: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum BotResults: TypeConstructorDescription {
|
||||
case botResults(flags: Int32, queryId: Int64, nextOffset: String?, switchPm: Api.InlineBotSwitchPM?, results: [Api.BotInlineResult], cacheTime: Int32, users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .botResults(let flags, let queryId, let nextOffset, let switchPm, let results, let cacheTime, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1803769784)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(queryId, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {switchPm!.serialize(buffer, true)}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(results.count))
|
||||
for item in results {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
serializeInt32(cacheTime, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .botResults(let flags, let queryId, let nextOffset, let switchPm, let results, let cacheTime, let users):
|
||||
return ("botResults", [("flags", String(describing: flags)), ("queryId", String(describing: queryId)), ("nextOffset", String(describing: nextOffset)), ("switchPm", String(describing: switchPm)), ("results", String(describing: results)), ("cacheTime", String(describing: cacheTime)), ("users", String(describing: users))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_botResults(_ reader: BufferReader) -> BotResults? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: String?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) }
|
||||
var _4: Api.InlineBotSwitchPM?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.InlineBotSwitchPM
|
||||
} }
|
||||
var _5: [Api.BotInlineResult]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.BotInlineResult.self)
|
||||
}
|
||||
var _6: Int32?
|
||||
_6 = reader.readInt32()
|
||||
var _7: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.messages.BotResults.botResults(flags: _1!, queryId: _2!, nextOffset: _3, switchPm: _4, results: _5!, cacheTime: _6!, users: _7!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum ChatAdminsWithInvites: TypeConstructorDescription {
|
||||
case chatAdminsWithInvites(admins: [Api.ChatAdminWithInvites], users: [Api.User])
|
||||
|
@ -2983,44 +2983,6 @@ public extension Api.functions.contacts {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.feed {
|
||||
static func getFeed(flags: Int32, filterId: Int32, offsetPosition: Api.FeedPosition?, addOffset: Int32, limit: Int32, maxPosition: Api.FeedPosition?, minPosition: Api.FeedPosition?, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.feed.FeedMessages>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(2121717715)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(filterId, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {offsetPosition!.serialize(buffer, true)}
|
||||
serializeInt32(addOffset, buffer: buffer, boxed: false)
|
||||
serializeInt32(limit, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {maxPosition!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {minPosition!.serialize(buffer, true)}
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "feed.getFeed", parameters: [("flags", String(describing: flags)), ("filterId", String(describing: filterId)), ("offsetPosition", String(describing: offsetPosition)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxPosition", String(describing: maxPosition)), ("minPosition", String(describing: minPosition)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.feed.FeedMessages? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.feed.FeedMessages?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.feed.FeedMessages
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.feed {
|
||||
static func readFeed(filterId: Int32, maxPosition: Api.FeedPosition) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1271479809)
|
||||
serializeInt32(filterId, buffer: buffer, boxed: false)
|
||||
maxPosition.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "feed.readFeed", parameters: [("filterId", String(describing: filterId)), ("maxPosition", String(describing: maxPosition))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.folders {
|
||||
static func deleteFolder(folderId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
@ -5155,16 +5117,18 @@ public extension Api.functions.messages {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func getUnreadMentions(peer: Api.InputPeer, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.Messages>) {
|
||||
static func getUnreadMentions(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.Messages>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1180140658)
|
||||
buffer.appendInt32(-251140208)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)}
|
||||
serializeInt32(offsetId, buffer: buffer, boxed: false)
|
||||
serializeInt32(addOffset, buffer: buffer, boxed: false)
|
||||
serializeInt32(limit, buffer: buffer, boxed: false)
|
||||
serializeInt32(maxId, buffer: buffer, boxed: false)
|
||||
serializeInt32(minId, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "messages.getUnreadMentions", parameters: [("peer", String(describing: peer)), ("offsetId", String(describing: offsetId)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in
|
||||
return (FunctionDescription(name: "messages.getUnreadMentions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId)), ("offsetId", String(describing: offsetId)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.Messages?
|
||||
if let signature = reader.readInt32() {
|
||||
@ -5175,16 +5139,18 @@ public extension Api.functions.messages {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func getUnreadReactions(peer: Api.InputPeer, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.Messages>) {
|
||||
static func getUnreadReactions(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.Messages>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-396644838)
|
||||
buffer.appendInt32(841173339)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)}
|
||||
serializeInt32(offsetId, buffer: buffer, boxed: false)
|
||||
serializeInt32(addOffset, buffer: buffer, boxed: false)
|
||||
serializeInt32(limit, buffer: buffer, boxed: false)
|
||||
serializeInt32(maxId, buffer: buffer, boxed: false)
|
||||
serializeInt32(minId, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "messages.getUnreadReactions", parameters: [("peer", String(describing: peer)), ("offsetId", String(describing: offsetId)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in
|
||||
return (FunctionDescription(name: "messages.getUnreadReactions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId)), ("offsetId", String(describing: offsetId)), ("addOffset", String(describing: addOffset)), ("limit", String(describing: limit)), ("maxId", String(describing: maxId)), ("minId", String(describing: minId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.Messages?
|
||||
if let signature = reader.readInt32() {
|
||||
@ -5466,11 +5432,13 @@ public extension Api.functions.messages {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func readMentions(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.AffectedHistory>) {
|
||||
static func readMentions(flags: Int32, peer: Api.InputPeer, topMsgId: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.AffectedHistory>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(251759059)
|
||||
buffer.appendInt32(921026381)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "messages.readMentions", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topMsgId!, buffer: buffer, boxed: false)}
|
||||
return (FunctionDescription(name: "messages.readMentions", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("topMsgId", String(describing: topMsgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AffectedHistory? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.AffectedHistory?
|
||||
if let signature = reader.readInt32() {
|
||||
|
@ -956,52 +956,6 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum FeedPosition: TypeConstructorDescription {
|
||||
case feedPosition(date: Int32, peer: Api.Peer, id: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .feedPosition(let date, let peer, let id):
|
||||
if boxed {
|
||||
buffer.appendInt32(1348066419)
|
||||
}
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .feedPosition(let date, let peer, let id):
|
||||
return ("feedPosition", [("date", String(describing: date)), ("peer", String(describing: peer)), ("id", String(describing: id))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_feedPosition(_ reader: BufferReader) -> FeedPosition? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.FeedPosition.feedPosition(date: _1!, peer: _2!, id: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum FileHash: TypeConstructorDescription {
|
||||
case fileHash(offset: Int64, limit: Int32, hash: Buffer)
|
||||
@ -1138,3 +1092,79 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum ForumTopic: TypeConstructorDescription {
|
||||
case forumTopic(flags: Int32, id: Int32, date: Int32, title: String, iconEmojiId: Int64?, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, unreadMentionsCount: Int32, unreadReactionsCount: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .forumTopic(let flags, let id, let date, let title, let iconEmojiId, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let unreadMentionsCount, let unreadReactionsCount):
|
||||
if boxed {
|
||||
buffer.appendInt32(792599046)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(iconEmojiId!, buffer: buffer, boxed: false)}
|
||||
serializeInt32(topMessage, buffer: buffer, boxed: false)
|
||||
serializeInt32(readInboxMaxId, buffer: buffer, boxed: false)
|
||||
serializeInt32(readOutboxMaxId, buffer: buffer, boxed: false)
|
||||
serializeInt32(unreadCount, buffer: buffer, boxed: false)
|
||||
serializeInt32(unreadMentionsCount, buffer: buffer, boxed: false)
|
||||
serializeInt32(unreadReactionsCount, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .forumTopic(let flags, let id, let date, let title, let iconEmojiId, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let unreadMentionsCount, let unreadReactionsCount):
|
||||
return ("forumTopic", [("flags", String(describing: flags)), ("id", String(describing: id)), ("date", String(describing: date)), ("title", String(describing: title)), ("iconEmojiId", String(describing: iconEmojiId)), ("topMessage", String(describing: topMessage)), ("readInboxMaxId", String(describing: readInboxMaxId)), ("readOutboxMaxId", String(describing: readOutboxMaxId)), ("unreadCount", String(describing: unreadCount)), ("unreadMentionsCount", String(describing: unreadMentionsCount)), ("unreadReactionsCount", String(describing: unreadReactionsCount))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_forumTopic(_ reader: BufferReader) -> ForumTopic? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
var _5: Int64?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt64() }
|
||||
var _6: Int32?
|
||||
_6 = reader.readInt32()
|
||||
var _7: Int32?
|
||||
_7 = reader.readInt32()
|
||||
var _8: Int32?
|
||||
_8 = reader.readInt32()
|
||||
var _9: Int32?
|
||||
_9 = reader.readInt32()
|
||||
var _10: Int32?
|
||||
_10 = reader.readInt32()
|
||||
var _11: Int32?
|
||||
_11 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
let _c9 = _9 != nil
|
||||
let _c10 = _10 != nil
|
||||
let _c11 = _11 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
|
||||
return Api.ForumTopic.forumTopic(flags: _1!, id: _2!, date: _3!, title: _4!, iconEmojiId: _5, topMessage: _6!, readInboxMaxId: _7!, readOutboxMaxId: _8!, unreadCount: _9!, unreadMentionsCount: _10!, unreadReactionsCount: _11!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,71 +1,3 @@
|
||||
public extension Api {
|
||||
enum ForumTopic: TypeConstructorDescription {
|
||||
case forumTopic(flags: Int32, id: Int32, date: Int32, title: String, iconEmojiId: Int64?, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .forumTopic(let flags, let id, let date, let title, let iconEmojiId, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount):
|
||||
if boxed {
|
||||
buffer.appendInt32(1885902651)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(iconEmojiId!, buffer: buffer, boxed: false)}
|
||||
serializeInt32(topMessage, buffer: buffer, boxed: false)
|
||||
serializeInt32(readInboxMaxId, buffer: buffer, boxed: false)
|
||||
serializeInt32(readOutboxMaxId, buffer: buffer, boxed: false)
|
||||
serializeInt32(unreadCount, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .forumTopic(let flags, let id, let date, let title, let iconEmojiId, let topMessage, let readInboxMaxId, let readOutboxMaxId, let unreadCount):
|
||||
return ("forumTopic", [("flags", String(describing: flags)), ("id", String(describing: id)), ("date", String(describing: date)), ("title", String(describing: title)), ("iconEmojiId", String(describing: iconEmojiId)), ("topMessage", String(describing: topMessage)), ("readInboxMaxId", String(describing: readInboxMaxId)), ("readOutboxMaxId", String(describing: readOutboxMaxId)), ("unreadCount", String(describing: unreadCount))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_forumTopic(_ reader: BufferReader) -> ForumTopic? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
var _5: Int64?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt64() }
|
||||
var _6: Int32?
|
||||
_6 = reader.readInt32()
|
||||
var _7: Int32?
|
||||
_7 = reader.readInt32()
|
||||
var _8: Int32?
|
||||
_8 = reader.readInt32()
|
||||
var _9: Int32?
|
||||
_9 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
let _c9 = _9 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
||||
return Api.ForumTopic.forumTopic(flags: _1!, id: _2!, date: _3!, title: _4!, iconEmojiId: _5, topMessage: _6!, readInboxMaxId: _7!, readOutboxMaxId: _8!, unreadCount: _9!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum Game: TypeConstructorDescription {
|
||||
case game(flags: Int32, id: Int64, accessHash: Int64, shortName: String, title: String, description: String, photo: Api.Photo, document: Api.Document?)
|
||||
|
@ -193,7 +193,7 @@ func _internal_loadMessageHistoryThreads(account: Account, peerId: PeerId) -> Si
|
||||
|
||||
for topic in topics {
|
||||
switch topic {
|
||||
case let .forumTopic(_, id, _, title, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount):
|
||||
case let .forumTopic(_, id, _, title, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, _, _):
|
||||
let data = MessageHistoryThreadData(
|
||||
info: EngineMessageHistoryThread.Info(
|
||||
title: title,
|
||||
|
@ -1374,7 +1374,8 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
|
||||
}
|
||||
case let .updateReadMessagesContents(messages, _, _):
|
||||
updatedState.addReadMessagesContents((nil, messages))
|
||||
case let .updateChannelReadMessagesContents(channelId, messages):
|
||||
case let .updateChannelReadMessagesContents(_, channelId, topMsgId, messages):
|
||||
let _ = topMsgId
|
||||
updatedState.addReadMessagesContents((PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), messages))
|
||||
case let .updateChannelMessageViews(channelId, id, views):
|
||||
updatedState.addUpdateMessageImpressionCount(id: MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), namespace: Namespaces.Message.Cloud, id: id), count: views)
|
||||
@ -1693,7 +1694,7 @@ func resolveForumThreads(postbox: Postbox, network: Network, state: AccountMutab
|
||||
|
||||
for topic in topics {
|
||||
switch topic {
|
||||
case let .forumTopic(_, id, _, title, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount):
|
||||
case let .forumTopic(_, id, _, title, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, _, _):
|
||||
state.operations.append(.ResetForumTopic(topicId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id), data: MessageHistoryThreadData(
|
||||
info: EngineMessageHistoryThread.Info(
|
||||
title: title,
|
||||
@ -1785,7 +1786,7 @@ func resolveForumThreads(postbox: Postbox, network: Network, fetchedChatList: Fe
|
||||
|
||||
for topic in topics {
|
||||
switch topic {
|
||||
case let .forumTopic(_, id, _, title, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount):
|
||||
case let .forumTopic(_, id, _, title, iconEmojiId, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, _, _):
|
||||
fetchedChatList.threadInfos[MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id)] = MessageHistoryThreadData(
|
||||
info: EngineMessageHistoryThread.Info(
|
||||
title: title,
|
||||
@ -2429,7 +2430,8 @@ private func pollChannel(postbox: Postbox, network: Network, peer: Peer, state:
|
||||
updatedState.updateMessagesPinned(ids: messages.map { id in
|
||||
MessageId(peerId: channelPeerId, namespace: Namespaces.Message.Cloud, id: id)
|
||||
}, pinned: (flags & (1 << 0)) != 0)
|
||||
case let .updateChannelReadMessagesContents(_, messages):
|
||||
case let .updateChannelReadMessagesContents(_, _, topMsgId, messages):
|
||||
let _ = topMsgId
|
||||
updatedState.addReadMessagesContents((peer.id, messages))
|
||||
case let .updateChannelMessageViews(_, id, views):
|
||||
updatedState.addUpdateMessageImpressionCount(id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id), count: views)
|
||||
|
@ -493,9 +493,9 @@ private func validateChannelMessagesBatch(postbox: Postbox, network: Network, ac
|
||||
let requestSignal: Signal<Api.messages.Messages, MTRpcError>
|
||||
if let tag = tag {
|
||||
if tag == MessageTags.unseenPersonalMessage {
|
||||
requestSignal = network.request(Api.functions.messages.getUnreadMentions(peer: inputPeer, offsetId: messageIds[messageIds.count - 1].id + 1, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1))
|
||||
requestSignal = network.request(Api.functions.messages.getUnreadMentions(flags: 0, peer: inputPeer, topMsgId: nil, offsetId: messageIds[messageIds.count - 1].id + 1, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1))
|
||||
} else if tag == MessageTags.unseenReaction {
|
||||
requestSignal = network.request(Api.functions.messages.getUnreadReactions(peer: inputPeer, offsetId: messageIds[messageIds.count - 1].id + 1, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1))
|
||||
requestSignal = network.request(Api.functions.messages.getUnreadReactions(flags: 0, peer: inputPeer, topMsgId: nil, offsetId: messageIds[messageIds.count - 1].id + 1, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1))
|
||||
} else if let filter = messageFilterForTagMask(tag) {
|
||||
requestSignal = network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, topMsgId: nil, filter: filter, minDate: 0, maxDate: 0, offsetId: messageIds[messageIds.count - 1].id + 1, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1, hash: hash))
|
||||
} else {
|
||||
|
@ -406,7 +406,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
|
||||
minMaxRange = 1 ... Int32.max - 1
|
||||
}
|
||||
|
||||
request = source.request(Api.functions.messages.getUnreadMentions(peer: inputPeer, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId))
|
||||
request = source.request(Api.functions.messages.getUnreadMentions(flags: 0, peer: inputPeer, topMsgId: nil, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId))
|
||||
} else if tag == .unseenReaction {
|
||||
let offsetId: Int32
|
||||
let addOffset: Int32
|
||||
@ -454,7 +454,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
|
||||
minMaxRange = 1 ... Int32.max - 1
|
||||
}
|
||||
|
||||
request = source.request(Api.functions.messages.getUnreadReactions(peer: inputPeer, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId))
|
||||
request = source.request(Api.functions.messages.getUnreadReactions(flags: 0, peer: inputPeer, topMsgId: nil, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId))
|
||||
} else if tag == .liveLocation {
|
||||
let selectedLimit = count
|
||||
|
||||
|
@ -123,7 +123,7 @@ private func synchronizeMarkAllUnseen(transaction: Transaction, postbox: Postbox
|
||||
let inputChannel = transaction.getPeer(peerId).flatMap(apiInputChannel)
|
||||
let limit: Int32 = 100
|
||||
let oneOperation: (Int32) -> Signal<Int32?, MTRpcError> = { maxId in
|
||||
return network.request(Api.functions.messages.getUnreadMentions(peer: inputPeer, offsetId: maxId, addOffset: maxId == 0 ? 0 : -1, limit: limit, maxId: maxId == 0 ? 0 : (maxId + 1), minId: 1))
|
||||
return network.request(Api.functions.messages.getUnreadMentions(flags: 0, peer: inputPeer, topMsgId: nil, offsetId: maxId, addOffset: maxId == 0 ? 0 : -1, limit: limit, maxId: maxId == 0 ? 0 : (maxId + 1), minId: 1))
|
||||
|> mapToSignal { result -> Signal<[MessageId], MTRpcError> in
|
||||
switch result {
|
||||
case let .messages(messages, _, _):
|
||||
|
@ -7,6 +7,7 @@ private struct DiscussionMessage {
|
||||
var messageId: MessageId
|
||||
var channelMessageId: MessageId?
|
||||
var isChannelPost: Bool
|
||||
var isForumPost: Bool
|
||||
var maxMessage: MessageId?
|
||||
var maxReadIncomingMessageId: MessageId?
|
||||
var maxReadOutgoingMessageId: MessageId?
|
||||
@ -242,10 +243,16 @@ private class ReplyThreadHistoryContextImpl {
|
||||
})
|
||||
}
|
||||
|
||||
var isForumPost = false
|
||||
if let channel = transaction.getPeer(parsedIndex.id.peerId) as? TelegramChannel, channel.flags.contains(.isForum) {
|
||||
isForumPost = true
|
||||
}
|
||||
|
||||
return .single(DiscussionMessage(
|
||||
messageId: parsedIndex.id,
|
||||
channelMessageId: channelMessageId,
|
||||
isChannelPost: isChannelPost,
|
||||
isForumPost: isForumPost,
|
||||
maxMessage: resolvedMaxMessage,
|
||||
maxReadIncomingMessageId: maxReadIncomingMessageId,
|
||||
maxReadOutgoingMessageId: readOutboxMaxId.flatMap { readMaxId in
|
||||
@ -554,6 +561,7 @@ public struct ChatReplyThreadMessage: Equatable {
|
||||
public var messageId: MessageId
|
||||
public var channelMessageId: MessageId?
|
||||
public var isChannelPost: Bool
|
||||
public var isForumPost: Bool
|
||||
public var maxMessage: MessageId?
|
||||
public var maxReadIncomingMessageId: MessageId?
|
||||
public var maxReadOutgoingMessageId: MessageId?
|
||||
@ -562,10 +570,11 @@ public struct ChatReplyThreadMessage: Equatable {
|
||||
public var initialAnchor: Anchor
|
||||
public var isNotAvailable: Bool
|
||||
|
||||
fileprivate init(messageId: MessageId, channelMessageId: MessageId?, isChannelPost: Bool, maxMessage: MessageId?, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, unreadCount: Int, initialFilledHoles: IndexSet, initialAnchor: Anchor, isNotAvailable: Bool) {
|
||||
fileprivate init(messageId: MessageId, channelMessageId: MessageId?, isChannelPost: Bool, isForumPost: Bool, maxMessage: MessageId?, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, unreadCount: Int, initialFilledHoles: IndexSet, initialAnchor: Anchor, isNotAvailable: Bool) {
|
||||
self.messageId = messageId
|
||||
self.channelMessageId = channelMessageId
|
||||
self.isChannelPost = isChannelPost
|
||||
self.isForumPost = isForumPost
|
||||
self.maxMessage = maxMessage
|
||||
self.maxReadIncomingMessageId = maxReadIncomingMessageId
|
||||
self.maxReadOutgoingMessageId = maxReadOutgoingMessageId
|
||||
@ -664,10 +673,16 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa
|
||||
}
|
||||
}
|
||||
|
||||
var isForumPost = false
|
||||
if let channel = transaction.getPeer(parsedIndex.id.peerId) as? TelegramChannel, channel.flags.contains(.isForum) {
|
||||
isForumPost = true
|
||||
}
|
||||
|
||||
return DiscussionMessage(
|
||||
messageId: parsedIndex.id,
|
||||
channelMessageId: channelMessageId,
|
||||
isChannelPost: isChannelPost,
|
||||
isForumPost: isForumPost,
|
||||
maxMessage: resolvedMaxMessage,
|
||||
maxReadIncomingMessageId: readInboxMaxId.flatMap { readMaxId in
|
||||
MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId)
|
||||
@ -691,6 +706,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa
|
||||
messageId: messageId,
|
||||
channelMessageId: nil,
|
||||
isChannelPost: false,
|
||||
isForumPost: true,
|
||||
maxMessage: MessageId(peerId: messageId.peerId, namespace: messageId.namespace, id: threadData.maxKnownMessageId),
|
||||
maxReadIncomingMessageId: MessageId(peerId: messageId.peerId, namespace: messageId.namespace, id: threadData.maxIncomingReadId),
|
||||
maxReadOutgoingMessageId: MessageId(peerId: messageId.peerId, namespace: messageId.namespace, id: threadData.maxOutgoingReadId),
|
||||
@ -929,6 +945,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa
|
||||
messageId: discussionMessage.messageId,
|
||||
channelMessageId: discussionMessage.channelMessageId,
|
||||
isChannelPost: discussionMessage.isChannelPost,
|
||||
isForumPost: discussionMessage.isForumPost,
|
||||
maxMessage: discussionMessage.maxMessage,
|
||||
maxReadIncomingMessageId: discussionMessage.maxReadIncomingMessageId,
|
||||
maxReadOutgoingMessageId: discussionMessage.maxReadOutgoingMessageId,
|
||||
|
@ -47,9 +47,9 @@ public enum ExternalJoiningChatState {
|
||||
}
|
||||
|
||||
case invite(Invite)
|
||||
case alreadyJoined(PeerId)
|
||||
case alreadyJoined(EnginePeer)
|
||||
case invalidHash
|
||||
case peek(PeerId, Int32)
|
||||
case peek(EnginePeer, Int32)
|
||||
}
|
||||
|
||||
func _internal_joinChatInteractively(with hash: String, account: Account) -> Signal<PeerId?, JoinLinkError> {
|
||||
@ -112,7 +112,7 @@ func _internal_joinLinkInformation(_ hash: String, account: Account) -> Signal<E
|
||||
return updated
|
||||
})
|
||||
|
||||
return .alreadyJoined(peer.id)
|
||||
return .alreadyJoined(EnginePeer(peer))
|
||||
})
|
||||
|> castError(JoinLinkInfoError.self)
|
||||
}
|
||||
@ -124,7 +124,7 @@ func _internal_joinLinkInformation(_ hash: String, account: Account) -> Signal<E
|
||||
return updated
|
||||
})
|
||||
|
||||
return .peek(peer.id, expires)
|
||||
return .peek(EnginePeer(peer), expires)
|
||||
})
|
||||
|> castError(JoinLinkInfoError.self)
|
||||
}
|
||||
|
@ -524,8 +524,18 @@ public extension TelegramEngine {
|
||||
}
|
||||
}
|
||||
|
||||
public func joinChatInteractively(with hash: String) -> Signal <PeerId?, JoinLinkError> {
|
||||
public func joinChatInteractively(with hash: String) -> Signal <EnginePeer?, JoinLinkError> {
|
||||
let account = self.account
|
||||
return _internal_joinChatInteractively(with: hash, account: self.account)
|
||||
|> mapToSignal { id -> Signal <EnginePeer?, JoinLinkError> in
|
||||
guard let id = id else {
|
||||
return .single(nil)
|
||||
}
|
||||
return account.postbox.transaction { transaction -> EnginePeer? in
|
||||
return transaction.getPeer(id).flatMap(EnginePeer.init)
|
||||
}
|
||||
|> castError(JoinLinkError.self)
|
||||
}
|
||||
}
|
||||
|
||||
public func joinLinkInformation(_ hash: String) -> Signal<ExternalJoiningChatState, JoinLinkInfoError> {
|
||||
|
@ -299,6 +299,7 @@ swift_library(
|
||||
"//submodules/Components/LottieAnimationComponent:LottieAnimationComponent",
|
||||
"//submodules/TelegramUI/Components/ForumTopicListScreen:ForumTopicListScreen",
|
||||
"//submodules/TelegramUI/Components/ForumCreateTopicScreen:ForumCreateTopicScreen",
|
||||
"//submodules/TelegramUI/Components/ChatTitleView",
|
||||
] + select({
|
||||
"@build_bazel_rules_apple//apple:ios_armv7": [],
|
||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
||||
|
37
submodules/TelegramUI/Components/ChatTitleView/BUILD
Normal file
37
submodules/TelegramUI/Components/ChatTitleView/BUILD
Normal file
@ -0,0 +1,37 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "ChatTitleView",
|
||||
module_name = "ChatTitleView",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/Postbox:Postbox",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/LegacyComponents:LegacyComponents",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
|
||||
"//submodules/ActivityIndicator:ActivityIndicator",
|
||||
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
|
||||
"//submodules/PeerPresenceStatusManager:PeerPresenceStatusManager",
|
||||
"//submodules/ChatTitleActivityNode",
|
||||
"//submodules/LocalizedPeerData",
|
||||
"//submodules/PhoneNumberFormat",
|
||||
"//submodules/AccountContext",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/AnimatedCountLabelNode",
|
||||
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
|
||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -0,0 +1,848 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
import LegacyComponents
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import ActivityIndicator
|
||||
import TelegramStringFormatting
|
||||
import PeerPresenceStatusManager
|
||||
import ChatTitleActivityNode
|
||||
import LocalizedPeerData
|
||||
import PhoneNumberFormat
|
||||
import ChatTitleActivityNode
|
||||
import AnimatedCountLabelNode
|
||||
import AccountContext
|
||||
import ComponentFlow
|
||||
import EmojiStatusComponent
|
||||
import AnimationCache
|
||||
import MultiAnimationRenderer
|
||||
|
||||
private let titleFont = Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers])
|
||||
private let subtitleFont = Font.regular(13.0)
|
||||
|
||||
public enum ChatTitleContent {
|
||||
public enum ReplyThreadType {
|
||||
case comments
|
||||
case replies
|
||||
}
|
||||
|
||||
case peer(peerView: PeerView, customTitle: String?, onlineMemberCount: Int32?, isScheduledMessages: Bool)
|
||||
case replyThread(type: ReplyThreadType, count: Int)
|
||||
case custom(String, String?, Bool)
|
||||
}
|
||||
|
||||
private enum ChatTitleIcon {
|
||||
case none
|
||||
case lock
|
||||
case mute
|
||||
}
|
||||
|
||||
private enum ChatTitleCredibilityIcon: Equatable {
|
||||
case none
|
||||
case fake
|
||||
case scam
|
||||
case verified
|
||||
case premium
|
||||
case emojiStatus(PeerEmojiStatus)
|
||||
}
|
||||
|
||||
public final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
private let context: AccountContext
|
||||
|
||||
private var theme: PresentationTheme
|
||||
private var hasEmbeddedTitleContent: Bool = false
|
||||
private var strings: PresentationStrings
|
||||
private var dateTimeFormat: PresentationDateTimeFormat
|
||||
private var nameDisplayOrder: PresentationPersonNameOrder
|
||||
private let animationCache: AnimationCache
|
||||
private let animationRenderer: MultiAnimationRenderer
|
||||
|
||||
private let contentContainer: ASDisplayNode
|
||||
public let titleContainerView: PortalSourceView
|
||||
public let titleTextNode: ImmediateAnimatedCountLabelNode
|
||||
public let titleLeftIconNode: ASImageNode
|
||||
public let titleRightIconNode: ASImageNode
|
||||
public let titleCredibilityIconView: ComponentHostView<Empty>
|
||||
public let activityNode: ChatTitleActivityNode
|
||||
|
||||
private let button: HighlightTrackingButtonNode
|
||||
|
||||
private var validLayout: (CGSize, CGRect)?
|
||||
|
||||
private var titleLeftIcon: ChatTitleIcon = .none
|
||||
private var titleRightIcon: ChatTitleIcon = .none
|
||||
private var titleCredibilityIcon: ChatTitleCredibilityIcon = .none
|
||||
|
||||
private var presenceManager: PeerPresenceStatusManager?
|
||||
|
||||
private var pointerInteraction: PointerInteraction?
|
||||
|
||||
public var inputActivities: (PeerId, [(Peer, PeerInputActivity)])? {
|
||||
didSet {
|
||||
let _ = self.updateStatus()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateNetworkStatusNode(networkState: AccountNetworkState, layout: ContainerViewLayout?) {
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
|
||||
public var networkState: AccountNetworkState = .online(proxy: nil) {
|
||||
didSet {
|
||||
if self.networkState != oldValue {
|
||||
updateNetworkStatusNode(networkState: self.networkState, layout: self.layout)
|
||||
let _ = self.updateStatus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var layout: ContainerViewLayout? {
|
||||
didSet {
|
||||
if self.layout != oldValue {
|
||||
updateNetworkStatusNode(networkState: self.networkState, layout: self.layout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var pressed: (() -> Void)?
|
||||
public var longPressed: (() -> Void)?
|
||||
|
||||
public var titleContent: ChatTitleContent? {
|
||||
didSet {
|
||||
if let titleContent = self.titleContent {
|
||||
let titleTheme = self.hasEmbeddedTitleContent ? defaultDarkPresentationTheme : self.theme
|
||||
|
||||
var segments: [AnimatedCountLabelNode.Segment] = []
|
||||
var titleLeftIcon: ChatTitleIcon = .none
|
||||
var titleRightIcon: ChatTitleIcon = .none
|
||||
var titleCredibilityIcon: ChatTitleCredibilityIcon = .none
|
||||
var isEnabled = true
|
||||
switch titleContent {
|
||||
case let .peer(peerView, customTitle, _, isScheduledMessages):
|
||||
if peerView.peerId.isReplies {
|
||||
let typeText: String = self.strings.DialogList_Replies
|
||||
segments = [.text(0, NSAttributedString(string: typeText, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
|
||||
isEnabled = false
|
||||
} else if isScheduledMessages {
|
||||
if peerView.peerId == self.context.account.peerId {
|
||||
segments = [.text(0, NSAttributedString(string: self.strings.ScheduledMessages_RemindersTitle, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
|
||||
} else {
|
||||
segments = [.text(0, NSAttributedString(string: self.strings.ScheduledMessages_Title, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
|
||||
}
|
||||
isEnabled = false
|
||||
} else {
|
||||
if let peer = peerViewMainPeer(peerView) {
|
||||
if let customTitle = customTitle {
|
||||
segments = [.text(0, NSAttributedString(string: customTitle, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
|
||||
} else if peerView.peerId == self.context.account.peerId {
|
||||
segments = [.text(0, NSAttributedString(string: self.strings.Conversation_SavedMessages, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
|
||||
} else {
|
||||
if !peerView.peerIsContact, let user = peer as? TelegramUser, !user.flags.contains(.isSupport), user.botInfo == nil, let phone = user.phone, !phone.isEmpty {
|
||||
segments = [.text(0, NSAttributedString(string: formatPhoneNumber(phone), font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
|
||||
} else {
|
||||
segments = [.text(0, NSAttributedString(string: EnginePeer(peer).displayTitle(strings: self.strings, displayOrder: self.nameDisplayOrder), font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
|
||||
}
|
||||
}
|
||||
if peer.id != self.context.account.peerId {
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||
if peer.isFake {
|
||||
titleCredibilityIcon = .fake
|
||||
} else if peer.isScam {
|
||||
titleCredibilityIcon = .scam
|
||||
} else if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled {
|
||||
titleCredibilityIcon = .emojiStatus(emojiStatus)
|
||||
} else if peer.isVerified {
|
||||
titleCredibilityIcon = .verified
|
||||
} else if peer.isPremium && !premiumConfiguration.isPremiumDisabled {
|
||||
titleCredibilityIcon = .premium
|
||||
}
|
||||
}
|
||||
}
|
||||
if peerView.peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
titleLeftIcon = .lock
|
||||
}
|
||||
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings {
|
||||
if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
|
||||
if titleCredibilityIcon != .verified {
|
||||
titleRightIcon = .mute
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .replyThread(type, count):
|
||||
let textFont = titleFont
|
||||
let textColor = titleTheme.rootController.navigationBar.primaryTextColor
|
||||
|
||||
if count > 0 {
|
||||
var commentsPart: String
|
||||
switch type {
|
||||
case .comments:
|
||||
commentsPart = self.strings.Conversation_TitleComments(Int32(count))
|
||||
case .replies:
|
||||
commentsPart = self.strings.Conversation_TitleReplies(Int32(count))
|
||||
}
|
||||
|
||||
if commentsPart.contains("[") && commentsPart.contains("]") {
|
||||
if let startIndex = commentsPart.firstIndex(of: "["), let endIndex = commentsPart.firstIndex(of: "]") {
|
||||
commentsPart.removeSubrange(startIndex ... endIndex)
|
||||
}
|
||||
} else {
|
||||
commentsPart = commentsPart.trimmingCharacters(in: CharacterSet(charactersIn: "0123456789-,."))
|
||||
}
|
||||
|
||||
let rawTextAndRanges: PresentationStrings.FormattedString
|
||||
switch type {
|
||||
case .comments:
|
||||
rawTextAndRanges = self.strings.Conversation_TitleCommentsFormat("\(count)", commentsPart)
|
||||
case .replies:
|
||||
rawTextAndRanges = self.strings.Conversation_TitleRepliesFormat("\(count)", commentsPart)
|
||||
}
|
||||
|
||||
let rawText = rawTextAndRanges.string
|
||||
|
||||
var textIndex = 0
|
||||
var latestIndex = 0
|
||||
for indexAndRange in rawTextAndRanges.ranges {
|
||||
let index = indexAndRange.index
|
||||
let range = indexAndRange.range
|
||||
|
||||
var lowerSegmentIndex = range.lowerBound
|
||||
if index != 0 {
|
||||
lowerSegmentIndex = min(lowerSegmentIndex, latestIndex)
|
||||
} else {
|
||||
if latestIndex < range.lowerBound {
|
||||
let part = String(rawText[rawText.index(rawText.startIndex, offsetBy: latestIndex) ..< rawText.index(rawText.startIndex, offsetBy: range.lowerBound)])
|
||||
segments.append(.text(textIndex, NSAttributedString(string: part, font: textFont, textColor: textColor)))
|
||||
textIndex += 1
|
||||
}
|
||||
}
|
||||
latestIndex = range.upperBound
|
||||
|
||||
let part = String(rawText[rawText.index(rawText.startIndex, offsetBy: lowerSegmentIndex) ..< rawText.index(rawText.startIndex, offsetBy: min(rawText.count, range.upperBound))])
|
||||
if index == 0 {
|
||||
segments.append(.number(count, NSAttributedString(string: part, font: textFont, textColor: textColor)))
|
||||
} else {
|
||||
segments.append(.text(textIndex, NSAttributedString(string: part, font: textFont, textColor: textColor)))
|
||||
textIndex += 1
|
||||
}
|
||||
}
|
||||
if latestIndex < rawText.count {
|
||||
let part = String(rawText[rawText.index(rawText.startIndex, offsetBy: latestIndex)...])
|
||||
segments.append(.text(textIndex, NSAttributedString(string: part, font: textFont, textColor: textColor)))
|
||||
textIndex += 1
|
||||
}
|
||||
} else {
|
||||
switch type {
|
||||
case .comments:
|
||||
segments = [.text(0, NSAttributedString(string: strings.Conversation_TitleCommentsEmpty, font: textFont, textColor: textColor))]
|
||||
case .replies:
|
||||
segments = [.text(0, NSAttributedString(string: strings.Conversation_TitleRepliesEmpty, font: textFont, textColor: textColor))]
|
||||
}
|
||||
}
|
||||
|
||||
isEnabled = false
|
||||
case let .custom(text, _, enabled):
|
||||
segments = [.text(0, NSAttributedString(string: text, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
|
||||
isEnabled = enabled
|
||||
}
|
||||
|
||||
var updated = false
|
||||
|
||||
if self.titleTextNode.segments != segments {
|
||||
self.titleTextNode.segments = segments
|
||||
updated = true
|
||||
}
|
||||
|
||||
if titleLeftIcon != self.titleLeftIcon {
|
||||
self.titleLeftIcon = titleLeftIcon
|
||||
switch titleLeftIcon {
|
||||
case .lock:
|
||||
self.titleLeftIconNode.image = PresentationResourcesChat.chatTitleLockIcon(titleTheme)
|
||||
default:
|
||||
self.titleLeftIconNode.image = nil
|
||||
}
|
||||
updated = true
|
||||
}
|
||||
|
||||
if titleCredibilityIcon != self.titleCredibilityIcon {
|
||||
self.titleCredibilityIcon = titleCredibilityIcon
|
||||
/*switch titleCredibilityIcon {
|
||||
case .none:
|
||||
self.titleCredibilityIconNode.image = nil
|
||||
case .fake:
|
||||
self.titleCredibilityIconNode.image = PresentationResourcesChatList.fakeIcon(titleTheme, strings: self.strings, type: .regular)
|
||||
case .scam:
|
||||
self.titleCredibilityIconNode.image = PresentationResourcesChatList.scamIcon(titleTheme, strings: self.strings, type: .regular)
|
||||
case .verified:
|
||||
self.titleCredibilityIconNode.image = PresentationResourcesChatList.verifiedIcon(titleTheme)
|
||||
case .premium:
|
||||
self.titleCredibilityIconNode.image = PresentationResourcesChatList.premiumIcon(titleTheme)
|
||||
}*/
|
||||
updated = true
|
||||
}
|
||||
|
||||
if titleRightIcon != self.titleRightIcon {
|
||||
self.titleRightIcon = titleRightIcon
|
||||
switch titleRightIcon {
|
||||
case .mute:
|
||||
self.titleRightIconNode.image = PresentationResourcesChat.chatTitleMuteIcon(titleTheme)
|
||||
default:
|
||||
self.titleRightIconNode.image = nil
|
||||
}
|
||||
updated = true
|
||||
}
|
||||
self.isUserInteractionEnabled = isEnabled
|
||||
self.button.isUserInteractionEnabled = isEnabled
|
||||
if !self.updateStatus() {
|
||||
if updated {
|
||||
if let (size, clearBounds) = self.validLayout {
|
||||
self.updateLayout(size: size, clearBounds: clearBounds, transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateStatus() -> Bool {
|
||||
var inputActivitiesAllowed = true
|
||||
if let titleContent = self.titleContent {
|
||||
switch titleContent {
|
||||
case let .peer(peerView, _, _, isScheduledMessages):
|
||||
if let peer = peerViewMainPeer(peerView) {
|
||||
if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies {
|
||||
inputActivitiesAllowed = false
|
||||
}
|
||||
}
|
||||
case .replyThread:
|
||||
inputActivitiesAllowed = true
|
||||
default:
|
||||
inputActivitiesAllowed = false
|
||||
}
|
||||
}
|
||||
|
||||
let titleTheme = self.hasEmbeddedTitleContent ? defaultDarkPresentationTheme : self.theme
|
||||
|
||||
var state = ChatTitleActivityNodeState.none
|
||||
switch self.networkState {
|
||||
case .waitingForNetwork, .connecting, .updating:
|
||||
var infoText: String
|
||||
switch self.networkState {
|
||||
case .waitingForNetwork:
|
||||
infoText = self.strings.ChatState_WaitingForNetwork
|
||||
case .connecting:
|
||||
infoText = self.strings.ChatState_Connecting
|
||||
case .updating:
|
||||
infoText = self.strings.ChatState_Updating
|
||||
case .online:
|
||||
infoText = ""
|
||||
}
|
||||
state = .info(NSAttributedString(string: infoText, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor), .generic)
|
||||
case .online:
|
||||
if let (peerId, inputActivities) = self.inputActivities, !inputActivities.isEmpty, inputActivitiesAllowed {
|
||||
var stringValue = ""
|
||||
var mergedActivity = inputActivities[0].1
|
||||
for (_, activity) in inputActivities {
|
||||
if activity != mergedActivity {
|
||||
mergedActivity = .typingText
|
||||
break
|
||||
}
|
||||
}
|
||||
if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
switch mergedActivity {
|
||||
case .typingText:
|
||||
stringValue = strings.Conversation_typing
|
||||
case .uploadingFile:
|
||||
stringValue = strings.Activity_UploadingDocument
|
||||
case .recordingVoice:
|
||||
stringValue = strings.Activity_RecordingAudio
|
||||
case .uploadingPhoto:
|
||||
stringValue = strings.Activity_UploadingPhoto
|
||||
case .uploadingVideo:
|
||||
stringValue = strings.Activity_UploadingVideo
|
||||
case .playingGame:
|
||||
stringValue = strings.Activity_PlayingGame
|
||||
case .recordingInstantVideo:
|
||||
stringValue = strings.Activity_RecordingVideoMessage
|
||||
case .uploadingInstantVideo:
|
||||
stringValue = strings.Activity_UploadingVideoMessage
|
||||
case .choosingSticker:
|
||||
stringValue = strings.Activity_ChoosingSticker
|
||||
case let .seeingEmojiInteraction(emoticon):
|
||||
stringValue = strings.Activity_EnjoyingAnimations(emoticon).string
|
||||
case .speakingInGroupCall, .interactingWithEmoji:
|
||||
stringValue = ""
|
||||
}
|
||||
} else {
|
||||
if inputActivities.count > 1 {
|
||||
let peerTitle = EnginePeer(inputActivities[0].0).compactDisplayTitle
|
||||
if inputActivities.count == 2 {
|
||||
let secondPeerTitle = EnginePeer(inputActivities[1].0).compactDisplayTitle
|
||||
stringValue = strings.Chat_MultipleTypingPair(peerTitle, secondPeerTitle).string
|
||||
} else {
|
||||
stringValue = strings.Chat_MultipleTypingMore(peerTitle, String(inputActivities.count - 1)).string
|
||||
}
|
||||
} else if let (peer, _) = inputActivities.first {
|
||||
stringValue = EnginePeer(peer).compactDisplayTitle
|
||||
}
|
||||
}
|
||||
let color = titleTheme.rootController.navigationBar.accentTextColor
|
||||
let string = NSAttributedString(string: stringValue, font: subtitleFont, textColor: color)
|
||||
switch mergedActivity {
|
||||
case .typingText:
|
||||
state = .typingText(string, color)
|
||||
case .recordingVoice:
|
||||
state = .recordingVoice(string, color)
|
||||
case .recordingInstantVideo:
|
||||
state = .recordingVideo(string, color)
|
||||
case .uploadingFile, .uploadingInstantVideo, .uploadingPhoto, .uploadingVideo:
|
||||
state = .uploading(string, color)
|
||||
case .playingGame:
|
||||
state = .playingGame(string, color)
|
||||
case .speakingInGroupCall, .interactingWithEmoji:
|
||||
state = .typingText(string, color)
|
||||
case .choosingSticker:
|
||||
state = .choosingSticker(string, color)
|
||||
case .seeingEmojiInteraction:
|
||||
state = .choosingSticker(string, color)
|
||||
}
|
||||
} else {
|
||||
if let titleContent = self.titleContent {
|
||||
switch titleContent {
|
||||
case let .peer(peerView, _, onlineMemberCount, isScheduledMessages):
|
||||
if let peer = peerViewMainPeer(peerView) {
|
||||
let servicePeer = isServicePeer(peer)
|
||||
if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies {
|
||||
let string = NSAttributedString(string: "", font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
} else if let user = peer as? TelegramUser {
|
||||
if user.isDeleted {
|
||||
state = .none
|
||||
} else if servicePeer {
|
||||
let string = NSAttributedString(string: "", font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
} else if user.flags.contains(.isSupport) {
|
||||
let statusText = self.strings.Bot_GenericSupportStatus
|
||||
|
||||
let string = NSAttributedString(string: statusText, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
} else if let _ = user.botInfo {
|
||||
let statusText = self.strings.Bot_GenericBotStatus
|
||||
|
||||
let string = NSAttributedString(string: statusText, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
} else if let peer = peerViewMainPeer(peerView) {
|
||||
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
|
||||
let userPresence: TelegramUserPresence
|
||||
if let presence = peerView.peerPresences[peer.id] as? TelegramUserPresence {
|
||||
userPresence = presence
|
||||
self.presenceManager?.reset(presence: EnginePeer.Presence(presence))
|
||||
} else {
|
||||
userPresence = TelegramUserPresence(status: .none, lastActivity: 0)
|
||||
}
|
||||
let (string, activity) = stringAndActivityForUserPresence(strings: self.strings, dateTimeFormat: self.dateTimeFormat, presence: EnginePeer.Presence(userPresence), relativeTo: Int32(timestamp))
|
||||
let attributedString = NSAttributedString(string: string, font: subtitleFont, textColor: activity ? titleTheme.rootController.navigationBar.accentTextColor : titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(attributedString, activity ? .online : .lastSeenTime)
|
||||
} else {
|
||||
let string = NSAttributedString(string: "", font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
}
|
||||
} else if let group = peer as? TelegramGroup {
|
||||
var onlineCount = 0
|
||||
if let cachedGroupData = peerView.cachedData as? CachedGroupData, let participants = cachedGroupData.participants {
|
||||
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
|
||||
for participant in participants.participants {
|
||||
if let presence = peerView.peerPresences[participant.peerId] as? TelegramUserPresence {
|
||||
let relativeStatus = relativeUserPresenceStatus(EnginePeer.Presence(presence), relativeTo: Int32(timestamp))
|
||||
switch relativeStatus {
|
||||
case .online:
|
||||
onlineCount += 1
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if onlineCount > 1 {
|
||||
let string = NSMutableAttributedString()
|
||||
|
||||
string.append(NSAttributedString(string: "\(strings.Conversation_StatusMembers(Int32(group.participantCount))), ", font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor))
|
||||
string.append(NSAttributedString(string: strings.Conversation_StatusOnline(Int32(onlineCount)), font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor))
|
||||
state = .info(string, .generic)
|
||||
} else {
|
||||
let string = NSAttributedString(string: strings.Conversation_StatusMembers(Int32(group.participantCount)), font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
}
|
||||
} else if let channel = peer as? TelegramChannel {
|
||||
if let cachedChannelData = peerView.cachedData as? CachedChannelData, let memberCount = cachedChannelData.participantsSummary.memberCount {
|
||||
if memberCount == 0 {
|
||||
let string: NSAttributedString
|
||||
if case .group = channel.info {
|
||||
string = NSAttributedString(string: strings.Group_Status, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
} else {
|
||||
string = NSAttributedString(string: strings.Channel_Status, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
}
|
||||
state = .info(string, .generic)
|
||||
} else {
|
||||
if case .group = channel.info, let onlineMemberCount = onlineMemberCount, onlineMemberCount > 1 {
|
||||
let string = NSMutableAttributedString()
|
||||
|
||||
string.append(NSAttributedString(string: "\(strings.Conversation_StatusMembers(Int32(memberCount))), ", font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor))
|
||||
string.append(NSAttributedString(string: strings.Conversation_StatusOnline(Int32(onlineMemberCount)), font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor))
|
||||
state = .info(string, .generic)
|
||||
} else {
|
||||
let membersString: String
|
||||
if case .group = channel.info {
|
||||
membersString = strings.Conversation_StatusMembers(memberCount)
|
||||
} else {
|
||||
membersString = strings.Conversation_StatusSubscribers(memberCount)
|
||||
}
|
||||
let string = NSAttributedString(string: membersString, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch channel.info {
|
||||
case .group:
|
||||
let string = NSAttributedString(string: strings.Group_Status, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
case .broadcast:
|
||||
let string = NSAttributedString(string: strings.Channel_Status, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .custom(_, subtitle?, _):
|
||||
let string = NSAttributedString(string: subtitle, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
var accessibilityText = ""
|
||||
for segment in self.titleTextNode.segments {
|
||||
switch segment {
|
||||
case let .number(_, string):
|
||||
accessibilityText.append(string.string)
|
||||
case let .text(_, string):
|
||||
accessibilityText.append(string.string)
|
||||
}
|
||||
}
|
||||
|
||||
self.accessibilityLabel = accessibilityText
|
||||
self.accessibilityValue = state.string
|
||||
} else {
|
||||
self.accessibilityLabel = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.activityNode.transitionToState(state, animation: .slide) {
|
||||
if let (size, clearBounds) = self.validLayout {
|
||||
self.updateLayout(size: size, clearBounds: clearBounds, transition: .animated(duration: 0.3, curve: .spring))
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
self.animationCache = animationCache
|
||||
self.animationRenderer = animationRenderer
|
||||
|
||||
self.contentContainer = ASDisplayNode()
|
||||
|
||||
self.titleContainerView = PortalSourceView()
|
||||
self.titleTextNode = ImmediateAnimatedCountLabelNode()
|
||||
|
||||
self.titleLeftIconNode = ASImageNode()
|
||||
self.titleLeftIconNode.isLayerBacked = true
|
||||
self.titleLeftIconNode.displayWithoutProcessing = true
|
||||
self.titleLeftIconNode.displaysAsynchronously = false
|
||||
|
||||
self.titleRightIconNode = ASImageNode()
|
||||
self.titleRightIconNode.isLayerBacked = true
|
||||
self.titleRightIconNode.displayWithoutProcessing = true
|
||||
self.titleRightIconNode.displaysAsynchronously = false
|
||||
|
||||
self.titleCredibilityIconView = ComponentHostView()
|
||||
self.titleCredibilityIconView.isUserInteractionEnabled = false
|
||||
|
||||
self.activityNode = ChatTitleActivityNode()
|
||||
self.button = HighlightTrackingButtonNode()
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.isAccessibilityElement = true
|
||||
self.accessibilityTraits = .header
|
||||
|
||||
self.addSubnode(self.contentContainer)
|
||||
self.titleContainerView.addSubnode(self.titleTextNode)
|
||||
self.contentContainer.view.addSubview(self.titleContainerView)
|
||||
self.contentContainer.addSubnode(self.activityNode)
|
||||
self.addSubnode(self.button)
|
||||
|
||||
self.presenceManager = PeerPresenceStatusManager(update: { [weak self] in
|
||||
let _ = self?.updateStatus()
|
||||
})
|
||||
|
||||
self.button.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: [.touchUpInside])
|
||||
self.button.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.titleTextNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.activityNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.titleCredibilityIconView.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.titleTextNode.alpha = 0.4
|
||||
strongSelf.activityNode.alpha = 0.4
|
||||
strongSelf.titleCredibilityIconView.alpha = 0.4
|
||||
} else {
|
||||
strongSelf.titleTextNode.alpha = 1.0
|
||||
strongSelf.activityNode.alpha = 1.0
|
||||
strongSelf.titleCredibilityIconView.alpha = 1.0
|
||||
strongSelf.titleTextNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
strongSelf.activityNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.button.view.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(self.longPressGesture(_:))))
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override public func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
if let (size, clearBounds) = self.validLayout {
|
||||
self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
public func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, hasEmbeddedTitleContent: Bool) {
|
||||
self.theme = theme
|
||||
self.hasEmbeddedTitleContent = hasEmbeddedTitleContent
|
||||
self.strings = strings
|
||||
|
||||
let titleContent = self.titleContent
|
||||
self.titleCredibilityIcon = .none
|
||||
self.titleContent = titleContent
|
||||
let _ = self.updateStatus()
|
||||
|
||||
if let (size, clearBounds) = self.validLayout {
|
||||
self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
public func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, clearBounds)
|
||||
|
||||
self.button.frame = clearBounds
|
||||
self.contentContainer.frame = clearBounds
|
||||
|
||||
var leftIconWidth: CGFloat = 0.0
|
||||
var rightIconWidth: CGFloat = 0.0
|
||||
var credibilityIconWidth: CGFloat = 0.0
|
||||
|
||||
if let image = self.titleLeftIconNode.image {
|
||||
if self.titleLeftIconNode.supernode == nil {
|
||||
self.titleTextNode.addSubnode(self.titleLeftIconNode)
|
||||
}
|
||||
leftIconWidth = image.size.width + 6.0
|
||||
} else if self.titleLeftIconNode.supernode != nil {
|
||||
self.titleLeftIconNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
let titleCredibilityContent: EmojiStatusComponent.Content
|
||||
switch self.titleCredibilityIcon {
|
||||
case .none:
|
||||
titleCredibilityContent = .none
|
||||
case .premium:
|
||||
titleCredibilityContent = .premium(color: self.theme.list.itemAccentColor)
|
||||
case .verified:
|
||||
titleCredibilityContent = .verified(fillColor: self.theme.list.itemCheckColors.fillColor, foregroundColor: self.theme.list.itemCheckColors.foregroundColor, sizeType: .large)
|
||||
case .fake:
|
||||
titleCredibilityContent = .text(color: self.theme.chat.message.incoming.scamColor, string: self.strings.Message_FakeAccount.uppercased())
|
||||
case .scam:
|
||||
titleCredibilityContent = .text(color: self.theme.chat.message.incoming.scamColor, string: self.strings.Message_ScamAccount.uppercased())
|
||||
case let .emojiStatus(emojiStatus):
|
||||
titleCredibilityContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: self.theme.list.mediaPlaceholderColor, themeColor: self.theme.list.itemAccentColor, loopMode: .count(2))
|
||||
}
|
||||
|
||||
let titleCredibilitySize = self.titleCredibilityIconView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(EmojiStatusComponent(
|
||||
context: self.context,
|
||||
animationCache: self.animationCache,
|
||||
animationRenderer: self.animationRenderer,
|
||||
content: titleCredibilityContent,
|
||||
isVisibleForAnimations: true,
|
||||
action: nil
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 20.0, height: 20.0)
|
||||
)
|
||||
|
||||
if self.titleCredibilityIcon != .none {
|
||||
self.titleTextNode.view.addSubview(self.titleCredibilityIconView)
|
||||
credibilityIconWidth = titleCredibilitySize.width + 3.0
|
||||
} else {
|
||||
if self.titleCredibilityIconView.superview != nil {
|
||||
self.titleCredibilityIconView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
if let image = self.titleRightIconNode.image {
|
||||
if self.titleRightIconNode.supernode == nil {
|
||||
self.titleTextNode.addSubnode(self.titleRightIconNode)
|
||||
}
|
||||
rightIconWidth = image.size.width + 3.0
|
||||
} else if self.titleRightIconNode.supernode != nil {
|
||||
self.titleRightIconNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
var titleTransition = transition
|
||||
if self.titleContainerView.bounds.width.isZero {
|
||||
titleTransition = .immediate
|
||||
}
|
||||
|
||||
let titleSideInset: CGFloat = 3.0
|
||||
var titleFrame: CGRect
|
||||
if size.height > 40.0 {
|
||||
var titleSize = self.titleTextNode.updateLayout(size: CGSize(width: clearBounds.width - leftIconWidth - credibilityIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height), animated: titleTransition.isAnimated)
|
||||
titleSize.width += credibilityIconWidth
|
||||
let activitySize = self.activityNode.updateLayout(clearBounds.size, alignment: .center)
|
||||
let titleInfoSpacing: CGFloat = 0.0
|
||||
|
||||
if activitySize.height.isZero {
|
||||
titleFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - titleSize.width) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
|
||||
if titleFrame.size.width < size.width {
|
||||
titleFrame.origin.x = -clearBounds.minX + floor((size.width - titleFrame.width) / 2.0)
|
||||
}
|
||||
titleTransition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame)
|
||||
titleTransition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size))
|
||||
} else {
|
||||
let combinedHeight = titleSize.height + activitySize.height + titleInfoSpacing
|
||||
|
||||
titleFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - titleSize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0)), size: titleSize)
|
||||
if titleFrame.size.width < size.width {
|
||||
titleFrame.origin.x = -clearBounds.minX + floor((size.width - titleFrame.width) / 2.0)
|
||||
}
|
||||
titleFrame.origin.x = max(titleFrame.origin.x, clearBounds.minX + leftIconWidth)
|
||||
titleTransition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame)
|
||||
titleTransition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size))
|
||||
|
||||
var activityFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - activitySize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0) + titleSize.height + titleInfoSpacing), size: activitySize)
|
||||
if activitySize.width < size.width {
|
||||
activityFrame.origin.x = -clearBounds.minX + floor((size.width - activityFrame.width) / 2.0)
|
||||
}
|
||||
self.activityNode.frame = activityFrame
|
||||
}
|
||||
|
||||
if let image = self.titleLeftIconNode.image {
|
||||
self.titleLeftIconNode.frame = CGRect(origin: CGPoint(x: -image.size.width - 3.0 - UIScreenPixel, y: 4.0), size: image.size)
|
||||
}
|
||||
|
||||
self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: titleFrame.width - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize)
|
||||
|
||||
if let image = self.titleRightIconNode.image {
|
||||
self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.width + 3.0 + UIScreenPixel, y: 6.0), size: image.size)
|
||||
}
|
||||
} else {
|
||||
let titleSize = self.titleTextNode.updateLayout(size: CGSize(width: floor(clearBounds.width / 2.0 - leftIconWidth - credibilityIconWidth - rightIconWidth - titleSideInset * 2.0), height: size.height), animated: titleTransition.isAnimated)
|
||||
let activitySize = self.activityNode.updateLayout(CGSize(width: floor(clearBounds.width / 2.0), height: size.height), alignment: .center)
|
||||
|
||||
let titleInfoSpacing: CGFloat = 8.0
|
||||
let combinedWidth = titleSize.width + leftIconWidth + credibilityIconWidth + rightIconWidth + activitySize.width + titleInfoSpacing
|
||||
|
||||
titleFrame = CGRect(origin: CGPoint(x: leftIconWidth + floor((clearBounds.width - combinedWidth) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
|
||||
|
||||
titleTransition.updateFrameAdditiveToCenter(view: self.titleContainerView, frame: titleFrame)
|
||||
titleTransition.updateFrameAdditiveToCenter(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size))
|
||||
|
||||
self.activityNode.frame = CGRect(origin: CGPoint(x: floor((clearBounds.width - combinedWidth) / 2.0 + titleSize.width + leftIconWidth + credibilityIconWidth + rightIconWidth + titleInfoSpacing), y: floor((size.height - activitySize.height) / 2.0)), size: activitySize)
|
||||
|
||||
if let image = self.titleLeftIconNode.image {
|
||||
self.titleLeftIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.minY + 4.0), size: image.size)
|
||||
}
|
||||
|
||||
self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: titleFrame.maxX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize)
|
||||
|
||||
if let image = self.titleRightIconNode.image {
|
||||
self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX - image.size.width, y: titleFrame.minY + 6.0), size: image.size)
|
||||
}
|
||||
}
|
||||
|
||||
self.pointerInteraction = PointerInteraction(view: self, style: .rectangle(CGSize(width: titleFrame.width + 16.0, height: 40.0)))
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
self.pressed?()
|
||||
}
|
||||
|
||||
@objc private func longPressGesture(_ gesture: UILongPressGestureRecognizer) {
|
||||
switch gesture.state {
|
||||
case .began:
|
||||
self.longPressed?()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func animateLayoutTransition() {
|
||||
UIView.transition(with: self, duration: 0.25, options: [.transitionCrossDissolve], animations: {
|
||||
}, completion: nil)
|
||||
}
|
||||
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if !self.isUserInteractionEnabled {
|
||||
return nil
|
||||
}
|
||||
if self.button.frame.contains(point) {
|
||||
return self.button.view
|
||||
}
|
||||
return super.hitTest(point, with: event)
|
||||
}
|
||||
|
||||
public final class SnapshotState {
|
||||
fileprivate let snapshotView: UIView
|
||||
|
||||
fileprivate init(snapshotView: UIView) {
|
||||
self.snapshotView = snapshotView
|
||||
}
|
||||
}
|
||||
|
||||
public func prepareSnapshotState() -> SnapshotState {
|
||||
let snapshotView = self.snapshotView(afterScreenUpdates: false)!
|
||||
return SnapshotState(
|
||||
snapshotView: snapshotView
|
||||
)
|
||||
}
|
||||
|
||||
public func animateFromSnapshot(_ snapshotState: SnapshotState) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
self.layer.animatePosition(from: CGPoint(x: 0.0, y: 20.0), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true, additive: true)
|
||||
|
||||
snapshotState.snapshotView.frame = self.frame
|
||||
self.superview?.insertSubview(snapshotState.snapshotView, belowSubview: self)
|
||||
|
||||
let snapshotView = snapshotState.snapshotView
|
||||
snapshotState.snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
||||
snapshotView?.removeFromSuperview()
|
||||
})
|
||||
snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -20.0), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
|
||||
}
|
||||
}
|
@ -48,7 +48,7 @@ public final class EmojiStatusComponent: Component {
|
||||
case verified(fillColor: UIColor, foregroundColor: UIColor, sizeType: SizeType)
|
||||
case text(color: UIColor, string: String)
|
||||
case animation(content: AnimationContent, size: CGSize, placeholderColor: UIColor, themeColor: UIColor?, loopMode: LoopMode)
|
||||
case topic(title: String)
|
||||
case topic(title: String, colorIndex: Int)
|
||||
}
|
||||
|
||||
public let context: AccountContext
|
||||
@ -223,7 +223,7 @@ public final class EmojiStatusComponent: Component {
|
||||
} else {
|
||||
iconImage = nil
|
||||
}
|
||||
case let .topic(title):
|
||||
case let .topic(title, colorIndex):
|
||||
func generateTopicIcon(backgroundColors: [UIColor], strokeColors: [UIColor]) -> UIImage? {
|
||||
return generateImage(CGSize(width: 44.0, height: 44.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: .zero, size: size))
|
||||
@ -279,7 +279,18 @@ public final class EmojiStatusComponent: Component {
|
||||
})
|
||||
}
|
||||
|
||||
if let image = generateTopicIcon(backgroundColors: [UIColor(rgb: 0x6FB9F0), UIColor(rgb: 0x0261E4)], strokeColors: [UIColor(rgb: 0x026CB5), UIColor(rgb: 0x064BB7)]) {
|
||||
let topicColors: [([UInt32], [UInt32])] = [
|
||||
([0x6FB9F0, 0x0261E4], [0x026CB5, 0x064BB7]),
|
||||
([0x6FB9F0, 0x0261E4], [0x026CB5, 0x064BB7]),
|
||||
([0xFFD67E, 0xFC8601], [0xDA9400, 0xFA5F00]),
|
||||
([0xCB86DB, 0x9338AF], [0x812E98, 0x6F2B87]),
|
||||
([0x8EEE98, 0x02B504], [0x02A01B, 0x009716]),
|
||||
([0xFF93B2, 0xE23264], [0xFC447A, 0xC80C46]),
|
||||
([0xFB6F5F, 0xD72615], [0xDC1908, 0xB61506])
|
||||
]
|
||||
let clippedIndex = colorIndex % topicColors.count
|
||||
|
||||
if let image = generateTopicIcon(backgroundColors: topicColors[clippedIndex].0.map(UIColor.init(rgb:)), strokeColors: topicColors[clippedIndex].1.map(UIColor.init(rgb:))) {
|
||||
iconImage = image
|
||||
} else {
|
||||
iconImage = nil
|
||||
|
@ -106,7 +106,7 @@ private final class TitleFieldComponent: Component {
|
||||
|
||||
let titleCredibilityContent: EmojiStatusComponent.Content
|
||||
if component.fileId == 0 {
|
||||
titleCredibilityContent = .topic(title: String(component.text.prefix(1)))
|
||||
titleCredibilityContent = .topic(title: String(component.text.prefix(1)), colorIndex: 0)
|
||||
} else {
|
||||
titleCredibilityContent = .animation(content: .customEmoji(fileId: component.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: component.placeholderColor, themeColor: component.accentColor, loopMode: .count(2))
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ import UniversalMediaPlayer
|
||||
import GalleryUI
|
||||
import HierarchyTrackingLayer
|
||||
import AccountContext
|
||||
import ComponentFlow
|
||||
import EmojiStatusComponent
|
||||
|
||||
private let normalFont = avatarPlaceholderFont(size: 16.0)
|
||||
private let smallFont = avatarPlaceholderFont(size: 12.0)
|
||||
@ -26,6 +28,8 @@ final class ChatAvatarNavigationNode: ASDisplayNode {
|
||||
let avatarNode: AvatarNode
|
||||
private var videoNode: UniversalVideoNode?
|
||||
|
||||
private let statusView: ComponentView<Empty>
|
||||
|
||||
private var videoContent: NativeVideoContent?
|
||||
private let playbackStartDisposable = MetaDisposable()
|
||||
private var cachedDataDisposable = MetaDisposable()
|
||||
@ -54,6 +58,7 @@ final class ChatAvatarNavigationNode: ASDisplayNode {
|
||||
override init() {
|
||||
self.containerNode = ContextControllerSourceNode()
|
||||
self.avatarNode = AvatarNode(font: normalFont)
|
||||
self.statusView = ComponentView()
|
||||
|
||||
super.init()
|
||||
|
||||
@ -81,6 +86,31 @@ final class ChatAvatarNavigationNode: ASDisplayNode {
|
||||
self.view.isOpaque = false
|
||||
}
|
||||
|
||||
public func setStatus(context: AccountContext, content: EmojiStatusComponent.Content) {
|
||||
let statusSize = self.statusView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(EmojiStatusComponent(
|
||||
context: context,
|
||||
animationCache: context.animationCache,
|
||||
animationRenderer: context.animationRenderer,
|
||||
content: content,
|
||||
isVisibleForAnimations: true,
|
||||
action: nil
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 32.0, height: 32.0)
|
||||
)
|
||||
if let statusComponentView = self.statusView.view {
|
||||
if statusComponentView.superview == nil {
|
||||
self.containerNode.view.addSubview(statusComponentView)
|
||||
}
|
||||
|
||||
statusComponentView.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - statusSize.width) / 2.0), y: floor((self.containerNode.bounds.height - statusSize.height) / 2.0)), size: statusSize)
|
||||
}
|
||||
|
||||
self.avatarNode.isHidden = true
|
||||
}
|
||||
|
||||
public func setPeer(context: AccountContext, theme: PresentationTheme, peer: EnginePeer?, authorOfMessage: MessageReference? = nil, overrideImage: AvatarNodeImageOverride? = nil, emptyColor: UIColor? = nil, clipStyle: AvatarNodeClipStyle = .round, synchronousLoad: Bool = false, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), storeUnrounded: Bool = false) {
|
||||
self.context = context
|
||||
self.avatarNode.setPeer(context: context, theme: theme, peer: peer, authorOfMessage: authorOfMessage, overrideImage: overrideImage, emptyColor: emptyColor, clipStyle: clipStyle, synchronousLoad: synchronousLoad, displayDimensions: displayDimensions, storeUnrounded: storeUnrounded)
|
||||
|
@ -453,7 +453,14 @@ final class ChatBotInfoItemNode: ListViewItemNode {
|
||||
case let .url(url, concealed):
|
||||
self.item?.controllerInteraction.openUrl(url, concealed, nil, nil)
|
||||
case let .peerMention(peerId, _):
|
||||
self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil, peekData: nil), nil, false, nil)
|
||||
if let item = self.item {
|
||||
let _ = (item.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
if let peer = peer {
|
||||
self?.item?.controllerInteraction.openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: nil), nil, false)
|
||||
}
|
||||
})
|
||||
}
|
||||
case let .textMention(name):
|
||||
self.item?.controllerInteraction.openPeerMention(name)
|
||||
case let .botCommand(command):
|
||||
|
@ -242,12 +242,14 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
|
||||
botPeer = message.author
|
||||
}
|
||||
|
||||
var peerId: PeerId?
|
||||
var peer: Peer?
|
||||
if samePeer {
|
||||
peerId = message.id.peerId
|
||||
peer = message.peers[message.id.peerId]
|
||||
} else {
|
||||
peer = botPeer
|
||||
}
|
||||
if let botPeer = botPeer, let addressName = botPeer.addressName {
|
||||
self.controllerInteraction.openPeer(peerId, .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: "@\(addressName) \(query)")), subject: nil, peekData: nil), nil, false, nil)
|
||||
if let peer = peer, let botPeer = botPeer, let addressName = botPeer.addressName {
|
||||
self.controllerInteraction.openPeer(EnginePeer(peer), .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: "@\(addressName) \(query)")), subject: nil, peekData: nil), nil, false)
|
||||
}
|
||||
}
|
||||
case .payment:
|
||||
@ -259,7 +261,13 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
|
||||
case let .setupPoll(isQuiz):
|
||||
self.controllerInteraction.openPollCreation(isQuiz)
|
||||
case let .openUserProfile(peerId):
|
||||
self.controllerInteraction.openPeer(peerId, .info, nil, false, nil)
|
||||
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let self, let peer else {
|
||||
return
|
||||
}
|
||||
self.controllerInteraction.openPeer(peer, .info, nil, false)
|
||||
})
|
||||
case let .openWebView(url, simple):
|
||||
self.controllerInteraction.openWebView(markupButton.title, url, simple, false)
|
||||
}
|
||||
|
@ -81,6 +81,8 @@ import ImageTransparency
|
||||
import StickerPackPreviewUI
|
||||
import TextNodeWithEntities
|
||||
import EntityKeyboard
|
||||
import ChatTitleView
|
||||
import EmojiStatusComponent
|
||||
|
||||
#if DEBUG
|
||||
import os.signpost
|
||||
@ -258,6 +260,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
private var rightNavigationButton: ChatNavigationButton?
|
||||
private var chatInfoNavigationButton: ChatNavigationButton?
|
||||
|
||||
private var moreBarButton: MoreHeaderButton
|
||||
private var moreInfoNavigationButton: ChatNavigationButton?
|
||||
|
||||
private var peerView: PeerView?
|
||||
|
||||
private var historyStateDisposable: Disposable?
|
||||
@ -590,6 +595,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
default:
|
||||
navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, hideBackground: self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding ? true : false, hideBadge: false)
|
||||
}
|
||||
|
||||
self.moreBarButton = MoreHeaderButton(color: self.presentationData.theme.rootController.navigationBar.buttonColor)
|
||||
self.moreBarButton.isUserInteractionEnabled = true
|
||||
|
||||
super.init(context: context, navigationBarPresentationData: navigationBarPresentationData, mediaAccessoryPanelVisibility: mediaAccessoryPanelVisibility, locationBroadcastPanelSource: locationBroadcastPanelSource, groupCallPanelSource: groupCallPanelSource)
|
||||
|
||||
self.automaticallyControlPresentationContextLayout = false
|
||||
@ -831,7 +840,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}, openUrl: { url in
|
||||
self?.openUrl(url, concealed: false, skipConcealedAlert: isLocation, message: nil)
|
||||
}, openPeer: { peer, navigation in
|
||||
self?.openPeer(peerId: peer.id, navigation: navigation, fromMessage: nil)
|
||||
self?.openPeer(peer: EnginePeer(peer), navigation: navigation, fromMessage: nil)
|
||||
}, callPeer: { peerId, isVideo in
|
||||
self?.controllerInteraction?.callPeer(peerId, isVideo)
|
||||
}, enqueueMessage: { message in
|
||||
@ -892,9 +901,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerInteraction?.openPeerMention(mention)
|
||||
}
|
||||
}, openPeer: { [weak self] peerId in
|
||||
}, openPeer: { [weak self] peer in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerInteraction?.openPeer(peerId, .default, nil, false, nil)
|
||||
strongSelf.controllerInteraction?.openPeer(peer, .default, nil, false)
|
||||
}
|
||||
}, openHashtag: { [weak self] peerName, hashtag in
|
||||
if let strongSelf = self {
|
||||
@ -963,8 +972,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
})
|
||||
})))
|
||||
}, openPeer: { [weak self] id, navigation, fromMessage, isReaction, _ in
|
||||
self?.openPeer(peerId: id, navigation: navigation, fromMessage: fromMessage, fromReactionMessageId: isReaction ? fromMessage?.id : nil)
|
||||
}, openPeer: { [weak self] peer, navigation, fromMessage, isReaction in
|
||||
self?.openPeer(peer: peer, navigation: navigation, fromMessage: fromMessage, fromReactionMessageId: isReaction ? fromMessage?.id : nil)
|
||||
}, openPeerMention: { [weak self] name in
|
||||
self?.openPeerMention(name)
|
||||
}, openMessageContextMenu: { [weak self] message, selectAll, node, frame, anyRecognizer, location in
|
||||
@ -1406,13 +1415,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
availableReactions: availableReactions,
|
||||
animationCache: strongSelf.controllerInteraction!.presentationContext.animationCache,
|
||||
animationRenderer: strongSelf.controllerInteraction!.presentationContext.animationRenderer,
|
||||
message: EngineMessage(message), reaction: value, readStats: nil, back: nil, openPeer: { id in
|
||||
message: EngineMessage(message), reaction: value, readStats: nil, back: nil, openPeer: { peer in
|
||||
dismissController?({
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.openPeer(peerId: id, navigation: .default, fromMessage: MessageReference(message), fromReactionMessageId: message.id)
|
||||
strongSelf.openPeer(peer: peer, navigation: .default, fromMessage: MessageReference(message), fromReactionMessageId: message.id)
|
||||
})
|
||||
})))
|
||||
|
||||
@ -2318,9 +2327,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
if let botStart = strongSelf.botStart, case let .automatic(returnToPeerId, scheduled) = botStart.behavior {
|
||||
strongSelf.openPeer(peerId: returnToPeerId, navigation: .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: inputString)), subject: scheduled ? .scheduledMessages : nil, peekData: nil), fromMessage: nil)
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: returnToPeerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
strongSelf.openPeer(peer: peer, navigation: .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: inputString)), subject: scheduled ? .scheduledMessages : nil, peekData: nil), fromMessage: nil)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
strongSelf.openPeer(peerId: peerId, navigation: .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: inputString)), subject: nil, peekData: nil), fromMessage: nil)
|
||||
if let peerId = peerId {
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
strongSelf.openPeer(peer: peer, navigation: .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: inputString)), subject: nil, peekData: nil), fromMessage: nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}, openUrl: { [weak self] url, concealed, _, message in
|
||||
if let strongSelf = self {
|
||||
@ -2661,7 +2682,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_LinkDialogOpen, color: .accent, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
if let strongSelf = self {
|
||||
strongSelf.openPeer(peerId: peerId, navigation: .chat(textInputState: nil, subject: nil, peekData: nil), fromMessage: nil)
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
strongSelf.openPeer(peer: peer, navigation: .chat(textInputState: nil, subject: nil, peekData: nil), fromMessage: nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}))
|
||||
if !mention.isEmpty {
|
||||
@ -3578,14 +3604,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/User"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
self?.openPeer(peerId: peer.id, navigation: .info, fromMessage: nil)
|
||||
self?.openPeer(peer: peer, navigation: .info, fromMessage: nil)
|
||||
}))
|
||||
]
|
||||
items.append(.action(ContextMenuActionItem(text: isChannel ? strongSelf.presentationData.strings.Conversation_ContextMenuOpenChannel : strongSelf.presentationData.strings.Conversation_ContextMenuSendMessage, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: isChannel ? "Chat/Context Menu/Channels" : "Chat/Context Menu/Message"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
self?.openPeer(peerId: peer.id, navigation: .chat(textInputState: nil, subject: nil, peekData: nil), fromMessage: nil)
|
||||
self?.openPeer(peer: peer, navigation: .chat(textInputState: nil, subject: nil, peekData: nil), fromMessage: nil)
|
||||
})))
|
||||
if !isChannel && canSendMessagesToChat(strongSelf.presentationInterfaceState) {
|
||||
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_ContextMenuMention, icon: { theme in
|
||||
@ -4114,6 +4140,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
chatInfoButtonItem.action = #selector(self.rightNavigationButtonAction)
|
||||
self.chatInfoNavigationButton = ChatNavigationButton(action: .openChatInfo(expandAvatar: true), buttonItem: chatInfoButtonItem)
|
||||
|
||||
self.moreBarButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: self.presentationData.theme.rootController.navigationBar.buttonColor)))
|
||||
self.moreInfoNavigationButton = ChatNavigationButton(action: .toggleInfoPanel, buttonItem: UIBarButtonItem(customDisplayNode: self.moreBarButton)!)
|
||||
self.moreBarButton.contextAction = { [weak self] sourceNode, gesture in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
guard case let .peer(peerId) = self.chatLocation else {
|
||||
return
|
||||
}
|
||||
ChatListControllerImpl.openMoreMenu(context: self.context, peerId: peerId, sourceController: self, isViewingAsTopics: false, sourceView: sourceNode.view, gesture: gesture)
|
||||
}
|
||||
self.moreBarButton.addTarget(self, action: #selector(self.moreButtonPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.navigationItem.titleView = self.chatTitleView
|
||||
self.chatTitleView?.pressed = { [weak self] in
|
||||
self?.navigationButtonAction(.openChatInfo(expandAvatar: false))
|
||||
@ -4338,7 +4377,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if case .pinnedMessages = presentationInterfaceState.subject {
|
||||
strongSelf.chatTitleView?.titleContent = .custom(presentationInterfaceState.strings.Chat_TitlePinnedMessages(Int32(displayedCount ?? 1)), nil, false)
|
||||
} else {
|
||||
strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages)
|
||||
strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, customTitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages)
|
||||
let imageOverride: AvatarNodeImageOverride?
|
||||
if strongSelf.context.account.peerId == peer.id {
|
||||
imageOverride = .savedMessagesIcon
|
||||
@ -4369,9 +4408,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.hasScheduledMessages = hasScheduledMessages
|
||||
|
||||
var upgradedToPeerId: PeerId?
|
||||
var movedToForumTopics = false
|
||||
if let previous = strongSelf.peerView, let group = previous.peers[previous.peerId] as? TelegramGroup, group.migrationReference == nil, let updatedGroup = peerView.peers[peerView.peerId] as? TelegramGroup, let migrationReference = updatedGroup.migrationReference {
|
||||
upgradedToPeerId = migrationReference.peerId
|
||||
}
|
||||
if let previous = strongSelf.peerView, let channel = previous.peers[previous.peerId] as? TelegramChannel, !channel.flags.contains(.isForum), let updatedChannel = peerView.peers[peerView.peerId] as? TelegramChannel, updatedChannel.flags.contains(.isForum) {
|
||||
movedToForumTopics = true
|
||||
}
|
||||
|
||||
var shouldDismiss = false
|
||||
if let previous = strongSelf.peerView, let group = previous.peers[previous.peerId] as? TelegramGroup, group.membership != .Removed, let updatedGroup = peerView.peers[peerView.peerId] as? TelegramGroup, updatedGroup.membership == .Removed {
|
||||
@ -4661,6 +4704,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
navigationController.setViewControllers(viewControllers, animated: false)
|
||||
}
|
||||
}
|
||||
} else if movedToForumTopics {
|
||||
if let navigationController = strongSelf.effectiveNavigationController {
|
||||
let chatListController = strongSelf.context.sharedContext.makeChatListController(context: strongSelf.context, location: .forum(peerId: peerView.peerId), controlsHistoryPreload: false, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: false)
|
||||
navigationController.replaceController(strongSelf, with: chatListController, animated: true)
|
||||
}
|
||||
} else if shouldDismiss {
|
||||
strongSelf.dismiss()
|
||||
}
|
||||
@ -4685,13 +4733,65 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
let peerView = context.account.viewTracker.peerView(peerId)
|
||||
|
||||
let messageAndTopic = messagePromise.get()
|
||||
|> mapToSignal { message -> Signal<(message: Message?, threadInfo: EngineMessageHistoryThread.Info?), NoError> in
|
||||
guard let message = message else {
|
||||
return .single((nil, nil))
|
||||
}
|
||||
|
||||
let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: Int64(message.id.id))
|
||||
return context.account.postbox.combinedView(keys: [viewKey])
|
||||
|> map { views -> (message: Message?, threadInfo: EngineMessageHistoryThread.Info?) in
|
||||
guard let view = views.views[viewKey] as? MessageHistoryThreadInfoView else {
|
||||
return (message, nil)
|
||||
}
|
||||
return (message, view.info?.get(MessageHistoryThreadData.self)?.info)
|
||||
}
|
||||
}
|
||||
|
||||
var onlineMemberCount: Signal<Int32?, NoError> = .single(nil)
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
let recentOnlineSignal: Signal<Int32?, NoError> = peerView
|
||||
|> map { view -> Bool? in
|
||||
if let cachedData = view.cachedData as? CachedChannelData, let peer = peerViewMainPeer(view) as? TelegramChannel {
|
||||
if case .broadcast = peer.info {
|
||||
return nil
|
||||
} else if let memberCount = cachedData.participantsSummary.memberCount, memberCount > 50 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> mapToSignal { isLarge -> Signal<Int32?, NoError> in
|
||||
if let isLarge = isLarge {
|
||||
if isLarge {
|
||||
return context.peerChannelMemberCategoriesContextsManager.recentOnline(account: context.account, accountPeerId: context.account.peerId, peerId: peerId)
|
||||
|> map(Optional.init)
|
||||
} else {
|
||||
return context.peerChannelMemberCategoriesContextsManager.recentOnlineSmall(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId)
|
||||
|> map(Optional.init)
|
||||
}
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
onlineMemberCount = recentOnlineSignal
|
||||
}
|
||||
|
||||
self.titleDisposable.set(nil)
|
||||
self.peerDisposable.set((combineLatest(queue: Queue.mainQueue(),
|
||||
peerView,
|
||||
messagePromise.get()
|
||||
messageAndTopic,
|
||||
onlineMemberCount
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peerView, message in
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peerView, messageAndTopic, onlineMemberCount in
|
||||
if let strongSelf = self {
|
||||
let message = messageAndTopic.message
|
||||
|
||||
var count = 0
|
||||
if let message = message {
|
||||
for attribute in message.attributes {
|
||||
@ -4702,7 +4802,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.chatTitleView?.titleContent = .replyThread(type: replyThreadType, count: count)
|
||||
if let threadInfo = messageAndTopic.threadInfo, let message = message {
|
||||
strongSelf.chatTitleView?.titleContent = .peer(peerView: peerView, customTitle: threadInfo.title, onlineMemberCount: onlineMemberCount, isScheduledMessages: false)
|
||||
|
||||
let avatarContent: EmojiStatusComponent.Content
|
||||
if let fileId = threadInfo.icon {
|
||||
avatarContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: strongSelf.presentationData.theme.list.mediaPlaceholderColor, themeColor: nil, loopMode: .count(2))
|
||||
} else {
|
||||
avatarContent = .topic(title: String(threadInfo.title.prefix(1)), colorIndex: Int(message.id.id))
|
||||
}
|
||||
(strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.setStatus(context: strongSelf.context, content: avatarContent)
|
||||
} else {
|
||||
strongSelf.chatTitleView?.titleContent = .replyThread(type: replyThreadType, count: count)
|
||||
}
|
||||
|
||||
let firstTime = strongSelf.peerView == nil
|
||||
strongSelf.peerView = peerView
|
||||
@ -5866,7 +5978,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
if case let .replyThread(replyThreadMessageId) = strongSelf.chatLocation {
|
||||
pinnedMessageId = replyThreadMessageId.effectiveTopId
|
||||
if let channel = combinedInitialData.initialData?.peer as? TelegramChannel, channel.flags.contains(.isForum) {
|
||||
pinnedMessageId = nil
|
||||
} else {
|
||||
pinnedMessageId = replyThreadMessageId.effectiveTopId
|
||||
}
|
||||
}
|
||||
|
||||
var pinnedMessage: ChatPinnedMessage?
|
||||
@ -6012,12 +6128,23 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|
||||
let isForum = self.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> map { peer -> Bool in
|
||||
if case let .channel(channel) = peer {
|
||||
return channel.flags.contains(.isForum)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|
||||
self.cachedDataDisposable = combineLatest(queue: .mainQueue(), self.chatDisplayNode.historyNode.cachedPeerDataAndMessages,
|
||||
hasPendingMessages,
|
||||
isTopReplyThreadMessageShown,
|
||||
topPinnedMessage,
|
||||
customEmojiAvailable
|
||||
).start(next: { [weak self] cachedDataAndMessages, hasPendingMessages, isTopReplyThreadMessageShown, topPinnedMessage, customEmojiAvailable in
|
||||
customEmojiAvailable,
|
||||
isForum
|
||||
).start(next: { [weak self] cachedDataAndMessages, hasPendingMessages, isTopReplyThreadMessageShown, topPinnedMessage, customEmojiAvailable, isForum in
|
||||
if let strongSelf = self {
|
||||
let (cachedData, messages) = cachedDataAndMessages
|
||||
|
||||
@ -6073,14 +6200,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
var pinnedMessage: ChatPinnedMessage?
|
||||
switch strongSelf.chatLocation {
|
||||
case let .replyThread(replyThreadMessage):
|
||||
if isTopReplyThreadMessageShown {
|
||||
if isForum {
|
||||
pinnedMessageId = nil
|
||||
} else {
|
||||
pinnedMessageId = replyThreadMessage.effectiveTopId
|
||||
}
|
||||
if let pinnedMessageId = pinnedMessageId {
|
||||
if let message = messages?[pinnedMessageId] {
|
||||
pinnedMessage = ChatPinnedMessage(message: message, index: 0, totalCount: 1, topMessageId: message.id)
|
||||
if isTopReplyThreadMessageShown {
|
||||
pinnedMessageId = nil
|
||||
} else {
|
||||
pinnedMessageId = replyThreadMessage.effectiveTopId
|
||||
}
|
||||
if let pinnedMessageId = pinnedMessageId {
|
||||
if let message = messages?[pinnedMessageId] {
|
||||
pinnedMessage = ChatPinnedMessage(message: message, index: 0, totalCount: 1, topMessageId: message.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
case .peer:
|
||||
@ -7703,7 +7834,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.openPeer(peerId: peerId, navigation: .default, fromMessage: nil)
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
strongSelf.openPeer(peer: peer, navigation: .default, fromMessage: nil)
|
||||
}
|
||||
})
|
||||
}, openPeerInfo: { [weak self] in
|
||||
self?.navigationButtonAction(.openChatInfo(expandAvatar: false))
|
||||
}, togglePeerNotifications: { [weak self] in
|
||||
@ -7765,7 +7901,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if case .scheduledMessages = strongSelf.presentationInterfaceState.subject {
|
||||
isScheduled = true
|
||||
}
|
||||
strongSelf.openPeer(peerId: peerId, navigation: .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .automatic(returnToPeerId: currentPeerId, scheduled: isScheduled))), fromMessage: nil)
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
strongSelf.openPeer(peer: peer, navigation: .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .automatic(returnToPeerId: currentPeerId, scheduled: isScheduled))), fromMessage: nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, beginMediaRecording: { [weak self] isVideo in
|
||||
guard let strongSelf = self else {
|
||||
@ -10554,7 +10695,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.leftNavigationButton = nil
|
||||
}
|
||||
|
||||
if let button = rightNavigationButtonForChatInterfaceState(updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: self.rightNavigationButton, target: self, selector: #selector(self.rightNavigationButtonAction), chatInfoNavigationButton: self.chatInfoNavigationButton) {
|
||||
if let button = rightNavigationButtonForChatInterfaceState(updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: self.rightNavigationButton, target: self, selector: #selector(self.rightNavigationButtonAction), chatInfoNavigationButton: self.chatInfoNavigationButton, moreInfoNavigationButton: self.moreInfoNavigationButton) {
|
||||
if self.rightNavigationButton != button {
|
||||
var animated = transition.isAnimated
|
||||
if let currentButton = self.rightNavigationButton?.action, currentButton == button.action {
|
||||
@ -10680,6 +10821,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func moreButtonPressed() {
|
||||
self.moreBarButton.play()
|
||||
self.moreBarButton.contextAction?(self.moreBarButton.containerNode, nil)
|
||||
}
|
||||
|
||||
func beginClearHistory(type: InteractiveHistoryClearingType) {
|
||||
guard case let .peer(peerId) = self.chatLocation else {
|
||||
return
|
||||
@ -12799,7 +12945,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
case let .mention(peerId, mention):
|
||||
switch action {
|
||||
case .tap:
|
||||
strongSelf.controllerInteraction?.openPeer(peerId, .default, nil, false, nil)
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
strongSelf.controllerInteraction?.openPeer(peer, .default, nil, false)
|
||||
}
|
||||
})
|
||||
case .longTap:
|
||||
strongSelf.controllerInteraction?.longTap(.peerMention(peerId, mention), nil)
|
||||
}
|
||||
@ -12887,7 +13038,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
case let .mention(peerId, mention):
|
||||
switch action {
|
||||
case .tap:
|
||||
strongSelf.controllerInteraction?.openPeer(peerId, .default, nil, false, nil)
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
strongSelf.controllerInteraction?.openPeer(peer, .default, nil, false)
|
||||
}
|
||||
})
|
||||
case .longTap:
|
||||
strongSelf.controllerInteraction?.longTap(.peerMention(peerId, mention), nil)
|
||||
}
|
||||
@ -12996,7 +13152,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
case let .mention(peerId, mention):
|
||||
switch action {
|
||||
case .tap:
|
||||
strongSelf.controllerInteraction?.openPeer(peerId, .default, nil, false, nil)
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
strongSelf.controllerInteraction?.openPeer(peer, .default, nil, false)
|
||||
}
|
||||
})
|
||||
case .longTap:
|
||||
strongSelf.controllerInteraction?.longTap(.peerMention(peerId, mention), nil)
|
||||
}
|
||||
@ -14980,9 +15141,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
}
|
||||
|
||||
private func openPeer(peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer, fromMessage: MessageReference?, fromReactionMessageId: MessageId? = nil, expandAvatar: Bool = false) {
|
||||
private func openPeer(peer: EnginePeer?, navigation: ChatControllerInteractionNavigateToPeer, fromMessage: MessageReference?, fromReactionMessageId: MessageId? = nil, expandAvatar: Bool = false) {
|
||||
let _ = self.presentVoiceMessageDiscardAlert(action: {
|
||||
if case let .peer(currentPeerId) = self.chatLocation, peerId == currentPeerId {
|
||||
if case let .peer(currentPeerId) = self.chatLocation, peer?.id == currentPeerId {
|
||||
switch navigation {
|
||||
case .info:
|
||||
self.navigationButtonAction(.openChatInfo(expandAvatar: expandAvatar))
|
||||
@ -15006,7 +15167,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if let peerId = peerId {
|
||||
if let peer = peer {
|
||||
do {
|
||||
var chatPeerId: PeerId?
|
||||
if let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramGroup {
|
||||
@ -15019,9 +15180,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
case .info, .default:
|
||||
let peerSignal: Signal<Peer?, NoError>
|
||||
if let messageId = fromMessage?.id {
|
||||
peerSignal = loadedPeerFromMessage(account: self.context.account, peerId: peerId, messageId: messageId)
|
||||
peerSignal = loadedPeerFromMessage(account: self.context.account, peerId: peer.id, messageId: messageId)
|
||||
} else {
|
||||
peerSignal = self.context.account.postbox.loadedPeerWithId(peerId) |> map(Optional.init)
|
||||
peerSignal = self.context.account.postbox.loadedPeerWithId(peer.id) |> map(Optional.init)
|
||||
}
|
||||
self.navigationActionDisposable.set((peerSignal |> take(1) |> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
@ -15046,22 +15207,22 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}))
|
||||
case let .chat(textInputState, subject, peekData):
|
||||
if let textInputState = textInputState {
|
||||
let _ = (ChatInterfaceState.update(engine: self.context.engine, peerId: peerId, threadId: nil, { currentState in
|
||||
let _ = (ChatInterfaceState.update(engine: self.context.engine, peerId: peer.id, threadId: nil, { currentState in
|
||||
return currentState.withUpdatedComposeInputState(textInputState)
|
||||
})
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
if let strongSelf = self, let navigationController = strongSelf.effectiveNavigationController {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), subject: subject, updateTextInputState: textInputState, peekData: peekData))
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), subject: subject, updateTextInputState: textInputState, peekData: peekData))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
self.effectiveNavigationController?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(id: peerId), subject: subject))
|
||||
self.effectiveNavigationController?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(id: peer.id), subject: subject))
|
||||
}
|
||||
case let .withBotStartPayload(botStart):
|
||||
self.effectiveNavigationController?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(id: peerId), botStart: botStart))
|
||||
self.effectiveNavigationController?.pushViewController(ChatControllerImpl(context: self.context, chatLocation: .peer(id: peer.id), botStart: botStart))
|
||||
case let .withAttachBot(attachBotStart):
|
||||
if let navigationController = self.effectiveNavigationController {
|
||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(id: peerId), attachBotStart: attachBotStart))
|
||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(id: peer.id), attachBotStart: attachBotStart))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -15175,7 +15336,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
navigation = .chat(textInputState: nil, subject: nil, peekData: nil)
|
||||
}
|
||||
}
|
||||
strongSelf.openResolved(result: .peer(peer.id, navigation), sourceMessageId: sourceMessageId)
|
||||
strongSelf.openResolved(result: .peer(peer, navigation), sourceMessageId: sourceMessageId)
|
||||
} else {
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Resolve_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
@ -15505,17 +15666,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
switch navigation {
|
||||
case let .chat(_, subject, peekData):
|
||||
if case .peer(peerId) = strongSelf.chatLocation {
|
||||
if case .peer(peerId.id) = strongSelf.chatLocation {
|
||||
if let subject = subject, case let .message(messageSubject, _, timecode) = subject {
|
||||
if case let .id(messageId) = messageSubject {
|
||||
strongSelf.navigateToMessage(from: sourceMessageId, to: .id(messageId, timecode))
|
||||
}
|
||||
}
|
||||
} else if let navigationController = strongSelf.effectiveNavigationController {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), subject: subject, keepStack: .always, peekData: peekData))
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId.id), subject: subject, keepStack: .always, peekData: peekData))
|
||||
}
|
||||
case .info:
|
||||
strongSelf.navigationActionDisposable.set((strongSelf.context.account.postbox.loadedPeerWithId(peerId)
|
||||
strongSelf.navigationActionDisposable.set((strongSelf.context.account.postbox.loadedPeerWithId(peerId.id)
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
if let strongSelf = self, peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil {
|
||||
@ -15525,16 +15686,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}))
|
||||
case let .withBotStartPayload(startPayload):
|
||||
if case .peer(peerId) = strongSelf.chatLocation {
|
||||
if case .peer(peerId.id) = strongSelf.chatLocation {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||
$0.updatedBotStartPayload(startPayload.payload)
|
||||
})
|
||||
} else if let navigationController = strongSelf.effectiveNavigationController {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), botStart: startPayload, keepStack: .always))
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId.id), botStart: startPayload, keepStack: .always))
|
||||
}
|
||||
case let .withAttachBot(attachBotStart):
|
||||
if let navigationController = strongSelf.effectiveNavigationController {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), attachBotStart: attachBotStart))
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId.id), attachBotStart: attachBotStart))
|
||||
}
|
||||
default:
|
||||
break
|
||||
|
@ -61,7 +61,7 @@ struct UnreadMessageRangeKey: Hashable {
|
||||
|
||||
public final class ChatControllerInteraction {
|
||||
let openMessage: (Message, ChatControllerInteractionOpenMessageMode) -> Bool
|
||||
let openPeer: (PeerId?, ChatControllerInteractionNavigateToPeer, MessageReference?, Bool, Peer?) -> Void
|
||||
let openPeer: (EnginePeer, ChatControllerInteractionNavigateToPeer, MessageReference?, Bool) -> Void
|
||||
let openPeerMention: (String) -> Void
|
||||
let openMessageContextMenu: (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?, CGPoint?) -> Void
|
||||
let updateMessageReaction: (Message, ChatControllerInteractionReaction) -> Void
|
||||
@ -167,7 +167,7 @@ public final class ChatControllerInteraction {
|
||||
|
||||
init(
|
||||
openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool,
|
||||
openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, MessageReference?, Bool, Peer?) -> Void,
|
||||
openPeer: @escaping (EnginePeer, ChatControllerInteractionNavigateToPeer, MessageReference?, Bool) -> Void,
|
||||
openPeerMention: @escaping (String) -> Void,
|
||||
openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?, CGPoint?) -> Void,
|
||||
openMessageReactionContextMenu: @escaping (Message, ContextExtractedContentContainingView, ContextGesture?, MessageReaction.Reaction) -> Void,
|
||||
|
@ -20,6 +20,7 @@ import SparseItemGrid
|
||||
import ChatPresentationInterfaceState
|
||||
import ChatInputPanelContainer
|
||||
import PremiumUI
|
||||
import ChatTitleView
|
||||
|
||||
final class VideoNavigationControllerDropContentItem: NavigationControllerDropContentItem {
|
||||
let itemNode: OverlayMediaItemNode
|
||||
|
@ -238,9 +238,11 @@ func chatHistoryEntriesForView(
|
||||
entries.insert(.MessageEntry(messages[0], presentationData, false, nil, selection, ChatMessageEntryAttributes(rank: adminRank, isContact: false, contentTypeHint: contentTypeHint, updatingMedia: updatingMedia[messages[0].id], isPlaying: false, isCentered: false)), at: 0)
|
||||
}
|
||||
|
||||
let replyCount = view.entries.isEmpty ? 0 : 1
|
||||
|
||||
entries.insert(.ReplyCountEntry(messages[0].index, replyThreadMessage.isChannelPost, replyCount, presentationData), at: 1)
|
||||
if !replyThreadMessage.isForumPost {
|
||||
let replyCount = view.entries.isEmpty ? 0 : 1
|
||||
|
||||
entries.insert(.ReplyCountEntry(messages[0].index, replyThreadMessage.isChannelPost, replyCount, presentationData), at: 1)
|
||||
}
|
||||
}
|
||||
break loop
|
||||
default:
|
||||
|
@ -1599,7 +1599,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
actions.insert(.custom(ChatReadReportContextItem(context: context, message: message, hasReadReports: hasReadReports, stats: readStats, action: { c, f, stats, customReactionEmojiPacks, firstCustomEmojiReaction in
|
||||
if reactionCount == 0, let stats = stats, stats.peers.count == 1 {
|
||||
c.dismiss(completion: {
|
||||
controllerInteraction.openPeer(stats.peers[0].id, .default, nil, false, nil)
|
||||
controllerInteraction.openPeer(stats.peers[0], .default, nil, false)
|
||||
})
|
||||
} else if (stats != nil && !stats!.peers.isEmpty) || reactionCount != 0 {
|
||||
var tip: ContextController.Tip?
|
||||
@ -1641,9 +1641,9 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
back: { [weak c] in
|
||||
c?.popItems()
|
||||
},
|
||||
openPeer: { [weak c] id in
|
||||
openPeer: { [weak c] peer in
|
||||
c?.dismiss(completion: {
|
||||
controllerInteraction.openPeer(id, .default, MessageReference(message), true, nil)
|
||||
controllerInteraction.openPeer(peer, .default, MessageReference(message), true)
|
||||
})
|
||||
}
|
||||
)), tip: tip)))
|
||||
|
@ -76,7 +76,7 @@ func leftNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Cha
|
||||
return nil
|
||||
}
|
||||
|
||||
func rightNavigationButtonForChatInterfaceState(_ presentationInterfaceState: ChatPresentationInterfaceState, strings: PresentationStrings, currentButton: ChatNavigationButton?, target: Any?, selector: Selector?, chatInfoNavigationButton: ChatNavigationButton?) -> ChatNavigationButton? {
|
||||
func rightNavigationButtonForChatInterfaceState(_ presentationInterfaceState: ChatPresentationInterfaceState, strings: PresentationStrings, currentButton: ChatNavigationButton?, target: Any?, selector: Selector?, chatInfoNavigationButton: ChatNavigationButton?, moreInfoNavigationButton: ChatNavigationButton?) -> ChatNavigationButton? {
|
||||
if let _ = presentationInterfaceState.interfaceState.selectionState {
|
||||
if case .forwardedMessages = presentationInterfaceState.subject {
|
||||
return nil
|
||||
@ -90,6 +90,13 @@ func rightNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Ch
|
||||
}
|
||||
}
|
||||
|
||||
if let channel = presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum), let moreInfoNavigationButton = moreInfoNavigationButton {
|
||||
if case .replyThread = presentationInterfaceState.chatLocation {
|
||||
} else {
|
||||
return moreInfoNavigationButton
|
||||
}
|
||||
}
|
||||
|
||||
var hasMessages = false
|
||||
if let chatHistoryState = presentationInterfaceState.chatHistoryState {
|
||||
if case .loaded(false) = chatHistoryState {
|
||||
@ -106,7 +113,8 @@ func rightNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Ch
|
||||
}
|
||||
|
||||
if case .replyThread = presentationInterfaceState.chatLocation {
|
||||
if hasMessages {
|
||||
if let channel = presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum) {
|
||||
} else if hasMessages {
|
||||
if case .search = currentButton?.action {
|
||||
return currentButton
|
||||
} else {
|
||||
|
@ -1993,7 +1993,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
|
||||
} else if let peer = forwardInfo.source ?? forwardInfo.author {
|
||||
item.controllerInteraction.openPeer(peer.id, peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, false, nil)
|
||||
item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, false)
|
||||
} else if let _ = forwardInfo.authorSignature {
|
||||
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil)
|
||||
}
|
||||
|
@ -3295,7 +3295,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
})
|
||||
} else {
|
||||
return .optionalAction({
|
||||
item.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil, peekData: nil), nil, false, item.message.peers[peerId])
|
||||
if let peer = item.message.peers[peerId] {
|
||||
item.controllerInteraction.openPeer(EnginePeer(peer), .chat(textInputState: nil, subject: nil, peekData: nil), nil, false)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -3327,7 +3329,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
}
|
||||
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
|
||||
} else if let peer = forwardInfo.source ?? forwardInfo.author {
|
||||
item.controllerInteraction.openPeer(peer.id, peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, false, nil)
|
||||
item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, false)
|
||||
} else if let _ = forwardInfo.authorSignature {
|
||||
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil)
|
||||
}
|
||||
@ -3365,8 +3367,15 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
self.item?.controllerInteraction.openUrl(url, concealed, nil, self.item?.content.firstMessage)
|
||||
})
|
||||
case let .peerMention(peerId, _):
|
||||
return .action({
|
||||
self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil, peekData: nil), nil, false, nil)
|
||||
return .action({ [weak self] in
|
||||
if let item = self?.item {
|
||||
let _ = (item.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let self = self, let item = self.item, let peer = peer {
|
||||
item.controllerInteraction.openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: nil), nil, false)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
case let .textMention(name):
|
||||
return .action({
|
||||
|
@ -635,11 +635,11 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
|
||||
if case .ended = recognizer.state {
|
||||
if self.peerId.namespace == Namespaces.Peer.Empty, case let .message(_, id, _, _, _) = self.messageReference?.content {
|
||||
self.controllerInteraction.displayMessageTooltip(id, self.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, self.avatarNode.frame)
|
||||
} else {
|
||||
if let channel = self.peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
self.controllerInteraction.openPeer(self.peerId, .chat(textInputState: nil, subject: nil, peekData: nil), self.messageReference, false, nil)
|
||||
} else if let peer = self.peer {
|
||||
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
self.controllerInteraction.openPeer(EnginePeer(peer), .chat(textInputState: nil, subject: nil, peekData: nil), self.messageReference, false)
|
||||
} else {
|
||||
self.controllerInteraction.openPeer(self.peerId, .info, self.messageReference, false, nil)
|
||||
self.controllerInteraction.openPeer(EnginePeer(peer), .info, self.messageReference, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -928,7 +928,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
||||
}
|
||||
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
|
||||
} else if let peer = forwardInfo.source ?? forwardInfo.author {
|
||||
item.controllerInteraction.openPeer(peer.id, peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, false, nil)
|
||||
item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, false)
|
||||
} else if let _ = forwardInfo.authorSignature {
|
||||
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil)
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import LocalizedPeerData
|
||||
import ContextUI
|
||||
import ChatListUI
|
||||
import TelegramPresentationData
|
||||
import SwiftSignalKit
|
||||
|
||||
struct ChatMessageItemWidthFill {
|
||||
var compactInset: CGFloat
|
||||
@ -851,7 +852,12 @@ public class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol
|
||||
case .setupPoll:
|
||||
break
|
||||
case let .openUserProfile(peerId):
|
||||
item.controllerInteraction.openPeer(peerId, .info, nil, false, nil)
|
||||
let _ = (item.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let peer = peer {
|
||||
item.controllerInteraction.openPeer(peer, .info, nil, false)
|
||||
}
|
||||
})
|
||||
case let .openWebView(url, simple):
|
||||
item.controllerInteraction.openWebView(button.title, url, simple, false)
|
||||
}
|
||||
|
@ -76,7 +76,12 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
navigationData = .chat(textInputState: nil, subject: subject, peekData: nil)
|
||||
}
|
||||
item.controllerInteraction.openPeer(id, navigationData, nil, false, nil)
|
||||
let _ = (item.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: id))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let peer = peer {
|
||||
item.controllerInteraction.openPeer(peer, navigationData, nil, false)
|
||||
}
|
||||
})
|
||||
case let .join(_, joinHash):
|
||||
item.controllerInteraction.openJoinLink(joinHash)
|
||||
}
|
||||
|
@ -851,7 +851,12 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
case .setupPoll:
|
||||
break
|
||||
case let .openUserProfile(peerId):
|
||||
controllerInteraction.openPeer(peerId, .info, nil, false, nil)
|
||||
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let peer = peer {
|
||||
controllerInteraction.openPeer(peer, .info, nil, false)
|
||||
}
|
||||
})
|
||||
case let .openWebView(url, simple):
|
||||
controllerInteraction.openWebView(button.title, url, simple, false)
|
||||
}
|
||||
|
@ -221,7 +221,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}, openUrl: { url in
|
||||
self?.openUrl(url)
|
||||
}, openPeer: { peer, navigation in
|
||||
self?.openPeer(peerId: peer.id, peer: peer)
|
||||
self?.openPeer(peer: EnginePeer(peer))
|
||||
}, callPeer: { peerId, isVideo in
|
||||
self?.controllerInteraction?.callPeer(peerId, isVideo)
|
||||
}, enqueueMessage: { _ in
|
||||
@ -248,9 +248,9 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}, gallerySource: gallerySource))
|
||||
}
|
||||
return false
|
||||
}, openPeer: { [weak self] peerId, _, message, _, peer in
|
||||
if let peerId = peerId, peerId != context.account.peerId {
|
||||
self?.openPeer(peerId: peerId, peer: peer)
|
||||
}, openPeer: { [weak self] peer, _, message, _ in
|
||||
if peer.id != context.account.peerId {
|
||||
self?.openPeer(peer: peer)
|
||||
}
|
||||
}, openPeerMention: { [weak self] name in
|
||||
self?.openPeerMention(name)
|
||||
@ -376,7 +376,12 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_LinkDialogOpen, color: .accent, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
if let strongSelf = self {
|
||||
strongSelf.openPeer(peerId: peerId, peer: nil)
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
strongSelf.openPeer(peer: peer)
|
||||
}
|
||||
})
|
||||
}
|
||||
}))
|
||||
if !mention.isEmpty {
|
||||
@ -762,13 +767,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
self.eventLogContext.setFilter(self.filter)
|
||||
}
|
||||
|
||||
private func openPeer(peerId: PeerId, peer: Peer?, peekData: ChatPeekTimeout? = nil) {
|
||||
let peerSignal: Signal<Peer?, NoError>
|
||||
if let peer = peer {
|
||||
peerSignal = .single(peer)
|
||||
} else {
|
||||
peerSignal = self.context.account.postbox.loadedPeerWithId(peerId) |> map(Optional.init)
|
||||
}
|
||||
private func openPeer(peer: EnginePeer, peekData: ChatPeekTimeout? = nil) {
|
||||
let peerSignal: Signal<Peer?, NoError> = .single(peer._asPeer())
|
||||
self.navigationActionDisposable.set((peerSignal |> take(1) |> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
if peer is TelegramChannel, let navigationController = strongSelf.getNavigationController() {
|
||||
@ -882,9 +882,9 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}
|
||||
case .urlAuth:
|
||||
break
|
||||
case let .peer(peerId, _):
|
||||
if let peerId = peerId {
|
||||
strongSelf.openPeer(peerId: peerId, peer: nil)
|
||||
case let .peer(peer, _):
|
||||
if let peer = peer {
|
||||
strongSelf.openPeer(peer: EnginePeer(peer))
|
||||
}
|
||||
case .inaccessiblePeer:
|
||||
strongSelf.controllerInteraction.presentController(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Conversation_ErrorInaccessibleMessage, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil)
|
||||
@ -892,9 +892,9 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
break
|
||||
case .groupBotStart:
|
||||
break
|
||||
case let .channelMessage(peerId, messageId, timecode):
|
||||
case let .channelMessage(peer, messageId, timecode):
|
||||
if let navigationController = strongSelf.getNavigationController() {
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .message(id: .id(messageId), highlight: true, timecode: timecode)))
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), subject: .message(id: .id(messageId), highlight: true, timecode: timecode)))
|
||||
}
|
||||
case let .replyThreadMessage(replyThreadMessage, messageId):
|
||||
if let navigationController = strongSelf.getNavigationController() {
|
||||
@ -931,17 +931,17 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
case let .instantView(webpage, anchor):
|
||||
strongSelf.pushController(InstantPageController(context: strongSelf.context, webPage: webpage, sourcePeerType: .channel, anchor: anchor))
|
||||
case let .join(link):
|
||||
strongSelf.presentController(JoinLinkPreviewController(context: strongSelf.context, link: link, navigateToPeer: { peerId, peekData in
|
||||
strongSelf.presentController(JoinLinkPreviewController(context: strongSelf.context, link: link, navigateToPeer: { peer, peekData in
|
||||
if let strongSelf = self {
|
||||
strongSelf.openPeer(peerId: peerId, peer: nil, peekData: peekData)
|
||||
strongSelf.openPeer(peer: peer, peekData: peekData)
|
||||
}
|
||||
}, parentNavigationController: strongSelf.getNavigationController()), .window(.root), nil)
|
||||
case let .localization(identifier):
|
||||
strongSelf.presentController(LanguageLinkPreviewController(context: strongSelf.context, identifier: identifier), .window(.root), nil)
|
||||
case .proxy, .confirmationCode, .cancelAccountReset, .share:
|
||||
strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.getNavigationController(), forceExternal: false, openPeer: { peerId, _ in
|
||||
strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.getNavigationController(), forceExternal: false, openPeer: { peer, _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.openPeer(peerId: peerId, peer: nil)
|
||||
strongSelf.openPeer(peer: peer)
|
||||
}
|
||||
}, sendFile: nil,
|
||||
sendSticker: nil,
|
||||
|
@ -1,843 +1 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
import LegacyComponents
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import ActivityIndicator
|
||||
import TelegramStringFormatting
|
||||
import PeerPresenceStatusManager
|
||||
import ChatTitleActivityNode
|
||||
import LocalizedPeerData
|
||||
import PhoneNumberFormat
|
||||
import ChatTitleActivityNode
|
||||
import AnimatedCountLabelNode
|
||||
import AccountContext
|
||||
import ComponentFlow
|
||||
import EmojiStatusComponent
|
||||
import AnimationCache
|
||||
import MultiAnimationRenderer
|
||||
|
||||
private let titleFont = Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers])
|
||||
private let subtitleFont = Font.regular(13.0)
|
||||
|
||||
enum ChatTitleContent {
|
||||
enum ReplyThreadType {
|
||||
case comments
|
||||
case replies
|
||||
}
|
||||
|
||||
case peer(peerView: PeerView, onlineMemberCount: Int32?, isScheduledMessages: Bool)
|
||||
case replyThread(type: ReplyThreadType, count: Int)
|
||||
case custom(String, String?, Bool)
|
||||
}
|
||||
|
||||
private enum ChatTitleIcon {
|
||||
case none
|
||||
case lock
|
||||
case mute
|
||||
}
|
||||
|
||||
private enum ChatTitleCredibilityIcon: Equatable {
|
||||
case none
|
||||
case fake
|
||||
case scam
|
||||
case verified
|
||||
case premium
|
||||
case emojiStatus(PeerEmojiStatus)
|
||||
}
|
||||
|
||||
final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
private let context: AccountContext
|
||||
|
||||
private var theme: PresentationTheme
|
||||
private var hasEmbeddedTitleContent: Bool = false
|
||||
private var strings: PresentationStrings
|
||||
private var dateTimeFormat: PresentationDateTimeFormat
|
||||
private var nameDisplayOrder: PresentationPersonNameOrder
|
||||
private let animationCache: AnimationCache
|
||||
private let animationRenderer: MultiAnimationRenderer
|
||||
|
||||
private let contentContainer: ASDisplayNode
|
||||
let titleContainerView: PortalSourceView
|
||||
let titleTextNode: ImmediateAnimatedCountLabelNode
|
||||
let titleLeftIconNode: ASImageNode
|
||||
let titleRightIconNode: ASImageNode
|
||||
let titleCredibilityIconView: ComponentHostView<Empty>
|
||||
let activityNode: ChatTitleActivityNode
|
||||
|
||||
private let button: HighlightTrackingButtonNode
|
||||
|
||||
private var validLayout: (CGSize, CGRect)?
|
||||
|
||||
private var titleLeftIcon: ChatTitleIcon = .none
|
||||
private var titleRightIcon: ChatTitleIcon = .none
|
||||
private var titleCredibilityIcon: ChatTitleCredibilityIcon = .none
|
||||
|
||||
//private var networkStatusNode: ChatTitleNetworkStatusNode?
|
||||
|
||||
private var presenceManager: PeerPresenceStatusManager?
|
||||
|
||||
private var pointerInteraction: PointerInteraction?
|
||||
|
||||
var inputActivities: (PeerId, [(Peer, PeerInputActivity)])? {
|
||||
didSet {
|
||||
let _ = self.updateStatus()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateNetworkStatusNode(networkState: AccountNetworkState, layout: ContainerViewLayout?) {
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
|
||||
var networkState: AccountNetworkState = .online(proxy: nil) {
|
||||
didSet {
|
||||
if self.networkState != oldValue {
|
||||
updateNetworkStatusNode(networkState: self.networkState, layout: self.layout)
|
||||
let _ = self.updateStatus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var layout: ContainerViewLayout? {
|
||||
didSet {
|
||||
if self.layout != oldValue {
|
||||
updateNetworkStatusNode(networkState: self.networkState, layout: self.layout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var pressed: (() -> Void)?
|
||||
var longPressed: (() -> Void)?
|
||||
|
||||
var titleContent: ChatTitleContent? {
|
||||
didSet {
|
||||
if let titleContent = self.titleContent {
|
||||
let titleTheme = self.hasEmbeddedTitleContent ? defaultDarkPresentationTheme : self.theme
|
||||
|
||||
var segments: [AnimatedCountLabelNode.Segment] = []
|
||||
var titleLeftIcon: ChatTitleIcon = .none
|
||||
var titleRightIcon: ChatTitleIcon = .none
|
||||
var titleCredibilityIcon: ChatTitleCredibilityIcon = .none
|
||||
var isEnabled = true
|
||||
switch titleContent {
|
||||
case let .peer(peerView, _, isScheduledMessages):
|
||||
if peerView.peerId.isReplies {
|
||||
let typeText: String = self.strings.DialogList_Replies
|
||||
segments = [.text(0, NSAttributedString(string: typeText, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
|
||||
isEnabled = false
|
||||
} else if isScheduledMessages {
|
||||
if peerView.peerId == self.context.account.peerId {
|
||||
segments = [.text(0, NSAttributedString(string: self.strings.ScheduledMessages_RemindersTitle, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
|
||||
} else {
|
||||
segments = [.text(0, NSAttributedString(string: self.strings.ScheduledMessages_Title, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
|
||||
}
|
||||
isEnabled = false
|
||||
} else {
|
||||
if let peer = peerViewMainPeer(peerView) {
|
||||
if peerView.peerId == self.context.account.peerId {
|
||||
segments = [.text(0, NSAttributedString(string: self.strings.Conversation_SavedMessages, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
|
||||
} else {
|
||||
if !peerView.peerIsContact, let user = peer as? TelegramUser, !user.flags.contains(.isSupport), user.botInfo == nil, let phone = user.phone, !phone.isEmpty {
|
||||
segments = [.text(0, NSAttributedString(string: formatPhoneNumber(phone), font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
|
||||
} else {
|
||||
segments = [.text(0, NSAttributedString(string: EnginePeer(peer).displayTitle(strings: self.strings, displayOrder: self.nameDisplayOrder), font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
|
||||
}
|
||||
}
|
||||
if peer.id != self.context.account.peerId {
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||
if peer.isFake {
|
||||
titleCredibilityIcon = .fake
|
||||
} else if peer.isScam {
|
||||
titleCredibilityIcon = .scam
|
||||
} else if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus, !premiumConfiguration.isPremiumDisabled {
|
||||
titleCredibilityIcon = .emojiStatus(emojiStatus)
|
||||
} else if peer.isVerified {
|
||||
titleCredibilityIcon = .verified
|
||||
} else if peer.isPremium && !premiumConfiguration.isPremiumDisabled {
|
||||
titleCredibilityIcon = .premium
|
||||
}
|
||||
}
|
||||
}
|
||||
if peerView.peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
titleLeftIcon = .lock
|
||||
}
|
||||
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings {
|
||||
if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
|
||||
if titleCredibilityIcon != .verified {
|
||||
titleRightIcon = .mute
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .replyThread(type, count):
|
||||
let textFont = titleFont
|
||||
let textColor = titleTheme.rootController.navigationBar.primaryTextColor
|
||||
|
||||
if count > 0 {
|
||||
var commentsPart: String
|
||||
switch type {
|
||||
case .comments:
|
||||
commentsPart = self.strings.Conversation_TitleComments(Int32(count))
|
||||
case .replies:
|
||||
commentsPart = self.strings.Conversation_TitleReplies(Int32(count))
|
||||
}
|
||||
|
||||
if commentsPart.contains("[") && commentsPart.contains("]") {
|
||||
if let startIndex = commentsPart.firstIndex(of: "["), let endIndex = commentsPart.firstIndex(of: "]") {
|
||||
commentsPart.removeSubrange(startIndex ... endIndex)
|
||||
}
|
||||
} else {
|
||||
commentsPart = commentsPart.trimmingCharacters(in: CharacterSet(charactersIn: "0123456789-,."))
|
||||
}
|
||||
|
||||
let rawTextAndRanges: PresentationStrings.FormattedString
|
||||
switch type {
|
||||
case .comments:
|
||||
rawTextAndRanges = self.strings.Conversation_TitleCommentsFormat("\(count)", commentsPart)
|
||||
case .replies:
|
||||
rawTextAndRanges = self.strings.Conversation_TitleRepliesFormat("\(count)", commentsPart)
|
||||
}
|
||||
|
||||
let rawText = rawTextAndRanges.string
|
||||
|
||||
var textIndex = 0
|
||||
var latestIndex = 0
|
||||
for indexAndRange in rawTextAndRanges.ranges {
|
||||
let index = indexAndRange.index
|
||||
let range = indexAndRange.range
|
||||
|
||||
var lowerSegmentIndex = range.lowerBound
|
||||
if index != 0 {
|
||||
lowerSegmentIndex = min(lowerSegmentIndex, latestIndex)
|
||||
} else {
|
||||
if latestIndex < range.lowerBound {
|
||||
let part = String(rawText[rawText.index(rawText.startIndex, offsetBy: latestIndex) ..< rawText.index(rawText.startIndex, offsetBy: range.lowerBound)])
|
||||
segments.append(.text(textIndex, NSAttributedString(string: part, font: textFont, textColor: textColor)))
|
||||
textIndex += 1
|
||||
}
|
||||
}
|
||||
latestIndex = range.upperBound
|
||||
|
||||
let part = String(rawText[rawText.index(rawText.startIndex, offsetBy: lowerSegmentIndex) ..< rawText.index(rawText.startIndex, offsetBy: min(rawText.count, range.upperBound))])
|
||||
if index == 0 {
|
||||
segments.append(.number(count, NSAttributedString(string: part, font: textFont, textColor: textColor)))
|
||||
} else {
|
||||
segments.append(.text(textIndex, NSAttributedString(string: part, font: textFont, textColor: textColor)))
|
||||
textIndex += 1
|
||||
}
|
||||
}
|
||||
if latestIndex < rawText.count {
|
||||
let part = String(rawText[rawText.index(rawText.startIndex, offsetBy: latestIndex)...])
|
||||
segments.append(.text(textIndex, NSAttributedString(string: part, font: textFont, textColor: textColor)))
|
||||
textIndex += 1
|
||||
}
|
||||
} else {
|
||||
switch type {
|
||||
case .comments:
|
||||
segments = [.text(0, NSAttributedString(string: strings.Conversation_TitleCommentsEmpty, font: textFont, textColor: textColor))]
|
||||
case .replies:
|
||||
segments = [.text(0, NSAttributedString(string: strings.Conversation_TitleRepliesEmpty, font: textFont, textColor: textColor))]
|
||||
}
|
||||
}
|
||||
|
||||
isEnabled = false
|
||||
case let .custom(text, _, enabled):
|
||||
segments = [.text(0, NSAttributedString(string: text, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))]
|
||||
isEnabled = enabled
|
||||
}
|
||||
|
||||
var updated = false
|
||||
|
||||
if self.titleTextNode.segments != segments {
|
||||
self.titleTextNode.segments = segments
|
||||
updated = true
|
||||
}
|
||||
|
||||
if titleLeftIcon != self.titleLeftIcon {
|
||||
self.titleLeftIcon = titleLeftIcon
|
||||
switch titleLeftIcon {
|
||||
case .lock:
|
||||
self.titleLeftIconNode.image = PresentationResourcesChat.chatTitleLockIcon(titleTheme)
|
||||
default:
|
||||
self.titleLeftIconNode.image = nil
|
||||
}
|
||||
updated = true
|
||||
}
|
||||
|
||||
if titleCredibilityIcon != self.titleCredibilityIcon {
|
||||
self.titleCredibilityIcon = titleCredibilityIcon
|
||||
/*switch titleCredibilityIcon {
|
||||
case .none:
|
||||
self.titleCredibilityIconNode.image = nil
|
||||
case .fake:
|
||||
self.titleCredibilityIconNode.image = PresentationResourcesChatList.fakeIcon(titleTheme, strings: self.strings, type: .regular)
|
||||
case .scam:
|
||||
self.titleCredibilityIconNode.image = PresentationResourcesChatList.scamIcon(titleTheme, strings: self.strings, type: .regular)
|
||||
case .verified:
|
||||
self.titleCredibilityIconNode.image = PresentationResourcesChatList.verifiedIcon(titleTheme)
|
||||
case .premium:
|
||||
self.titleCredibilityIconNode.image = PresentationResourcesChatList.premiumIcon(titleTheme)
|
||||
}*/
|
||||
updated = true
|
||||
}
|
||||
|
||||
if titleRightIcon != self.titleRightIcon {
|
||||
self.titleRightIcon = titleRightIcon
|
||||
switch titleRightIcon {
|
||||
case .mute:
|
||||
self.titleRightIconNode.image = PresentationResourcesChat.chatTitleMuteIcon(titleTheme)
|
||||
default:
|
||||
self.titleRightIconNode.image = nil
|
||||
}
|
||||
updated = true
|
||||
}
|
||||
self.isUserInteractionEnabled = isEnabled
|
||||
self.button.isUserInteractionEnabled = isEnabled
|
||||
if !self.updateStatus() {
|
||||
if updated {
|
||||
if let (size, clearBounds) = self.validLayout {
|
||||
self.updateLayout(size: size, clearBounds: clearBounds, transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateStatus() -> Bool {
|
||||
var inputActivitiesAllowed = true
|
||||
if let titleContent = self.titleContent {
|
||||
switch titleContent {
|
||||
case let .peer(peerView, _, isScheduledMessages):
|
||||
if let peer = peerViewMainPeer(peerView) {
|
||||
if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies {
|
||||
inputActivitiesAllowed = false
|
||||
}
|
||||
}
|
||||
case .replyThread:
|
||||
inputActivitiesAllowed = true
|
||||
default:
|
||||
inputActivitiesAllowed = false
|
||||
}
|
||||
}
|
||||
|
||||
let titleTheme = self.hasEmbeddedTitleContent ? defaultDarkPresentationTheme : self.theme
|
||||
|
||||
var state = ChatTitleActivityNodeState.none
|
||||
switch self.networkState {
|
||||
case .waitingForNetwork, .connecting, .updating:
|
||||
var infoText: String
|
||||
switch self.networkState {
|
||||
case .waitingForNetwork:
|
||||
infoText = self.strings.ChatState_WaitingForNetwork
|
||||
case .connecting:
|
||||
infoText = self.strings.ChatState_Connecting
|
||||
case .updating:
|
||||
infoText = self.strings.ChatState_Updating
|
||||
case .online:
|
||||
infoText = ""
|
||||
}
|
||||
state = .info(NSAttributedString(string: infoText, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor), .generic)
|
||||
case .online:
|
||||
if let (peerId, inputActivities) = self.inputActivities, !inputActivities.isEmpty, inputActivitiesAllowed {
|
||||
var stringValue = ""
|
||||
var mergedActivity = inputActivities[0].1
|
||||
for (_, activity) in inputActivities {
|
||||
if activity != mergedActivity {
|
||||
mergedActivity = .typingText
|
||||
break
|
||||
}
|
||||
}
|
||||
if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
switch mergedActivity {
|
||||
case .typingText:
|
||||
stringValue = strings.Conversation_typing
|
||||
case .uploadingFile:
|
||||
stringValue = strings.Activity_UploadingDocument
|
||||
case .recordingVoice:
|
||||
stringValue = strings.Activity_RecordingAudio
|
||||
case .uploadingPhoto:
|
||||
stringValue = strings.Activity_UploadingPhoto
|
||||
case .uploadingVideo:
|
||||
stringValue = strings.Activity_UploadingVideo
|
||||
case .playingGame:
|
||||
stringValue = strings.Activity_PlayingGame
|
||||
case .recordingInstantVideo:
|
||||
stringValue = strings.Activity_RecordingVideoMessage
|
||||
case .uploadingInstantVideo:
|
||||
stringValue = strings.Activity_UploadingVideoMessage
|
||||
case .choosingSticker:
|
||||
stringValue = strings.Activity_ChoosingSticker
|
||||
case let .seeingEmojiInteraction(emoticon):
|
||||
stringValue = strings.Activity_EnjoyingAnimations(emoticon).string
|
||||
case .speakingInGroupCall, .interactingWithEmoji:
|
||||
stringValue = ""
|
||||
}
|
||||
} else {
|
||||
if inputActivities.count > 1 {
|
||||
let peerTitle = EnginePeer(inputActivities[0].0).compactDisplayTitle
|
||||
if inputActivities.count == 2 {
|
||||
let secondPeerTitle = EnginePeer(inputActivities[1].0).compactDisplayTitle
|
||||
stringValue = strings.Chat_MultipleTypingPair(peerTitle, secondPeerTitle).string
|
||||
} else {
|
||||
stringValue = strings.Chat_MultipleTypingMore(peerTitle, String(inputActivities.count - 1)).string
|
||||
}
|
||||
} else if let (peer, _) = inputActivities.first {
|
||||
stringValue = EnginePeer(peer).compactDisplayTitle
|
||||
}
|
||||
}
|
||||
let color = titleTheme.rootController.navigationBar.accentTextColor
|
||||
let string = NSAttributedString(string: stringValue, font: subtitleFont, textColor: color)
|
||||
switch mergedActivity {
|
||||
case .typingText:
|
||||
state = .typingText(string, color)
|
||||
case .recordingVoice:
|
||||
state = .recordingVoice(string, color)
|
||||
case .recordingInstantVideo:
|
||||
state = .recordingVideo(string, color)
|
||||
case .uploadingFile, .uploadingInstantVideo, .uploadingPhoto, .uploadingVideo:
|
||||
state = .uploading(string, color)
|
||||
case .playingGame:
|
||||
state = .playingGame(string, color)
|
||||
case .speakingInGroupCall, .interactingWithEmoji:
|
||||
state = .typingText(string, color)
|
||||
case .choosingSticker:
|
||||
state = .choosingSticker(string, color)
|
||||
case .seeingEmojiInteraction:
|
||||
state = .choosingSticker(string, color)
|
||||
}
|
||||
} else {
|
||||
if let titleContent = self.titleContent {
|
||||
switch titleContent {
|
||||
case let .peer(peerView, onlineMemberCount, isScheduledMessages):
|
||||
if let peer = peerViewMainPeer(peerView) {
|
||||
let servicePeer = isServicePeer(peer)
|
||||
if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies {
|
||||
let string = NSAttributedString(string: "", font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
} else if let user = peer as? TelegramUser {
|
||||
if user.isDeleted {
|
||||
state = .none
|
||||
} else if servicePeer {
|
||||
let string = NSAttributedString(string: "", font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
} else if user.flags.contains(.isSupport) {
|
||||
let statusText = self.strings.Bot_GenericSupportStatus
|
||||
|
||||
let string = NSAttributedString(string: statusText, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
} else if let _ = user.botInfo {
|
||||
let statusText = self.strings.Bot_GenericBotStatus
|
||||
|
||||
let string = NSAttributedString(string: statusText, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
} else if let peer = peerViewMainPeer(peerView) {
|
||||
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
|
||||
let userPresence: TelegramUserPresence
|
||||
if let presence = peerView.peerPresences[peer.id] as? TelegramUserPresence {
|
||||
userPresence = presence
|
||||
self.presenceManager?.reset(presence: EnginePeer.Presence(presence))
|
||||
} else {
|
||||
userPresence = TelegramUserPresence(status: .none, lastActivity: 0)
|
||||
}
|
||||
let (string, activity) = stringAndActivityForUserPresence(strings: self.strings, dateTimeFormat: self.dateTimeFormat, presence: EnginePeer.Presence(userPresence), relativeTo: Int32(timestamp))
|
||||
let attributedString = NSAttributedString(string: string, font: subtitleFont, textColor: activity ? titleTheme.rootController.navigationBar.accentTextColor : titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(attributedString, activity ? .online : .lastSeenTime)
|
||||
} else {
|
||||
let string = NSAttributedString(string: "", font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
}
|
||||
} else if let group = peer as? TelegramGroup {
|
||||
var onlineCount = 0
|
||||
if let cachedGroupData = peerView.cachedData as? CachedGroupData, let participants = cachedGroupData.participants {
|
||||
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
|
||||
for participant in participants.participants {
|
||||
if let presence = peerView.peerPresences[participant.peerId] as? TelegramUserPresence {
|
||||
let relativeStatus = relativeUserPresenceStatus(EnginePeer.Presence(presence), relativeTo: Int32(timestamp))
|
||||
switch relativeStatus {
|
||||
case .online:
|
||||
onlineCount += 1
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if onlineCount > 1 {
|
||||
let string = NSMutableAttributedString()
|
||||
|
||||
string.append(NSAttributedString(string: "\(strings.Conversation_StatusMembers(Int32(group.participantCount))), ", font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor))
|
||||
string.append(NSAttributedString(string: strings.Conversation_StatusOnline(Int32(onlineCount)), font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor))
|
||||
state = .info(string, .generic)
|
||||
} else {
|
||||
let string = NSAttributedString(string: strings.Conversation_StatusMembers(Int32(group.participantCount)), font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
}
|
||||
} else if let channel = peer as? TelegramChannel {
|
||||
if let cachedChannelData = peerView.cachedData as? CachedChannelData, let memberCount = cachedChannelData.participantsSummary.memberCount {
|
||||
if memberCount == 0 {
|
||||
let string: NSAttributedString
|
||||
if case .group = channel.info {
|
||||
string = NSAttributedString(string: strings.Group_Status, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
} else {
|
||||
string = NSAttributedString(string: strings.Channel_Status, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
}
|
||||
state = .info(string, .generic)
|
||||
} else {
|
||||
if case .group = channel.info, let onlineMemberCount = onlineMemberCount, onlineMemberCount > 1 {
|
||||
let string = NSMutableAttributedString()
|
||||
|
||||
string.append(NSAttributedString(string: "\(strings.Conversation_StatusMembers(Int32(memberCount))), ", font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor))
|
||||
string.append(NSAttributedString(string: strings.Conversation_StatusOnline(Int32(onlineMemberCount)), font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor))
|
||||
state = .info(string, .generic)
|
||||
} else {
|
||||
let membersString: String
|
||||
if case .group = channel.info {
|
||||
membersString = strings.Conversation_StatusMembers(memberCount)
|
||||
} else {
|
||||
membersString = strings.Conversation_StatusSubscribers(memberCount)
|
||||
}
|
||||
let string = NSAttributedString(string: membersString, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch channel.info {
|
||||
case .group:
|
||||
let string = NSAttributedString(string: strings.Group_Status, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
case .broadcast:
|
||||
let string = NSAttributedString(string: strings.Channel_Status, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .custom(_, subtitle?, _):
|
||||
let string = NSAttributedString(string: subtitle, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor)
|
||||
state = .info(string, .generic)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
var accessibilityText = ""
|
||||
for segment in self.titleTextNode.segments {
|
||||
switch segment {
|
||||
case let .number(_, string):
|
||||
accessibilityText.append(string.string)
|
||||
case let .text(_, string):
|
||||
accessibilityText.append(string.string)
|
||||
}
|
||||
}
|
||||
|
||||
self.accessibilityLabel = accessibilityText
|
||||
self.accessibilityValue = state.string
|
||||
} else {
|
||||
self.accessibilityLabel = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.activityNode.transitionToState(state, animation: .slide) {
|
||||
if let (size, clearBounds) = self.validLayout {
|
||||
self.updateLayout(size: size, clearBounds: clearBounds, transition: .animated(duration: 0.3, curve: .spring))
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.nameDisplayOrder = nameDisplayOrder
|
||||
self.animationCache = animationCache
|
||||
self.animationRenderer = animationRenderer
|
||||
|
||||
self.contentContainer = ASDisplayNode()
|
||||
|
||||
self.titleContainerView = PortalSourceView()
|
||||
self.titleTextNode = ImmediateAnimatedCountLabelNode()
|
||||
|
||||
self.titleLeftIconNode = ASImageNode()
|
||||
self.titleLeftIconNode.isLayerBacked = true
|
||||
self.titleLeftIconNode.displayWithoutProcessing = true
|
||||
self.titleLeftIconNode.displaysAsynchronously = false
|
||||
|
||||
self.titleRightIconNode = ASImageNode()
|
||||
self.titleRightIconNode.isLayerBacked = true
|
||||
self.titleRightIconNode.displayWithoutProcessing = true
|
||||
self.titleRightIconNode.displaysAsynchronously = false
|
||||
|
||||
self.titleCredibilityIconView = ComponentHostView()
|
||||
self.titleCredibilityIconView.isUserInteractionEnabled = false
|
||||
|
||||
self.activityNode = ChatTitleActivityNode()
|
||||
self.button = HighlightTrackingButtonNode()
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.isAccessibilityElement = true
|
||||
self.accessibilityTraits = .header
|
||||
|
||||
self.addSubnode(self.contentContainer)
|
||||
self.titleContainerView.addSubnode(self.titleTextNode)
|
||||
self.contentContainer.view.addSubview(self.titleContainerView)
|
||||
self.contentContainer.addSubnode(self.activityNode)
|
||||
self.addSubnode(self.button)
|
||||
|
||||
self.presenceManager = PeerPresenceStatusManager(update: { [weak self] in
|
||||
let _ = self?.updateStatus()
|
||||
})
|
||||
|
||||
self.button.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: [.touchUpInside])
|
||||
self.button.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.titleTextNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.activityNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.titleCredibilityIconView.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.titleTextNode.alpha = 0.4
|
||||
strongSelf.activityNode.alpha = 0.4
|
||||
strongSelf.titleCredibilityIconView.alpha = 0.4
|
||||
} else {
|
||||
strongSelf.titleTextNode.alpha = 1.0
|
||||
strongSelf.activityNode.alpha = 1.0
|
||||
strongSelf.titleCredibilityIconView.alpha = 1.0
|
||||
strongSelf.titleTextNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
strongSelf.activityNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.button.view.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(self.longPressGesture(_:))))
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
if let (size, clearBounds) = self.validLayout {
|
||||
self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, hasEmbeddedTitleContent: Bool) {
|
||||
self.theme = theme
|
||||
self.hasEmbeddedTitleContent = hasEmbeddedTitleContent
|
||||
self.strings = strings
|
||||
|
||||
let titleContent = self.titleContent
|
||||
self.titleCredibilityIcon = .none
|
||||
self.titleContent = titleContent
|
||||
let _ = self.updateStatus()
|
||||
|
||||
if let (size, clearBounds) = self.validLayout {
|
||||
self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, clearBounds)
|
||||
|
||||
self.button.frame = clearBounds
|
||||
self.contentContainer.frame = clearBounds
|
||||
|
||||
var leftIconWidth: CGFloat = 0.0
|
||||
var rightIconWidth: CGFloat = 0.0
|
||||
var credibilityIconWidth: CGFloat = 0.0
|
||||
|
||||
if let image = self.titleLeftIconNode.image {
|
||||
if self.titleLeftIconNode.supernode == nil {
|
||||
self.titleTextNode.addSubnode(self.titleLeftIconNode)
|
||||
}
|
||||
leftIconWidth = image.size.width + 6.0
|
||||
} else if self.titleLeftIconNode.supernode != nil {
|
||||
self.titleLeftIconNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
let titleCredibilityContent: EmojiStatusComponent.Content
|
||||
switch self.titleCredibilityIcon {
|
||||
case .none:
|
||||
titleCredibilityContent = .none
|
||||
case .premium:
|
||||
titleCredibilityContent = .premium(color: self.theme.list.itemAccentColor)
|
||||
case .verified:
|
||||
titleCredibilityContent = .verified(fillColor: self.theme.list.itemCheckColors.fillColor, foregroundColor: self.theme.list.itemCheckColors.foregroundColor, sizeType: .large)
|
||||
case .fake:
|
||||
titleCredibilityContent = .text(color: self.theme.chat.message.incoming.scamColor, string: self.strings.Message_FakeAccount.uppercased())
|
||||
case .scam:
|
||||
titleCredibilityContent = .text(color: self.theme.chat.message.incoming.scamColor, string: self.strings.Message_ScamAccount.uppercased())
|
||||
case let .emojiStatus(emojiStatus):
|
||||
titleCredibilityContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: self.theme.list.mediaPlaceholderColor, themeColor: self.theme.list.itemAccentColor, loopMode: .count(2))
|
||||
}
|
||||
|
||||
let titleCredibilitySize = self.titleCredibilityIconView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(EmojiStatusComponent(
|
||||
context: self.context,
|
||||
animationCache: self.animationCache,
|
||||
animationRenderer: self.animationRenderer,
|
||||
content: titleCredibilityContent,
|
||||
isVisibleForAnimations: true,
|
||||
action: nil
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 20.0, height: 20.0)
|
||||
)
|
||||
|
||||
if self.titleCredibilityIcon != .none {
|
||||
self.titleTextNode.view.addSubview(self.titleCredibilityIconView)
|
||||
credibilityIconWidth = titleCredibilitySize.width + 3.0
|
||||
} else {
|
||||
if self.titleCredibilityIconView.superview != nil {
|
||||
self.titleCredibilityIconView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
if let image = self.titleRightIconNode.image {
|
||||
if self.titleRightIconNode.supernode == nil {
|
||||
self.titleTextNode.addSubnode(self.titleRightIconNode)
|
||||
}
|
||||
rightIconWidth = image.size.width + 3.0
|
||||
} else if self.titleRightIconNode.supernode != nil {
|
||||
self.titleRightIconNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
let titleSideInset: CGFloat = 3.0
|
||||
var titleFrame: CGRect
|
||||
if size.height > 40.0 {
|
||||
var titleSize = self.titleTextNode.updateLayout(size: CGSize(width: clearBounds.width - leftIconWidth - credibilityIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height), animated: transition.isAnimated)
|
||||
titleSize.width += credibilityIconWidth
|
||||
let activitySize = self.activityNode.updateLayout(clearBounds.size, alignment: .center)
|
||||
let titleInfoSpacing: CGFloat = 0.0
|
||||
|
||||
if activitySize.height.isZero {
|
||||
titleFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - titleSize.width) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
|
||||
if titleFrame.size.width < size.width {
|
||||
titleFrame.origin.x = -clearBounds.minX + floor((size.width - titleFrame.width) / 2.0)
|
||||
}
|
||||
transition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame)
|
||||
transition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size))
|
||||
} else {
|
||||
let combinedHeight = titleSize.height + activitySize.height + titleInfoSpacing
|
||||
|
||||
titleFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - titleSize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0)), size: titleSize)
|
||||
if titleFrame.size.width < size.width {
|
||||
titleFrame.origin.x = -clearBounds.minX + floor((size.width - titleFrame.width) / 2.0)
|
||||
}
|
||||
titleFrame.origin.x = max(titleFrame.origin.x, clearBounds.minX + leftIconWidth)
|
||||
transition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame)
|
||||
transition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size))
|
||||
|
||||
var activityFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - activitySize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0) + titleSize.height + titleInfoSpacing), size: activitySize)
|
||||
if activitySize.width < size.width {
|
||||
activityFrame.origin.x = -clearBounds.minX + floor((size.width - activityFrame.width) / 2.0)
|
||||
}
|
||||
self.activityNode.frame = activityFrame
|
||||
}
|
||||
|
||||
if let image = self.titleLeftIconNode.image {
|
||||
self.titleLeftIconNode.frame = CGRect(origin: CGPoint(x: -image.size.width - 3.0 - UIScreenPixel, y: 4.0), size: image.size)
|
||||
}
|
||||
|
||||
self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: titleFrame.width - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize)
|
||||
|
||||
if let image = self.titleRightIconNode.image {
|
||||
self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.width + 3.0 + UIScreenPixel, y: 6.0), size: image.size)
|
||||
}
|
||||
} else {
|
||||
let titleSize = self.titleTextNode.updateLayout(size: CGSize(width: floor(clearBounds.width / 2.0 - leftIconWidth - credibilityIconWidth - rightIconWidth - titleSideInset * 2.0), height: size.height), animated: transition.isAnimated)
|
||||
let activitySize = self.activityNode.updateLayout(CGSize(width: floor(clearBounds.width / 2.0), height: size.height), alignment: .center)
|
||||
|
||||
let titleInfoSpacing: CGFloat = 8.0
|
||||
let combinedWidth = titleSize.width + leftIconWidth + credibilityIconWidth + rightIconWidth + activitySize.width + titleInfoSpacing
|
||||
|
||||
titleFrame = CGRect(origin: CGPoint(x: leftIconWidth + floor((clearBounds.width - combinedWidth) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
|
||||
|
||||
transition.updateFrameAdditiveToCenter(view: self.titleContainerView, frame: titleFrame)
|
||||
transition.updateFrameAdditiveToCenter(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size))
|
||||
|
||||
self.activityNode.frame = CGRect(origin: CGPoint(x: floor((clearBounds.width - combinedWidth) / 2.0 + titleSize.width + leftIconWidth + credibilityIconWidth + rightIconWidth + titleInfoSpacing), y: floor((size.height - activitySize.height) / 2.0)), size: activitySize)
|
||||
|
||||
if let image = self.titleLeftIconNode.image {
|
||||
self.titleLeftIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.minY + 4.0), size: image.size)
|
||||
}
|
||||
|
||||
self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: titleFrame.maxX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize)
|
||||
|
||||
if let image = self.titleRightIconNode.image {
|
||||
self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX - image.size.width, y: titleFrame.minY + 6.0), size: image.size)
|
||||
}
|
||||
}
|
||||
|
||||
self.pointerInteraction = PointerInteraction(view: self, style: .rectangle(CGSize(width: titleFrame.width + 16.0, height: 40.0)))
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
self.pressed?()
|
||||
}
|
||||
|
||||
@objc private func longPressGesture(_ gesture: UILongPressGestureRecognizer) {
|
||||
switch gesture.state {
|
||||
case .began:
|
||||
self.longPressed?()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func animateLayoutTransition() {
|
||||
UIView.transition(with: self, duration: 0.25, options: [.transitionCrossDissolve], animations: {
|
||||
}, completion: nil)
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if !self.isUserInteractionEnabled {
|
||||
return nil
|
||||
}
|
||||
if self.button.frame.contains(point) {
|
||||
return self.button.view
|
||||
}
|
||||
return super.hitTest(point, with: event)
|
||||
}
|
||||
|
||||
final class SnapshotState {
|
||||
fileprivate let snapshotView: UIView
|
||||
|
||||
fileprivate init(snapshotView: UIView) {
|
||||
self.snapshotView = snapshotView
|
||||
}
|
||||
}
|
||||
|
||||
func prepareSnapshotState() -> SnapshotState {
|
||||
let snapshotView = self.snapshotView(afterScreenUpdates: false)!
|
||||
return SnapshotState(
|
||||
snapshotView: snapshotView
|
||||
)
|
||||
}
|
||||
|
||||
func animateFromSnapshot(_ snapshotState: SnapshotState) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
self.layer.animatePosition(from: CGPoint(x: 0.0, y: 20.0), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true, additive: true)
|
||||
|
||||
snapshotState.snapshotView.frame = self.frame
|
||||
self.superview?.insertSubview(snapshotState.snapshotView, belowSubview: self)
|
||||
|
||||
let snapshotView = snapshotState.snapshotView
|
||||
snapshotState.snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
||||
snapshotView?.removeFromSuperview()
|
||||
})
|
||||
snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -20.0), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
||||
var selectStickerImpl: ((FileMediaReference, UIView, CGRect) -> Bool)?
|
||||
|
||||
self.controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in
|
||||
return false }, openPeer: { _, _, _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in
|
||||
return false }, openPeer: { _, _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in
|
||||
}, updateMessageReaction: { _, _ in }, activateMessagePinch: { _ in
|
||||
}, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
|
||||
}, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { fileReference, _, _, _, _, node, rect, _, _ in return selectStickerImpl?(fileReference, node, rect) ?? false }, sendEmoji: { _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||
|
@ -45,7 +45,7 @@ private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatContr
|
||||
}
|
||||
}
|
||||
|
||||
func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, forceExternal: Bool, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)? = nil, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?) {
|
||||
func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, forceExternal: Bool, openPeer: @escaping (EnginePeer, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)? = nil, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?) {
|
||||
let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
|
||||
if case let .chat(_, maybeUpdatedPresentationData) = urlContext {
|
||||
updatedPresentationData = maybeUpdatedPresentationData
|
||||
@ -60,16 +60,16 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
requestMessageActionUrlAuth?(.url(url))
|
||||
dismissInput()
|
||||
break
|
||||
case let .peer(peerId, navigation):
|
||||
if let peerId = peerId {
|
||||
openPeer(peerId, defaultNavigationForPeerId(peerId, navigation: navigation))
|
||||
case let .peer(peer, navigation):
|
||||
if let peer = peer {
|
||||
openPeer(EnginePeer(peer), defaultNavigationForPeerId(peer.id, navigation: navigation))
|
||||
} else {
|
||||
present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Resolve_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
}
|
||||
case .inaccessiblePeer:
|
||||
present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Conversation_ErrorInaccessibleMessage, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
case let .botStart(peerId, payload):
|
||||
openPeer(peerId, .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .interactive)))
|
||||
case let .botStart(peer, payload):
|
||||
openPeer(EnginePeer(peer), .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .interactive)))
|
||||
case let .groupBotStart(botPeerId, payload, adminRights):
|
||||
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.onlyGroupsAndChannels, .onlyManageable, .excludeDisabled, .excludeRecent, .doNotSearchMessages], hasContactSelector: false, title: presentationData.strings.Bot_AddToChat_Title))
|
||||
controller.peerSelected = { [weak controller] peer in
|
||||
@ -155,8 +155,8 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
}
|
||||
dismissInput()
|
||||
navigationController?.pushViewController(controller)
|
||||
case let .channelMessage(peerId, messageId, timecode):
|
||||
openPeer(peerId, .chat(textInputState: nil, subject: .message(id: .id(messageId), highlight: true, timecode: timecode), peekData: nil))
|
||||
case let .channelMessage(peer, messageId, timecode):
|
||||
openPeer(EnginePeer(peer), .chat(textInputState: nil, subject: .message(id: .id(messageId), highlight: true, timecode: timecode), peekData: nil))
|
||||
case let .replyThreadMessage(replyThreadMessage, messageId):
|
||||
if let navigationController = navigationController {
|
||||
let _ = ChatControllerImpl.openMessageReplies(context: context, navigationController: navigationController, present: { c, a in
|
||||
@ -196,8 +196,8 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
navigationController?.pushViewController(InstantPageController(context: context, webPage: webpage, sourcePeerType: .channel, anchor: anchor))
|
||||
case let .join(link):
|
||||
dismissInput()
|
||||
present(JoinLinkPreviewController(context: context, link: link, navigateToPeer: { peerId, peekData in
|
||||
openPeer(peerId, .chat(textInputState: nil, subject: nil, peekData: peekData))
|
||||
present(JoinLinkPreviewController(context: context, link: link, navigateToPeer: { peer, peekData in
|
||||
openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: peekData))
|
||||
}, parentNavigationController: navigationController), nil)
|
||||
case let .localization(identifier):
|
||||
dismissInput()
|
||||
|
@ -190,30 +190,27 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
||||
if case let .externalUrl(value) = resolved {
|
||||
context.sharedContext.applicationBindings.openUrl(value)
|
||||
} else {
|
||||
context.sharedContext.openResolvedUrl(resolved, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peerId, navigation in
|
||||
context.sharedContext.openResolvedUrl(resolved, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peer, navigation in
|
||||
switch navigation {
|
||||
case .info:
|
||||
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
||||
context.sharedContext.applicationBindings.dismissNativeController()
|
||||
navigationController?.pushViewController(infoController)
|
||||
}
|
||||
})
|
||||
if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
||||
context.sharedContext.applicationBindings.dismissNativeController()
|
||||
navigationController?.pushViewController(infoController)
|
||||
}
|
||||
case let .chat(_, subject, peekData):
|
||||
context.sharedContext.applicationBindings.dismissNativeController()
|
||||
if let navigationController = navigationController {
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId), subject: subject, peekData: peekData))
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id), subject: subject, peekData: peekData))
|
||||
}
|
||||
case let .withBotStartPayload(payload):
|
||||
context.sharedContext.applicationBindings.dismissNativeController()
|
||||
if let navigationController = navigationController {
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId), botStart: payload))
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id), botStart: payload))
|
||||
}
|
||||
case let .withAttachBot(attachBotStart):
|
||||
context.sharedContext.applicationBindings.dismissNativeController()
|
||||
if let navigationController = navigationController {
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId), attachBotStart: attachBotStart))
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id), attachBotStart: attachBotStart))
|
||||
}
|
||||
default:
|
||||
break
|
||||
|
@ -67,7 +67,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}, openPeer: { _, _, _, _, _ in
|
||||
}, openPeer: { _, _, _, _ in
|
||||
}, openPeerMention: { _ in
|
||||
}, openMessageContextMenu: { _, _, _, _, _, _ in
|
||||
}, openMessageReactionContextMenu: { _, _, _, _ in
|
||||
|
@ -186,7 +186,7 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
||||
}
|
||||
}
|
||||
let transaction = preparedTransition(from: self.currentEntries, to: entries, context: self.context, presentationData: presentationData, openPeer: { [weak self] peer in
|
||||
self?.chatControllerInteraction.openPeer(peer.id, .default, nil, false, nil)
|
||||
self?.chatControllerInteraction.openPeer(EnginePeer(peer), .default, nil, false)
|
||||
}, openPeerContextAction: { [weak self] peer, node, gesture in
|
||||
self?.openPeerContextAction(peer, node, gesture)
|
||||
})
|
||||
|
@ -821,6 +821,9 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
}
|
||||
|
||||
var availablePanes = availablePanes
|
||||
if let channel = peerView.peers[peerView.peerId] as? TelegramChannel, channel.flags.contains(.isForum) {
|
||||
availablePanes?.removeAll()
|
||||
}
|
||||
if let membersData = membersData, case .longList = membersData {
|
||||
if availablePanes != nil {
|
||||
availablePanes?.insert(.members, at: 0)
|
||||
|
@ -27,6 +27,7 @@ import EmojiStatusComponent
|
||||
import AnimationCache
|
||||
import MultiAnimationRenderer
|
||||
import ComponentDisplayAdapters
|
||||
import ChatTitleView
|
||||
|
||||
enum PeerInfoHeaderButtonKey: Hashable {
|
||||
case message
|
||||
@ -407,7 +408,17 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||
self.containerNode.isGestureEnabled = false
|
||||
}
|
||||
|
||||
self.avatarNode.setPeer(context: self.context, theme: theme, peer: EnginePeer(peer), overrideImage: overrideImage, synchronousLoad: self.isFirstAvatarLoading, displayDimensions: CGSize(width: avatarSize, height: avatarSize), storeUnrounded: true)
|
||||
self.avatarNode.setPeer(context: self.context, theme: theme, peer: EnginePeer(peer), overrideImage: overrideImage, clipStyle: .none, synchronousLoad: self.isFirstAvatarLoading, displayDimensions: CGSize(width: avatarSize, height: avatarSize), storeUnrounded: true)
|
||||
|
||||
let avatarCornerRadius: CGFloat
|
||||
if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) {
|
||||
avatarCornerRadius = floor(avatarSize * 0.25)
|
||||
} else {
|
||||
avatarCornerRadius = avatarSize / 2.0
|
||||
}
|
||||
self.avatarNode.layer.cornerRadius = avatarCornerRadius
|
||||
self.avatarNode.layer.masksToBounds = true
|
||||
|
||||
self.isFirstAvatarLoading = false
|
||||
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize))
|
||||
@ -708,9 +719,18 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
|
||||
overrideImage = item == nil && canEdit ? .editAvatarIcon(forceNone: true) : nil
|
||||
}
|
||||
self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0))
|
||||
self.avatarNode.setPeer(context: self.context, theme: theme, peer: EnginePeer(peer), overrideImage: overrideImage, synchronousLoad: false, displayDimensions: CGSize(width: avatarSize, height: avatarSize))
|
||||
self.avatarNode.setPeer(context: self.context, theme: theme, peer: EnginePeer(peer), overrideImage: overrideImage, clipStyle: .none, synchronousLoad: false, displayDimensions: CGSize(width: avatarSize, height: avatarSize))
|
||||
self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize))
|
||||
|
||||
let avatarCornerRadius: CGFloat
|
||||
if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) {
|
||||
avatarCornerRadius = floor(avatarSize * 0.25)
|
||||
} else {
|
||||
avatarCornerRadius = avatarSize / 2.0
|
||||
}
|
||||
self.avatarNode.layer.cornerRadius = avatarCornerRadius
|
||||
self.avatarNode.layer.masksToBounds = true
|
||||
|
||||
if let item = item {
|
||||
let representations: [ImageRepresentationWithReference]
|
||||
let videoRepresentations: [VideoRepresentationWithReference]
|
||||
@ -2785,6 +2805,13 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
avatarScale = 1.0 * (1.0 - titleCollapseFraction) + avatarMinScale * titleCollapseFraction
|
||||
avatarOffset = apparentTitleLockOffset + 0.0 * (1.0 - titleCollapseFraction) + 10.0 * titleCollapseFraction
|
||||
}
|
||||
|
||||
let avatarCornerRadius: CGFloat
|
||||
if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) {
|
||||
avatarCornerRadius = floor(avatarSize * 0.25)
|
||||
} else {
|
||||
avatarCornerRadius = avatarSize / 2.0
|
||||
}
|
||||
|
||||
if self.isAvatarExpanded {
|
||||
self.avatarListNode.listContainerNode.isHidden = false
|
||||
@ -2795,9 +2822,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
transition.updateCornerRadius(node: self.avatarListNode.listContainerNode, cornerRadius: 0.0)
|
||||
transition.updateCornerRadius(node: self.avatarListNode.listContainerNode.controlsClippingNode, cornerRadius: 0.0)
|
||||
}
|
||||
} else if self.avatarListNode.listContainerNode.cornerRadius != avatarSize / 2.0 {
|
||||
transition.updateCornerRadius(node: self.avatarListNode.listContainerNode.controlsClippingNode, cornerRadius: avatarSize / 2.0)
|
||||
transition.updateCornerRadius(node: self.avatarListNode.listContainerNode, cornerRadius: avatarSize / 2.0, completion: { [weak self] _ in
|
||||
} else if self.avatarListNode.listContainerNode.cornerRadius != avatarCornerRadius {
|
||||
transition.updateCornerRadius(node: self.avatarListNode.listContainerNode.controlsClippingNode, cornerRadius: avatarCornerRadius)
|
||||
transition.updateCornerRadius(node: self.avatarListNode.listContainerNode, cornerRadius: avatarCornerRadius, completion: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ import EntityKeyboard
|
||||
import AvatarNode
|
||||
import ComponentFlow
|
||||
import EmojiStatusComponent
|
||||
import ChatTitleView
|
||||
|
||||
protocol PeerInfoScreenItem: AnyObject {
|
||||
var id: AnyHashable { get }
|
||||
@ -502,6 +503,7 @@ private final class PeerInfoInteraction {
|
||||
let openQrCode: () -> Void
|
||||
let editingOpenReactionsSetup: () -> Void
|
||||
let dismissInput: () -> Void
|
||||
let toggleForumTopics: (Bool) -> Void
|
||||
|
||||
init(
|
||||
openUsername: @escaping (String) -> Void,
|
||||
@ -545,7 +547,8 @@ private final class PeerInfoInteraction {
|
||||
openAddMember: @escaping () -> Void,
|
||||
openQrCode: @escaping () -> Void,
|
||||
editingOpenReactionsSetup: @escaping () -> Void,
|
||||
dismissInput: @escaping () -> Void
|
||||
dismissInput: @escaping () -> Void,
|
||||
toggleForumTopics: @escaping (Bool) -> Void
|
||||
) {
|
||||
self.openUsername = openUsername
|
||||
self.openPhone = openPhone
|
||||
@ -589,6 +592,7 @@ private final class PeerInfoInteraction {
|
||||
self.openQrCode = openQrCode
|
||||
self.editingOpenReactionsSetup = editingOpenReactionsSetup
|
||||
self.dismissInput = dismissInput
|
||||
self.toggleForumTopics = toggleForumTopics
|
||||
}
|
||||
}
|
||||
|
||||
@ -1246,6 +1250,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
||||
case notifications
|
||||
case groupLocation
|
||||
case peerPublicSettings
|
||||
case peerDataSettings
|
||||
case peerSettings
|
||||
case peerAdditionalSettings
|
||||
case peerActions
|
||||
@ -1419,6 +1424,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
||||
let ItemLocationSetup = 113
|
||||
let ItemDeleteGroup = 114
|
||||
let ItemReactions = 115
|
||||
let ItemTopics = 116
|
||||
|
||||
let isCreator = channel.flags.contains(.isCreator)
|
||||
let isPublic = channel.username != nil
|
||||
@ -1474,7 +1480,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
||||
invitesText = ""
|
||||
}
|
||||
|
||||
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, icon: UIImage(bundleImageName: "Chat/Info/GroupLinksIcon"), action: {
|
||||
items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, icon: UIImage(bundleImageName: "Chat/Info/GroupLinksIcon"), action: {
|
||||
interaction.editingOpenInviteLinksSetup()
|
||||
}))
|
||||
}
|
||||
@ -1487,7 +1493,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
||||
} else {
|
||||
peerTitle = EnginePeer(linkedDiscussionPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
}
|
||||
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemLinkedChannel, label: .text(peerTitle), text: presentationData.strings.Group_LinkedChannel, icon: UIImage(bundleImageName: "Chat/Info/GroupLinkedChannelIcon"), action: {
|
||||
items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemLinkedChannel, label: .text(peerTitle), text: presentationData.strings.Group_LinkedChannel, icon: UIImage(bundleImageName: "Chat/Info/GroupLinkedChannelIcon"), action: {
|
||||
interaction.editingOpenDiscussionGroupSetup()
|
||||
}))
|
||||
}
|
||||
@ -1506,12 +1512,12 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
||||
} else {
|
||||
label = ""
|
||||
}
|
||||
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: {
|
||||
items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: {
|
||||
interaction.editingOpenReactionsSetup()
|
||||
}))
|
||||
}
|
||||
|
||||
if !isPublic, case .known(nil) = cachedData.linkedDiscussionPeerId {
|
||||
if !isPublic, case .known(nil) = cachedData.linkedDiscussionPeerId, !channel.flags.contains(.isForum){
|
||||
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPreHistory, label: .text(cachedData.flags.contains(.preHistoryEnabled) ? presentationData.strings.GroupInfo_GroupHistoryVisible : presentationData.strings.GroupInfo_GroupHistoryHidden), text: presentationData.strings.GroupInfo_GroupHistoryShort, icon: UIImage(bundleImageName: "Chat/Info/GroupDiscussionIcon"), action: {
|
||||
interaction.editingOpenPreHistorySetup()
|
||||
}))
|
||||
@ -1531,18 +1537,25 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr
|
||||
} else {
|
||||
label = ""
|
||||
}
|
||||
items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: {
|
||||
items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: {
|
||||
interaction.editingOpenReactionsSetup()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if cachedData.flags.contains(.canSetStickerSet) && canEditPeerInfo(context: context, peer: channel) {
|
||||
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemStickerPack, label: .text(cachedData.stickerPack?.title ?? presentationData.strings.GroupInfo_SharedMediaNone), text: presentationData.strings.Stickers_GroupStickers, icon: UIImage(bundleImageName: "Settings/Menu/Stickers"), action: {
|
||||
items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemStickerPack, label: .text(cachedData.stickerPack?.title ?? presentationData.strings.GroupInfo_SharedMediaNone), text: presentationData.strings.Stickers_GroupStickers, icon: UIImage(bundleImageName: "Settings/Menu/Stickers"), action: {
|
||||
interaction.editingOpenStickerPackSetup()
|
||||
}))
|
||||
}
|
||||
|
||||
if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) {
|
||||
//TODO:localize
|
||||
items[.peerDataSettings]!.append(PeerInfoScreenSwitchItem(id: ItemTopics, text: "Topics", value: channel.flags.contains(.isForum), icon: UIImage(bundleImageName: "Settings/Menu/ChatListFilters"), toggled: { value in
|
||||
interaction.toggleForumTopics(value)
|
||||
}))
|
||||
}
|
||||
|
||||
var canViewAdminsAndBanned = false
|
||||
if let _ = channel.adminRights {
|
||||
canViewAdminsAndBanned = true
|
||||
@ -1957,6 +1970,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
},
|
||||
dismissInput: { [weak self] in
|
||||
self?.view.endEditing(true)
|
||||
},
|
||||
toggleForumTopics: { [weak self] value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let _ = strongSelf.context.engine.peers.setChannelForumMode(id: strongSelf.peerId, isForum: value).start()
|
||||
}
|
||||
)
|
||||
|
||||
@ -1965,10 +1984,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
return false
|
||||
}
|
||||
return strongSelf.openMessage(id: message.id)
|
||||
}, openPeer: { [weak self] id, navigation, _, _, _ in
|
||||
if let id = id {
|
||||
self?.openPeer(peerId: id, navigation: navigation)
|
||||
}
|
||||
}, openPeer: { [weak self] peer, navigation, _, _ in
|
||||
self?.openPeer(peerId: peer.id, navigation: navigation)
|
||||
}, openPeerMention: { _ in
|
||||
}, openMessageContextMenu: { [weak self] message, _, node, frame, anyRecognizer, _ in
|
||||
guard let strongSelf = self, let node = node as? ContextExtractedContentContainingNode else {
|
||||
@ -2495,7 +2512,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
let items: [ContextMenuItem] = [
|
||||
.action(ContextMenuActionItem(text: presentationData.strings.Conversation_LinkDialogOpen, icon: { _ in nil }, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
self?.chatInterfaceInteraction.openPeer(peer.id, .default, nil, false, nil)
|
||||
self?.chatInterfaceInteraction.openPeer(EnginePeer(peer), .default, nil, false)
|
||||
}))
|
||||
]
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
@ -3493,9 +3510,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
if let strongSelf = self {
|
||||
strongSelf.openPeerMention(mention)
|
||||
}
|
||||
}, openPeer: { [weak self] peerId in
|
||||
}, openPeer: { [weak self] peer in
|
||||
if let strongSelf = self {
|
||||
strongSelf.openPeer(peerId: peerId, navigation: .default)
|
||||
strongSelf.openPeer(peerId: peer.id, navigation: .default)
|
||||
}
|
||||
}, openHashtag: { [weak self] peerName, hashtag in
|
||||
if let strongSelf = self {
|
||||
@ -3581,27 +3598,23 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
guard let navigationController = self.controller?.navigationController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .chat(peerId: self.peerId, updatedPresentationData: self.controller?.updatedPresentationData), navigationController: navigationController, forceExternal: false, openPeer: { [weak self] peerId, navigation in
|
||||
self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .chat(peerId: self.peerId, updatedPresentationData: self.controller?.updatedPresentationData), navigationController: navigationController, forceExternal: false, openPeer: { [weak self] peer, navigation in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
switch navigation {
|
||||
case let .chat(_, subject, peekData):
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), subject: subject, keepStack: .always, peekData: peekData))
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), subject: subject, keepStack: .always, peekData: peekData))
|
||||
case .info:
|
||||
strongSelf.navigationActionDisposable.set((strongSelf.context.account.postbox.loadedPeerWithId(peerId)
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
if let strongSelf = self, peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil {
|
||||
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
||||
strongSelf.controller?.push(infoController)
|
||||
}
|
||||
if let strongSelf = self, peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil {
|
||||
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
||||
strongSelf.controller?.push(infoController)
|
||||
}
|
||||
}))
|
||||
}
|
||||
case let .withBotStartPayload(startPayload):
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), botStart: startPayload))
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), botStart: startPayload))
|
||||
case let .withAttachBot(attachBotStart):
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peerId), attachBotStart: attachBotStart))
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(id: peer.id), attachBotStart: attachBotStart))
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -3628,8 +3641,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
|
||||
let result: ResolvedUrl = external ? .externalUrl(url) : tempResolved
|
||||
|
||||
strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, forceExternal: false, openPeer: { peerId, navigation in
|
||||
self?.openPeer(peerId: peerId, navigation: navigation)
|
||||
strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, forceExternal: false, openPeer: { peer, navigation in
|
||||
self?.openPeer(peerId: peer.id, navigation: navigation)
|
||||
}, sendFile: nil,
|
||||
sendSticker: nil,
|
||||
requestMessageActionUrlAuth: nil,
|
||||
@ -3735,7 +3748,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
navigation = .chat(textInputState: nil, subject: nil, peekData: nil)
|
||||
}
|
||||
}
|
||||
strongSelf.openResolved(.peer(peer.id, navigation))
|
||||
strongSelf.openResolved(.peer(peer, navigation))
|
||||
} else {
|
||||
strongSelf.controller?.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Resolve_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
@ -6746,7 +6759,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
})
|
||||
if let selectedAccount = selectedAccount {
|
||||
let accountContext = self.context.sharedContext.makeTempAccountContext(account: selectedAccount)
|
||||
let chatListController = accountContext.sharedContext.makeChatListController(context: accountContext, groupId: .root, controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false)
|
||||
let chatListController = accountContext.sharedContext.makeChatListController(context: accountContext, location: .chatList(groupId: EngineChatList.Group(.root)), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false)
|
||||
|
||||
let contextController = ContextController(account: accountContext.account, presentationData: self.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node)), items: accountContextMenuItems(context: accountContext, logout: { [weak self] in
|
||||
self?.logoutAccount(id: id)
|
||||
|
@ -1210,7 +1210,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return resolveUrlImpl(context: context, peerId: peerId, url: url, skipUrlAuth: skipUrlAuth)
|
||||
}
|
||||
|
||||
public func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, forceExternal: Bool, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?) {
|
||||
public func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, forceExternal: Bool, openPeer: @escaping (EnginePeer, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?) {
|
||||
openResolvedUrlImpl(resolvedUrl, context: context, urlContext: urlContext, navigationController: navigationController, forceExternal: forceExternal, openPeer: openPeer, sendFile: sendFile, sendSticker: sendSticker, requestMessageActionUrlAuth: requestMessageActionUrlAuth, joinVoiceChat: joinVoiceChat, present: present, dismissInput: dismissInput, contentContext: contentContext)
|
||||
}
|
||||
|
||||
@ -1270,8 +1270,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return createGroupControllerImpl(context: context, peerIds: peerIds, initialTitle: initialTitle, mode: mode, completion: completion)
|
||||
}
|
||||
|
||||
public func makeChatListController(context: AccountContext, groupId: PeerGroupId, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool, previewing: Bool, enableDebugActions: Bool) -> ChatListController {
|
||||
return ChatListControllerImpl(context: context, location: .chatList(groupId: EngineChatList.Group(groupId)), controlsHistoryPreload: controlsHistoryPreload, hideNetworkActivityStatus: hideNetworkActivityStatus, previewing: previewing, enableDebugActions: enableDebugActions)
|
||||
public func makeChatListController(context: AccountContext, location: ChatListControllerLocation, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool, previewing: Bool, enableDebugActions: Bool) -> ChatListController {
|
||||
return ChatListControllerImpl(context: context, location: location, controlsHistoryPreload: controlsHistoryPreload, hideNetworkActivityStatus: hideNetworkActivityStatus, previewing: previewing, enableDebugActions: enableDebugActions)
|
||||
}
|
||||
|
||||
public func makePeerSelectionController(_ params: PeerSelectionControllerParams) -> PeerSelectionController {
|
||||
@ -1286,7 +1286,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
let controllerInteraction: ChatControllerInteraction
|
||||
|
||||
controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in
|
||||
return false }, openPeer: { _, _, _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in
|
||||
return false }, openPeer: { _, _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in
|
||||
}, updateMessageReaction: { _, _ in }, activateMessagePinch: { _ in
|
||||
}, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
|
||||
}, tapMessage: { message in
|
||||
|
@ -96,7 +96,7 @@ public final class TelegramRootController: NavigationController {
|
||||
public func addRootControllers(showCallsTab: Bool) {
|
||||
let tabBarController = TabBarControllerImpl(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), theme: TabBarControllerTheme(rootControllerTheme: self.presentationData.theme))
|
||||
tabBarController.navigationPresentation = .master
|
||||
let chatListController = self.context.sharedContext.makeChatListController(context: self.context, groupId: .root, controlsHistoryPreload: true, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: !GlobalExperimentalSettings.isAppStoreBuild)
|
||||
let chatListController = self.context.sharedContext.makeChatListController(context: self.context, location: .chatList(groupId: .root), controlsHistoryPreload: true, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: !GlobalExperimentalSettings.isAppStoreBuild)
|
||||
if let sharedContext = self.context.sharedContext as? SharedAccountContextImpl {
|
||||
chatListController.tabBarItem.badgeValue = sharedContext.switchingData.chatListBadge
|
||||
}
|
||||
|
@ -22,16 +22,19 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate
|
||||
controller.present(controllerToPresent, in: .window(.root))
|
||||
}
|
||||
|
||||
let openResolvedPeerImpl: (PeerId?, ChatControllerInteractionNavigateToPeer) -> Void = { [weak controller] peerId, navigation in
|
||||
context.sharedContext.openResolvedUrl(.peer(peerId, navigation), context: context, urlContext: .generic, navigationController: (controller?.navigationController as? NavigationController), forceExternal: false, openPeer: { (peerId, navigation) in
|
||||
let openResolvedPeerImpl: (EnginePeer?, ChatControllerInteractionNavigateToPeer) -> Void = { [weak controller] peer, navigation in
|
||||
guard let peer = peer else {
|
||||
return
|
||||
}
|
||||
context.sharedContext.openResolvedUrl(.peer(peer._asPeer(), navigation), context: context, urlContext: .generic, navigationController: (controller?.navigationController as? NavigationController), forceExternal: false, openPeer: { (peer, navigation) in
|
||||
switch navigation {
|
||||
case let .chat(_, subject, peekData):
|
||||
if let navigationController = controller?.navigationController as? NavigationController {
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId), subject: subject, keepStack: .always, peekData: peekData))
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id), subject: subject, keepStack: .always, peekData: peekData))
|
||||
}
|
||||
case .info:
|
||||
let peerSignal: Signal<Peer?, NoError>
|
||||
peerSignal = context.account.postbox.loadedPeerWithId(peerId) |> map(Optional.init)
|
||||
peerSignal = context.account.postbox.loadedPeerWithId(peer.id) |> map(Optional.init)
|
||||
navigateDisposable.set((peerSignal |> take(1) |> deliverOnMainQueue).start(next: { peer in
|
||||
if let controller = controller, let peer = peer {
|
||||
if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
||||
@ -55,11 +58,11 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate
|
||||
switch result {
|
||||
case let .externalUrl(url):
|
||||
context.sharedContext.applicationBindings.openUrl(url)
|
||||
case let .peer(peerId, navigation):
|
||||
openResolvedPeerImpl(peerId, navigation)
|
||||
case let .channelMessage(peerId, messageId, timecode):
|
||||
case let .peer(peer, navigation):
|
||||
openResolvedPeerImpl(peer.flatMap(EnginePeer.init), navigation)
|
||||
case let .channelMessage(peer, messageId, timecode):
|
||||
if let navigationController = controller.navigationController as? NavigationController {
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId), subject: .message(id: .id(messageId), highlight: true, timecode: timecode)))
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peer.id), subject: .message(id: .id(messageId), highlight: true, timecode: timecode)))
|
||||
}
|
||||
case let .replyThreadMessage(replyThreadMessage, messageId):
|
||||
if let navigationController = controller.navigationController as? NavigationController {
|
||||
@ -73,8 +76,8 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate
|
||||
case let .instantView(webpage, anchor):
|
||||
(controller.navigationController as? NavigationController)?.pushViewController(InstantPageController(context: context, webPage: webpage, sourcePeerType: .group, anchor: anchor))
|
||||
case let .join(link):
|
||||
controller.present(JoinLinkPreviewController(context: context, link: link, navigateToPeer: { peerId, peekData in
|
||||
openResolvedPeerImpl(peerId, .chat(textInputState: nil, subject: nil, peekData: peekData))
|
||||
controller.present(JoinLinkPreviewController(context: context, link: link, navigateToPeer: { peer, peekData in
|
||||
openResolvedPeerImpl(peer, .chat(textInputState: nil, subject: nil, peekData: peekData))
|
||||
}, parentNavigationController: controller.navigationController as? NavigationController), in: .window(.root))
|
||||
default:
|
||||
break
|
||||
@ -85,7 +88,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate
|
||||
|
||||
let openPeerMentionImpl: (String) -> Void = { mention in
|
||||
navigateDisposable.set((context.engine.peers.resolvePeerByName(name: mention, ageLimit: 10) |> take(1) |> deliverOnMainQueue).start(next: { peer in
|
||||
openResolvedPeerImpl(peer?.id, .default)
|
||||
openResolvedPeerImpl(peer, .default)
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -477,13 +477,13 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
||||
|> take(1)
|
||||
|> map { botPeer -> ResolvedUrl? in
|
||||
if let botPeer = botPeer?._asPeer() {
|
||||
return .peer(peer.id, .withAttachBot(ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: startAttach, justInstalled: false)))
|
||||
return .peer(peer, .withAttachBot(ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: startAttach, justInstalled: false)))
|
||||
} else {
|
||||
return .peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil))
|
||||
return .peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(.peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
return .single(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
}
|
||||
} else {
|
||||
return .single(.peer(nil, .info))
|
||||
@ -500,7 +500,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
||||
if let parameter = parameter {
|
||||
switch parameter {
|
||||
case let .botStart(payload):
|
||||
return .single(.botStart(peerId: peer.id, payload: payload))
|
||||
return .single(.botStart(peer: peer, payload: payload))
|
||||
case let .groupBotStart(payload, adminRights):
|
||||
return .single(.groupBotStart(peerId: peer.id, payload: payload, adminRights: adminRights))
|
||||
case let .attachBotStart(name, payload):
|
||||
@ -511,13 +511,13 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
||||
}
|
||||
|> mapToSignal { botPeer -> Signal<ResolvedUrl?, NoError> in
|
||||
if let botPeer = botPeer {
|
||||
return .single(.peer(peer.id, .withAttachBot(ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: payload, justInstalled: false))))
|
||||
return .single(.peer(peer, .withAttachBot(ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: payload, justInstalled: false))))
|
||||
} else {
|
||||
return .single(.peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
return .single(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
}
|
||||
}
|
||||
case let .channelMessage(id, timecode):
|
||||
return .single(.channelMessage(peerId: peer.id, messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id), timecode: timecode))
|
||||
return .single(.channelMessage(peer: peer, messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id), timecode: timecode))
|
||||
case let .replyThread(id, replyId):
|
||||
let replyThreadMessageId = MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id)
|
||||
return context.engine.messages.fetchChannelReplyThreadMessage(messageId: replyThreadMessageId, atMessageId: nil)
|
||||
@ -527,7 +527,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
||||
}
|
||||
|> map { result -> ResolvedUrl? in
|
||||
guard let result = result else {
|
||||
return .channelMessage(peerId: peer.id, messageId: replyThreadMessageId, timecode: nil)
|
||||
return .channelMessage(peer: peer, messageId: replyThreadMessageId, timecode: nil)
|
||||
}
|
||||
return .replyThreadMessage(replyThreadMessage: result, messageId: MessageId(peerId: result.messageId.peerId, namespace: Namespaces.Message.Cloud, id: replyId))
|
||||
}
|
||||
@ -535,7 +535,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
||||
return .single(.joinVoiceChat(peer.id, invite))
|
||||
}
|
||||
} else {
|
||||
return .single(.peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
return .single(.peer(peer, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
}
|
||||
} else {
|
||||
return .single(.peer(nil, .info))
|
||||
@ -545,7 +545,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
||||
return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> mapToSignal { peer -> Signal<ResolvedUrl?, NoError> in
|
||||
if let peer = peer {
|
||||
return .single(.peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
return .single(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||
} else {
|
||||
return .single(.inaccessiblePeer)
|
||||
}
|
||||
@ -571,12 +571,12 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
||||
}
|
||||
|> map { result -> ResolvedUrl? in
|
||||
guard let result = result else {
|
||||
return .channelMessage(peerId: foundPeer.id, messageId: replyThreadMessageId, timecode: timecode)
|
||||
return .channelMessage(peer: foundPeer._asPeer(), messageId: replyThreadMessageId, timecode: timecode)
|
||||
}
|
||||
return .replyThreadMessage(replyThreadMessage: result, messageId: messageId)
|
||||
}
|
||||
} else {
|
||||
return .single(.peer(foundPeer.id, .chat(textInputState: nil, subject: .message(id: .id(messageId), highlight: true, timecode: timecode), peekData: nil)))
|
||||
return .single(.peer(foundPeer._asPeer(), .chat(textInputState: nil, subject: .message(id: .id(messageId), highlight: true, timecode: timecode), peekData: nil)))
|
||||
}
|
||||
} else {
|
||||
return .single(.inaccessiblePeer)
|
||||
|
Loading…
x
Reference in New Issue
Block a user