mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
[WIP]
This commit is contained in:
parent
852478d851
commit
7ba86968c8
@ -471,6 +471,7 @@ public enum PeerInfoControllerMode {
|
||||
case nearbyPeer(distance: Int32)
|
||||
case group(PeerId)
|
||||
case reaction(MessageId)
|
||||
case forumTopic(thread: ChatReplyThreadMessage)
|
||||
}
|
||||
|
||||
public enum ContactListActionItemInlineIconPosition {
|
||||
@ -749,7 +750,7 @@ public protocol SharedAccountContext: AnyObject {
|
||||
|
||||
func makeRecentSessionsController(context: AccountContext, activeSessionsContext: ActiveSessionsContext) -> ViewController & RecentSessionsController
|
||||
|
||||
func makeChatQrCodeScreen(context: AccountContext, peer: Peer) -> ViewController
|
||||
func makeChatQrCodeScreen(context: AccountContext, peer: Peer, threadId: Int64?) -> ViewController
|
||||
|
||||
func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource) -> ViewController
|
||||
|
||||
|
||||
@ -1543,8 +1543,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
let context = strongSelf.context
|
||||
let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create)
|
||||
controller.navigationPresentation = .modal
|
||||
|
||||
controller.completion = { title, fileId in
|
||||
let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconFileId: fileId)
|
||||
let availableColors: [Int32] = [0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, 0xFB6F5F]
|
||||
|
||||
let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: availableColors.randomElement()!, iconFileId: fileId)
|
||||
|> deliverOnMainQueue).start(next: { topicId in
|
||||
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, navigationController: navigationController, activateInput: .text).start()
|
||||
})
|
||||
@ -2528,7 +2531,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create)
|
||||
controller.navigationPresentation = .modal
|
||||
controller.completion = { title, fileId in
|
||||
let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconFileId: fileId)
|
||||
let availableColors: [Int32] = [0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, 0xFB6F5F]
|
||||
|
||||
let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: availableColors.randomElement()!, iconFileId: fileId)
|
||||
|> deliverOnMainQueue).start(next: { topicId in
|
||||
if let navigationController = (sourceController.navigationController as? NavigationController) {
|
||||
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, navigationController: navigationController, activateInput: .text).start()
|
||||
|
||||
@ -487,7 +487,7 @@ public class ContactsController: ViewController {
|
||||
let _ = (strongSelf.context.account.postbox.loadedPeerWithId(strongSelf.context.account.peerId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self, weak controller] peer in
|
||||
if let strongSelf = self, let controller = controller {
|
||||
controller.present(strongSelf.context.sharedContext.makeChatQrCodeScreen(context: strongSelf.context, peer: peer), in: .window(.root))
|
||||
controller.present(strongSelf.context.sharedContext.makeChatQrCodeScreen(context: strongSelf.context, peer: peer, threadId: nil), in: .window(.root))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -89,13 +89,13 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
||||
return TelegramMediaAction(action: .giftPremium(currency: currency, amount: amount, months: months))
|
||||
case let .messageActionTopicCreate(_, title, iconColor, iconEmojiId):
|
||||
return TelegramMediaAction(action: .topicCreated(title: title, iconColor: iconColor, iconFileId: iconEmojiId))
|
||||
case let .messageActionTopicEdit(_, title, iconEmojiId):
|
||||
case let .messageActionTopicEdit(flags, title, iconEmojiId):
|
||||
var componenents: [TelegramMediaActionType.ForumTopicEditComponent] = []
|
||||
//messageActionTopicEdit#b117a9f5 flags:# title:flags.0?string icon_emoji_id:flags.1?long = MessageAction;
|
||||
if let title {
|
||||
componenents.append(.title(title))
|
||||
}
|
||||
if let iconEmojiId {
|
||||
if (flags & (1 << 1)) != 0 {
|
||||
componenents.append(.iconFileId(iconEmojiId == 0 ? nil : iconEmojiId))
|
||||
}
|
||||
return TelegramMediaAction(action: .topicEdited(components: componenents))
|
||||
|
||||
@ -123,6 +123,55 @@ func _internal_createForumChannelTopic(account: Account, peerId: PeerId, title:
|
||||
}
|
||||
}
|
||||
|
||||
public enum EditForumChannelTopicError {
|
||||
case generic
|
||||
}
|
||||
|
||||
func _internal_editForumChannelTopic(account: Account, peerId: PeerId, threadId: Int64, title: String, iconFileId: Int64?) -> Signal<Never, EditForumChannelTopicError> {
|
||||
return account.postbox.transaction { transaction -> Api.InputChannel? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputChannel)
|
||||
}
|
||||
|> castError(EditForumChannelTopicError.self)
|
||||
|> mapToSignal { inputChannel -> Signal<Never, EditForumChannelTopicError> in
|
||||
guard let inputChannel = inputChannel else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
var flags: Int32 = 0
|
||||
flags |= (1 << 0)
|
||||
flags |= (1 << 1)
|
||||
|
||||
return account.network.request(Api.functions.channels.editForumTopic(
|
||||
flags: flags,
|
||||
channel: inputChannel,
|
||||
topicId: Int32(clamping: threadId),
|
||||
title: title,
|
||||
iconEmojiId: iconFileId ?? 0
|
||||
))
|
||||
|> mapError { _ -> EditForumChannelTopicError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Never, EditForumChannelTopicError> in
|
||||
account.stateManager.addUpdates(result)
|
||||
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
if let initialData = transaction.getMessageHistoryThreadInfo(peerId: peerId, threadId: threadId)?.get(MessageHistoryThreadData.self) {
|
||||
var data = initialData
|
||||
|
||||
data.info = EngineMessageHistoryThread.Info(title: title, icon: iconFileId, iconColor: data.info.iconColor)
|
||||
|
||||
if data != initialData {
|
||||
if let entry = CodableEntry(data) {
|
||||
transaction.setMessageHistoryThreadInfo(peerId: peerId, threadId: threadId, info: entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|> castError(EditForumChannelTopicError.self)
|
||||
|> ignoreValues
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_setChannelForumMode(account: Account, peerId: PeerId, isForum: Bool) -> Signal<Never, NoError> {
|
||||
return account.postbox.transaction { transaction -> Api.InputChannel? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputChannel)
|
||||
|
||||
@ -7,6 +7,7 @@ public final class SparseMessageList {
|
||||
private let queue: Queue
|
||||
private let account: Account
|
||||
private let peerId: PeerId
|
||||
private let threadId: Int64?
|
||||
private let messageTag: MessageTags
|
||||
|
||||
private struct TopSection: Equatable {
|
||||
@ -91,14 +92,16 @@ public final class SparseMessageList {
|
||||
|
||||
let statePromise = Promise<SparseMessageList.State>()
|
||||
|
||||
init(queue: Queue, account: Account, peerId: PeerId, messageTag: MessageTags) {
|
||||
init(queue: Queue, account: Account, peerId: PeerId, threadId: Int64?, messageTag: MessageTags) {
|
||||
self.queue = queue
|
||||
self.account = account
|
||||
self.peerId = peerId
|
||||
self.threadId = threadId
|
||||
self.messageTag = messageTag
|
||||
|
||||
self.resetTopSection()
|
||||
|
||||
if self.threadId == nil {
|
||||
self.sparseItemsDisposable = (self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
@ -161,6 +164,7 @@ public final class SparseMessageList {
|
||||
strongSelf.updateState()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
self.deletedMessagesDisposable = (account.postbox.combinedView(keys: [.deletedMessages(peerId: peerId)])
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] views in
|
||||
@ -183,12 +187,16 @@ public final class SparseMessageList {
|
||||
|
||||
private func resetTopSection() {
|
||||
let count: Int
|
||||
/*#if DEBUG
|
||||
count = 20
|
||||
#else*/
|
||||
count = 200
|
||||
//#endif
|
||||
self.topItemsDisposable.set((self.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: peerId), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: count, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: self.messageTag, appendMessagesFromTheSameGroup: false, namespaces: .not(Set(Namespaces.Message.allScheduled)), orderStatistics: [])
|
||||
|
||||
let location: ChatLocationInput
|
||||
if let threadId = self.threadId {
|
||||
location = .thread(peerId: self.peerId, threadId: threadId, data: .single(MessageHistoryViewExternalInput(content: .thread(peerId: self.peerId, id: threadId, holes: [:]), maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil)))
|
||||
} else {
|
||||
location = .peer(peerId: self.peerId)
|
||||
}
|
||||
|
||||
self.topItemsDisposable.set((self.account.postbox.aroundMessageHistoryViewForLocation(location, anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: count, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: self.messageTag, appendMessagesFromTheSameGroup: false, namespaces: .not(Set(Namespaces.Message.allScheduled)), orderStatistics: [])
|
||||
|> deliverOn(self.queue)).start(next: { [weak self] view, updateType, _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -688,11 +696,11 @@ public final class SparseMessageList {
|
||||
}
|
||||
}
|
||||
|
||||
init(account: Account, peerId: PeerId, messageTag: MessageTags) {
|
||||
init(account: Account, peerId: PeerId, threadId: Int64?, messageTag: MessageTags) {
|
||||
self.queue = Queue()
|
||||
let queue = self.queue
|
||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||
return Impl(queue: queue, account: account, peerId: peerId, messageTag: messageTag)
|
||||
return Impl(queue: queue, account: account, peerId: peerId, threadId: threadId, messageTag: messageTag)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -285,8 +285,8 @@ public extension TelegramEngine {
|
||||
}
|
||||
}
|
||||
|
||||
public func sparseMessageList(peerId: EnginePeer.Id, tag: EngineMessage.Tags) -> SparseMessageList {
|
||||
return SparseMessageList(account: self.account, peerId: peerId, messageTag: tag)
|
||||
public func sparseMessageList(peerId: EnginePeer.Id, threadId: Int64?, tag: EngineMessage.Tags) -> SparseMessageList {
|
||||
return SparseMessageList(account: self.account, peerId: peerId, threadId: threadId, messageTag: tag)
|
||||
}
|
||||
|
||||
public func sparseMessageCalendar(peerId: EnginePeer.Id, tag: EngineMessage.Tags) -> SparseMessageCalendar {
|
||||
|
||||
@ -805,6 +805,10 @@ public extension TelegramEngine {
|
||||
public func createForumChannelTopic(id: EnginePeer.Id, title: String, iconColor: Int32, iconFileId: Int64?) -> Signal<Int64, CreateForumChannelTopicError> {
|
||||
return _internal_createForumChannelTopic(account: self.account, peerId: id, title: title, iconColor: iconColor, iconFileId: iconFileId)
|
||||
}
|
||||
|
||||
public func editForumChannelTopic(id: EnginePeer.Id, threadId: Int64, title: String, iconFileId: Int64?) -> Signal<Never, EditForumChannelTopicError> {
|
||||
return _internal_editForumChannelTopic(account: self.account, peerId: id, threadId: threadId, title: title, iconFileId: iconFileId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -225,8 +225,11 @@ public final class EmojiStatusComponent: Component {
|
||||
}
|
||||
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))
|
||||
return generateImage(CGSize(width: availableSize.width, height: availableSize.height), rotatedContext: { realSize, context in
|
||||
context.clear(CGRect(origin: .zero, size: realSize))
|
||||
|
||||
let size = CGSize(width: 44.0, height: 44.0)
|
||||
context.scaleBy(x: realSize.width / size.width, y: realSize.height / size.height)
|
||||
|
||||
context.saveGState()
|
||||
|
||||
|
||||
@ -357,9 +357,9 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
|
||||
case .create:
|
||||
self.title = ""
|
||||
self.fileId = 0
|
||||
case let .edit(topic):
|
||||
self.title = topic.info.title
|
||||
self.fileId = topic.info.icon ?? 0
|
||||
case let .edit(info):
|
||||
self.title = info.title
|
||||
self.fileId = info.icon ?? 0
|
||||
}
|
||||
|
||||
super.init()
|
||||
@ -670,7 +670,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
|
||||
public class ForumCreateTopicScreen: ViewControllerComponentContainer {
|
||||
public enum Mode: Equatable {
|
||||
case create
|
||||
case edit(topic: ForumChannelTopics.Item)
|
||||
case edit(topic: EngineMessageHistoryThread.Info)
|
||||
}
|
||||
|
||||
private var state: (String, Int64?) = ("", nil)
|
||||
|
||||
@ -4809,14 +4809,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
if let threadInfo = messageAndTopic.threadInfo, let message = message {
|
||||
if let threadInfo = messageAndTopic.threadInfo {
|
||||
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))
|
||||
avatarContent = .topic(title: String(threadInfo.title.prefix(1)), colorIndex: Int(threadInfo.iconColor))
|
||||
}
|
||||
(strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.setStatus(context: strongSelf.context, content: avatarContent)
|
||||
} else {
|
||||
@ -9979,7 +9979,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
if case let .peer(peerId) = self.chatLocation {
|
||||
let context = self.context
|
||||
self.keepPeerInfoScreenDataHotDisposable.set(keepPeerInfoScreenDataHot(context: context, peerId: peerId).start())
|
||||
self.keepPeerInfoScreenDataHotDisposable.set(keepPeerInfoScreenDataHot(context: context, peerId: peerId, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder).start())
|
||||
|
||||
if peerId.namespace == Namespaces.Peer.CloudUser {
|
||||
self.preloadAvatarDisposable.set((peerInfoProfilePhotosWithCache(context: context, peerId: peerId)
|
||||
@ -11140,7 +11140,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}))
|
||||
case .replyThread:
|
||||
break
|
||||
if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum), case let .replyThread(message) = self.chatLocation {
|
||||
if let infoController = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: channel, mode: .forumTopic(thread: message), avatarInitiallyExpanded: false, fromChat: true, requestsContext: self.inviteRequestsContext) {
|
||||
self.effectiveNavigationController?.pushViewController(infoController)
|
||||
}
|
||||
}
|
||||
case .feed:
|
||||
break
|
||||
}
|
||||
|
||||
@ -564,19 +564,24 @@ final class ChatQrCodeScreen: ViewController {
|
||||
static let themeCrossfadeDelay: Double = 0.05
|
||||
|
||||
enum Subject {
|
||||
case peer(Peer)
|
||||
case peer(peer: Peer, threadId: Int64?)
|
||||
case messages([Message])
|
||||
|
||||
var fileName: String {
|
||||
switch self {
|
||||
case let .peer(peer):
|
||||
case let .peer(peer, threadId):
|
||||
var result: String
|
||||
if let addressName = peer.addressName, !addressName.isEmpty {
|
||||
return "t_me-\(peer.addressName ?? "")"
|
||||
result = "t_me-\(peer.addressName ?? "")"
|
||||
} else if let peer = peer as? TelegramUser {
|
||||
return "t_me-\(peer.phone ?? "")"
|
||||
result = "t_me-\(peer.phone ?? "")"
|
||||
} else {
|
||||
return "t_me-\(Int32.random(in: 0 ..< Int32.max))"
|
||||
result = "t_me-\(Int32.random(in: 0 ..< Int32.max))"
|
||||
}
|
||||
if let threadId = threadId {
|
||||
result.append("-\(threadId)")
|
||||
}
|
||||
return result
|
||||
case let .messages(messages):
|
||||
if let message = messages.first, let chatPeer = message.peers[message.id.peerId] as? TelegramChannel, message.id.namespace == Namespaces.Message.Cloud, let addressName = chatPeer.addressName, !addressName.isEmpty {
|
||||
return "t_me-\(addressName)-\(message.id.id)"
|
||||
@ -800,8 +805,8 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg
|
||||
self.wrappingScrollNode.view.canCancelContentTouches = true
|
||||
|
||||
switch controller.subject {
|
||||
case let .peer(peer):
|
||||
self.contentNode = QrContentNode(context: context, peer: peer, isStatic: false)
|
||||
case let .peer(peer, threadId):
|
||||
self.contentNode = QrContentNode(context: context, peer: peer, threadId: threadId, isStatic: false)
|
||||
case let .messages(messages):
|
||||
self.contentNode = MessageContentNode(context: context, messages: messages)
|
||||
}
|
||||
@ -994,7 +999,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg
|
||||
let initiallySelectedEmoticon: Signal<String, NoError>
|
||||
let sharedData = self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings])
|
||||
|> take(1)
|
||||
if case let .peer(peer) = controller.subject, peer.id != self.context.account.peerId {
|
||||
if case let .peer(peer, _) = controller.subject, peer.id != self.context.account.peerId {
|
||||
let themeEmoticon = self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ThemeEmoticon(id: peer.id))
|
||||
initiallySelectedEmoticon = combineLatest(themeEmoticon, sharedData)
|
||||
|> map { themeEmoticon, sharedData -> String in
|
||||
@ -1434,6 +1439,7 @@ private protocol ContentNode: ASDisplayNode {
|
||||
private class QrContentNode: ASDisplayNode, ContentNode {
|
||||
private let context: AccountContext
|
||||
private let peer: Peer
|
||||
private let threadId: Int64?
|
||||
private let isStatic: Bool
|
||||
|
||||
fileprivate let containerNode: ASDisplayNode
|
||||
@ -1463,9 +1469,10 @@ private class QrContentNode: ASDisplayNode, ContentNode {
|
||||
return false
|
||||
}
|
||||
|
||||
init(context: AccountContext, peer: Peer, isStatic: Bool = false) {
|
||||
init(context: AccountContext, peer: Peer, threadId: Int64?, isStatic: Bool = false) {
|
||||
self.context = context
|
||||
self.peer = peer
|
||||
self.threadId = threadId
|
||||
self.isStatic = isStatic
|
||||
|
||||
self.containerNode = ASDisplayNode()
|
||||
@ -1506,12 +1513,15 @@ private class QrContentNode: ASDisplayNode, ContentNode {
|
||||
self.codeStaticIconNode = nil
|
||||
}
|
||||
|
||||
let codeText: String
|
||||
var codeText: String
|
||||
if let addressName = peer.addressName, !addressName.isEmpty {
|
||||
codeText = "@\(peer.addressName ?? "")".uppercased()
|
||||
} else {
|
||||
codeText = peer.debugDisplayTitle.uppercased()
|
||||
}
|
||||
if let threadId = self.threadId {
|
||||
codeText += "/\(threadId)"
|
||||
}
|
||||
|
||||
self.codeTextNode = ImmediateTextNode()
|
||||
self.codeTextNode.displaysAsynchronously = false
|
||||
@ -1550,14 +1560,19 @@ private class QrContentNode: ASDisplayNode, ContentNode {
|
||||
self.addSubnode(codeAnimatedIconNode)
|
||||
}
|
||||
|
||||
let codeLink: String
|
||||
var codeLink: String
|
||||
if let addressName = peer.addressName, !addressName.isEmpty {
|
||||
codeLink = "https://t.me/\(peer.addressName ?? "")"
|
||||
} else if let peer = peer as? TelegramUser {
|
||||
codeLink = "https://t.me/+\(peer.phone ?? "")"
|
||||
} else if let _ = peer as? TelegramChannel {
|
||||
codeLink = "https://t.me/c/\(peer.id.id._internalGetInt64Value())"
|
||||
} else {
|
||||
codeLink = ""
|
||||
}
|
||||
if let threadId = threadId {
|
||||
codeLink += "?topic=\(threadId)"
|
||||
}
|
||||
|
||||
let codeReadyPromise = ValuePromise<Bool>()
|
||||
self.codeImageNode.setSignal(qrCode(string: codeLink, color: .black, backgroundColor: nil, icon: .cutout, ecl: "Q") |> beforeNext { [weak self] size, _ in
|
||||
@ -1591,7 +1606,7 @@ private class QrContentNode: ASDisplayNode, ContentNode {
|
||||
let size = CGSize(width: 390.0, height: 844.0)
|
||||
let scale: CGFloat = 3.0
|
||||
|
||||
let copyNode = QrContentNode(context: self.context, peer: self.peer, isStatic: true)
|
||||
let copyNode = QrContentNode(context: self.context, peer: self.peer, threadId: self.threadId, isStatic: true)
|
||||
|
||||
func prepare(view: UIView, scale: CGFloat) {
|
||||
view.contentScaleFactor = scale
|
||||
|
||||
@ -1408,7 +1408,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
}
|
||||
} else if let channel = peer as? TelegramChannel, case .group = channel.info, channel.hasPermission(.canBeAnonymous) {
|
||||
placeholder = interfaceState.strings.Conversation_InputTextAnonymousPlaceholder
|
||||
} else if case let .replyThread(replyThreadMessage) = interfaceState.chatLocation {
|
||||
} else if case let .replyThread(replyThreadMessage) = interfaceState.chatLocation, !replyThreadMessage.isForumPost {
|
||||
if replyThreadMessage.isChannelPost {
|
||||
placeholder = interfaceState.strings.Conversation_InputTextPlaceholderComment
|
||||
} else {
|
||||
|
||||
@ -19,6 +19,8 @@ import ChatPresentationInterfaceState
|
||||
final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
||||
private let context: AccountContext
|
||||
private let peerId: PeerId
|
||||
private let chatLocation: ChatLocation
|
||||
private let chatLocationContextHolder: Atomic<ChatLocationContextHolder?>
|
||||
private let chatControllerInteraction: ChatControllerInteraction
|
||||
|
||||
weak var parentController: ViewController?
|
||||
@ -61,16 +63,18 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, tagMask: MessageTags) {
|
||||
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, tagMask: MessageTags) {
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
self.chatLocation = chatLocation
|
||||
self.chatLocationContextHolder = chatLocationContextHolder
|
||||
|
||||
self.chatControllerInteraction = chatControllerInteraction
|
||||
|
||||
self.selectedMessages = chatControllerInteraction.selectionState.flatMap { $0.selectedIds }
|
||||
self.selectedMessagesPromise.set(.single(self.selectedMessages))
|
||||
|
||||
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
|
||||
self.listNode = ChatHistoryListNode(context: context, updatedPresentationData: updatedPresentationData ?? (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: .peer(id: peerId), chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: nil, controllerInteraction: chatControllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), mode: .list(search: false, reversed: false, displayHeaders: .allButLast, hintLinks: tagMask == .webPage, isGlobalSearch: false))
|
||||
self.listNode = ChatHistoryListNode(context: context, updatedPresentationData: updatedPresentationData ?? (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: tagMask, subject: nil, controllerInteraction: chatControllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), mode: .list(search: false, reversed: false, displayHeaders: .allButLast, hintLinks: tagMask == .webPage, isGlobalSearch: false))
|
||||
self.listNode.clipsToBounds = true
|
||||
self.listNode.defaultToSynchronousTransactionWhileScrolling = true
|
||||
self.listNode.scroller.bounces = false
|
||||
|
||||
@ -1594,6 +1594,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
|
||||
private let context: AccountContext
|
||||
private let peerId: PeerId
|
||||
private let chatLocation: ChatLocation
|
||||
private let chatLocationContextHolder: Atomic<ChatLocationContextHolder?>
|
||||
private let chatControllerInteraction: ChatControllerInteraction
|
||||
private(set) var contentType: ContentType
|
||||
private var contentTypePromise: ValuePromise<ContentType>
|
||||
@ -1657,9 +1659,11 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
private var presentationData: PresentationData
|
||||
private var presentationDataDisposable: Disposable?
|
||||
|
||||
init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, contentType: ContentType, captureProtected: Bool) {
|
||||
init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, contentType: ContentType, captureProtected: Bool) {
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
self.chatLocation = chatLocation
|
||||
self.chatLocationContextHolder = chatLocationContextHolder
|
||||
self.chatControllerInteraction = chatControllerInteraction
|
||||
self.contentType = contentType
|
||||
self.contentTypePromise = ValuePromise<ContentType>(contentType)
|
||||
@ -1720,13 +1724,22 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
captureProtected: captureProtected
|
||||
)
|
||||
|
||||
self.listSource = self.context.engine.messages.sparseMessageList(peerId: self.peerId, tag: tagMaskForType(self.contentType))
|
||||
var threadId: Int64?
|
||||
if case let .replyThread(message) = chatLocation {
|
||||
threadId = Int64(message.messageId.id)
|
||||
}
|
||||
|
||||
self.listSource = self.context.engine.messages.sparseMessageList(peerId: self.peerId, threadId: threadId, tag: tagMaskForType(self.contentType))
|
||||
if threadId == nil {
|
||||
switch contentType {
|
||||
case .photoOrVideo, .photo, .video:
|
||||
self.calendarSource = self.context.engine.messages.sparseMessageCalendar(peerId: self.peerId, tag: tagMaskForType(self.contentType))
|
||||
default:
|
||||
self.calendarSource = nil
|
||||
}
|
||||
} else {
|
||||
self.calendarSource = nil
|
||||
}
|
||||
|
||||
super.init()
|
||||
|
||||
@ -2173,7 +2186,12 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
|
||||
self.itemGrid.hideScrollingArea()
|
||||
|
||||
self.listSource = self.context.engine.messages.sparseMessageList(peerId: self.peerId, tag: tagMaskForType(self.contentType))
|
||||
var threadId: Int64?
|
||||
if case let .replyThread(message) = chatLocation {
|
||||
threadId = Int64(message.messageId.id)
|
||||
}
|
||||
|
||||
self.listSource = self.context.engine.messages.sparseMessageList(peerId: self.peerId, threadId: threadId, tag: tagMaskForType(self.contentType))
|
||||
self.isRequestingView = false
|
||||
self.requestHistoryAroundVisiblePosition(synchronous: true, reloadAtTop: true)
|
||||
}
|
||||
|
||||
@ -187,6 +187,7 @@ final class PeerInfoScreenData {
|
||||
let invitations: PeerExportedInvitationsState?
|
||||
let requests: PeerInvitationImportersState?
|
||||
let requestsContext: PeerInvitationImportersContext?
|
||||
let threadInfo: EngineMessageHistoryThread.Info?
|
||||
|
||||
init(
|
||||
peer: Peer?,
|
||||
@ -204,7 +205,8 @@ final class PeerInfoScreenData {
|
||||
globalSettings: TelegramGlobalSettings?,
|
||||
invitations: PeerExportedInvitationsState?,
|
||||
requests: PeerInvitationImportersState?,
|
||||
requestsContext: PeerInvitationImportersContext?
|
||||
requestsContext: PeerInvitationImportersContext?,
|
||||
threadInfo: EngineMessageHistoryThread.Info?
|
||||
) {
|
||||
self.peer = peer
|
||||
self.chatPeer = chatPeer
|
||||
@ -222,6 +224,7 @@ final class PeerInfoScreenData {
|
||||
self.invitations = invitations
|
||||
self.requests = requests
|
||||
self.requestsContext = requestsContext
|
||||
self.threadInfo = threadInfo
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,7 +243,7 @@ private enum PeerInfoScreenInputData: Equatable {
|
||||
case group(groupId: PeerId)
|
||||
}
|
||||
|
||||
private func peerInfoAvailableMediaPanes(context: AccountContext, peerId: PeerId) -> Signal<[PeerInfoPaneKey]?, NoError> {
|
||||
private func peerInfoAvailableMediaPanes(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>) -> Signal<[PeerInfoPaneKey]?, NoError> {
|
||||
let tags: [(MessageTags, PeerInfoPaneKey)] = [
|
||||
(.photoOrVideo, .media),
|
||||
(.file, .files),
|
||||
@ -257,7 +260,8 @@ private func peerInfoAvailableMediaPanes(context: AccountContext, peerId: PeerId
|
||||
let loadedOnce = Atomic<Bool>(value: false)
|
||||
return combineLatest(queue: .mainQueue(), tags.map { tagAndKey -> Signal<(PeerInfoPaneKey, PaneState), NoError> in
|
||||
let (tag, key) = tagAndKey
|
||||
return context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId), index: .upperBound, anchorIndex: .upperBound, count: 20, clipHoles: false, fixedCombinedReadStates: nil, tagMask: tag)
|
||||
let location = context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder)
|
||||
return context.account.viewTracker.aroundMessageHistoryViewForLocation(location, index: .upperBound, anchorIndex: .upperBound, count: 20, clipHoles: false, fixedCombinedReadStates: nil, tagMask: tag)
|
||||
|> map { (view, _, _) -> (PeerInfoPaneKey, PaneState) in
|
||||
if view.entries.isEmpty {
|
||||
if view.isLoading {
|
||||
@ -353,7 +357,7 @@ private func peerInfoScreenInputData(context: AccountContext, peerId: EnginePeer
|
||||
|> distinctUntilChanged
|
||||
}
|
||||
|
||||
func keepPeerInfoScreenDataHot(context: AccountContext, peerId: PeerId) -> Signal<Never, NoError> {
|
||||
func keepPeerInfoScreenDataHot(context: AccountContext, peerId: PeerId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>) -> Signal<Never, NoError> {
|
||||
return peerInfoScreenInputData(context: context, peerId: peerId, isSettings: false)
|
||||
|> mapToSignal { inputData -> Signal<Never, NoError> in
|
||||
switch inputData {
|
||||
@ -361,7 +365,7 @@ func keepPeerInfoScreenDataHot(context: AccountContext, peerId: PeerId) -> Signa
|
||||
return .complete()
|
||||
case .user, .channel, .group:
|
||||
return combineLatest(
|
||||
context.peerChannelMemberCategoriesContextsManager.profileData(postbox: context.account.postbox, network: context.account.network, peerId: peerId, customData: peerInfoAvailableMediaPanes(context: context, peerId: peerId) |> ignoreValues),
|
||||
context.peerChannelMemberCategoriesContextsManager.profileData(postbox: context.account.postbox, network: context.account.network, peerId: peerId, customData: peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder) |> ignoreValues),
|
||||
context.peerChannelMemberCategoriesContextsManager.profilePhotos(postbox: context.account.postbox, network: context.account.network, peerId: peerId, fetch: peerInfoProfilePhotos(context: context, peerId: peerId)) |> ignoreValues
|
||||
)
|
||||
|> ignoreValues
|
||||
@ -472,12 +476,13 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
|
||||
globalSettings: globalSettings,
|
||||
invitations: nil,
|
||||
requests: nil,
|
||||
requestsContext: nil
|
||||
requestsContext: nil,
|
||||
threadInfo: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, isSettings: Bool, hintGroupInCommon: PeerId?, existingRequestsContext: PeerInvitationImportersContext?) -> Signal<PeerInfoScreenData, NoError> {
|
||||
func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, isSettings: Bool, hintGroupInCommon: PeerId?, existingRequestsContext: PeerInvitationImportersContext?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>) -> Signal<PeerInfoScreenData, NoError> {
|
||||
return peerInfoScreenInputData(context: context, peerId: peerId, isSettings: isSettings)
|
||||
|> mapToSignal { inputData -> Signal<PeerInfoScreenData, NoError> in
|
||||
switch inputData {
|
||||
@ -498,7 +503,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
globalSettings: nil,
|
||||
invitations: nil,
|
||||
requests: nil,
|
||||
requestsContext: nil
|
||||
requestsContext: nil,
|
||||
threadInfo: nil
|
||||
))
|
||||
case let .user(userPeerId, secretChatId, kind):
|
||||
let groupsInCommon: GroupsInCommonContext?
|
||||
@ -599,7 +605,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
|
||||
return combineLatest(
|
||||
context.account.viewTracker.peerView(peerId, updateData: true),
|
||||
peerInfoAvailableMediaPanes(context: context, peerId: peerId),
|
||||
peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder),
|
||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global()),
|
||||
secretChatKeyFingerprint,
|
||||
status
|
||||
@ -628,7 +634,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
globalSettings: nil,
|
||||
invitations: nil,
|
||||
requests: nil,
|
||||
requestsContext: nil
|
||||
requestsContext: nil,
|
||||
threadInfo: nil
|
||||
)
|
||||
}
|
||||
case .channel:
|
||||
@ -653,7 +660,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
|
||||
return combineLatest(
|
||||
context.account.viewTracker.peerView(peerId, updateData: true),
|
||||
peerInfoAvailableMediaPanes(context: context, peerId: peerId),
|
||||
peerInfoAvailableMediaPanes(context: context, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder),
|
||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global()),
|
||||
status,
|
||||
invitationsContextPromise.get(),
|
||||
@ -703,7 +710,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
globalSettings: nil,
|
||||
invitations: invitations,
|
||||
requests: requests,
|
||||
requestsContext: currentRequestsContext
|
||||
requestsContext: currentRequestsContext,
|
||||
threadInfo: nil
|
||||
)
|
||||
}
|
||||
case let .group(groupId):
|
||||
@ -785,9 +793,10 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|
||||
let membersData: Signal<PeerInfoMembersData?, NoError>
|
||||
if case .peer = chatLocation {
|
||||
let membersContext = PeerInfoMembersContext(context: context, peerId: groupId)
|
||||
|
||||
let membersData: Signal<PeerInfoMembersData?, NoError> = combineLatest(membersContext.state, context.account.viewTracker.peerView(groupId, updateData: false))
|
||||
membersData = combineLatest(membersContext.state, context.account.viewTracker.peerView(groupId, updateData: false))
|
||||
|> map { state, view -> PeerInfoMembersData? in
|
||||
if state.members.count > 5 {
|
||||
return .longList(membersContext)
|
||||
@ -796,6 +805,9 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
}
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
} else {
|
||||
membersData = .single(nil)
|
||||
}
|
||||
|
||||
let invitationsContextPromise = Promise<PeerExportedInvitationsContext?>(nil)
|
||||
let invitationsStatePromise = Promise<PeerExportedInvitationsState?>(nil)
|
||||
@ -803,27 +815,40 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
let requestsContextPromise = Promise<PeerInvitationImportersContext?>(nil)
|
||||
let requestsStatePromise = Promise<PeerInvitationImportersState?>(nil)
|
||||
|
||||
let threadInfo: Signal<EngineMessageHistoryThread.Info?, NoError>
|
||||
if case let .replyThread(message) = chatLocation {
|
||||
let threadId = Int64(message.messageId.id)
|
||||
let viewKey: PostboxViewKey = .messageHistoryThreadInfo(peerId: peerId, threadId: threadId)
|
||||
threadInfo = context.account.postbox.combinedView(keys: [viewKey])
|
||||
|> map { views -> EngineMessageHistoryThread.Info? in
|
||||
guard let view = views.views[viewKey] as? MessageHistoryThreadInfoView else {
|
||||
return nil
|
||||
}
|
||||
return view.info?.get(MessageHistoryThreadData.self)?.info
|
||||
}
|
||||
} else {
|
||||
threadInfo = .single(nil)
|
||||
}
|
||||
|
||||
return combineLatest(queue: .mainQueue(),
|
||||
context.account.viewTracker.peerView(groupId, updateData: true),
|
||||
peerInfoAvailableMediaPanes(context: context, peerId: groupId),
|
||||
peerInfoAvailableMediaPanes(context: context, peerId: groupId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder),
|
||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global()),
|
||||
status,
|
||||
membersData,
|
||||
invitationsContextPromise.get(),
|
||||
invitationsStatePromise.get(),
|
||||
requestsContextPromise.get(),
|
||||
requestsStatePromise.get()
|
||||
requestsStatePromise.get(),
|
||||
threadInfo
|
||||
)
|
||||
|> map { peerView, availablePanes, globalNotificationSettings, status, membersData, currentInvitationsContext, invitations, currentRequestsContext, requests -> PeerInfoScreenData in
|
||||
|> map { peerView, availablePanes, globalNotificationSettings, status, membersData, currentInvitationsContext, invitations, currentRequestsContext, requests, threadInfo -> PeerInfoScreenData in
|
||||
var discussionPeer: Peer?
|
||||
if case let .known(maybeLinkedDiscussionPeerId) = (peerView.cachedData as? CachedChannelData)?.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId, let peer = peerView.peers[linkedDiscussionPeerId] {
|
||||
discussionPeer = peer
|
||||
}
|
||||
|
||||
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)
|
||||
@ -874,7 +899,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
||||
globalSettings: nil,
|
||||
invitations: invitations,
|
||||
requests: requests,
|
||||
requestsContext: currentRequestsContext
|
||||
requestsContext: currentRequestsContext,
|
||||
threadInfo: threadInfo
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1013,7 +1039,7 @@ func peerInfoHeaderButtonIsHiddenWhileExpanded(buttonKey: PeerInfoHeaderButtonKe
|
||||
return hiddenWhileExpanded
|
||||
}
|
||||
|
||||
func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFromChat: Bool, isExpanded: Bool, videoCallsEnabled: Bool, isSecretChat: Bool, isContact: Bool) -> [PeerInfoHeaderButtonKey] {
|
||||
func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFromChat: Bool, isExpanded: Bool, videoCallsEnabled: Bool, isSecretChat: Bool, isContact: Bool, threadInfo: EngineMessageHistoryThread.Info?) -> [PeerInfoHeaderButtonKey] {
|
||||
var result: [PeerInfoHeaderButtonKey] = []
|
||||
if let user = peer as? TelegramUser {
|
||||
if !isOpenedFromChat {
|
||||
@ -1050,6 +1076,10 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro
|
||||
result.append(.more)
|
||||
}
|
||||
} else if let channel = peer as? TelegramChannel {
|
||||
if let _ = threadInfo {
|
||||
result.append(.mute)
|
||||
result.append(.search)
|
||||
} else {
|
||||
let hasVoiceChat = channel.flags.contains(.hasVoiceChat)
|
||||
let canStartVoiceChat = !hasVoiceChat && (channel.flags.contains(.isCreator) || channel.hasPermission(.manageCalls))
|
||||
let canManage = channel.flags.contains(.isCreator) || channel.adminRights != nil
|
||||
@ -1115,6 +1145,7 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro
|
||||
result.append(.more)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let group = peer as? TelegramGroup {
|
||||
let hasVoiceChat = group.flags.contains(.hasVoiceChat)
|
||||
let canStartVoiceChat: Bool
|
||||
|
||||
@ -308,6 +308,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||
|
||||
let avatarNode: AvatarNode
|
||||
fileprivate var videoNode: UniversalVideoNode?
|
||||
fileprivate var iconView: ComponentView<Empty>?
|
||||
private var videoContent: NativeVideoContent?
|
||||
private var videoStartTimestamp: Double?
|
||||
|
||||
@ -380,7 +381,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
var removedPhotoResourceIds = Set<String>()
|
||||
func update(peer: Peer?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool, isSettings: Bool) {
|
||||
func update(peer: Peer?, threadInfo: EngineMessageHistoryThread.Info?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool, isSettings: Bool) {
|
||||
if let peer = peer {
|
||||
let previousItem = self.item
|
||||
var item = item
|
||||
@ -410,6 +411,43 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||
|
||||
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)
|
||||
|
||||
if let threadInfo = threadInfo {
|
||||
self.avatarNode.isHidden = true
|
||||
|
||||
let iconView: ComponentView<Empty>
|
||||
if let current = self.iconView {
|
||||
iconView = current
|
||||
} else {
|
||||
iconView = ComponentView()
|
||||
self.iconView = iconView
|
||||
}
|
||||
let content: EmojiStatusComponent.Content
|
||||
if let iconFileId = threadInfo.icon {
|
||||
content = .animation(content: .customEmoji(fileId: iconFileId), size: CGSize(width: avatarSize, height: avatarSize), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: nil, loopMode: .forever)
|
||||
} else {
|
||||
content = .topic(title: String(threadInfo.title.prefix(1)), colorIndex: Int(threadInfo.iconColor))
|
||||
}
|
||||
let _ = iconView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(EmojiStatusComponent(
|
||||
context: self.context,
|
||||
animationCache: self.context.animationCache,
|
||||
animationRenderer: self.context.animationRenderer,
|
||||
content: content,
|
||||
isVisibleForAnimations: true,
|
||||
action: nil
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: avatarSize, height: avatarSize)
|
||||
)
|
||||
if let iconComponentView = iconView.view {
|
||||
if iconComponentView.superview == nil {
|
||||
self.avatarNode.view.superview?.addSubview(iconComponentView)
|
||||
}
|
||||
iconComponentView.frame = CGRect(origin: CGPoint(), size: CGSize(width: avatarSize, height: avatarSize))
|
||||
}
|
||||
}
|
||||
|
||||
let avatarCornerRadius: CGFloat
|
||||
if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) {
|
||||
avatarCornerRadius = floor(avatarSize * 0.25)
|
||||
@ -825,7 +863,7 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
|
||||
|
||||
let isReady = Promise<Bool>()
|
||||
|
||||
var arguments: (Peer?, PresentationTheme, CGFloat, Bool)?
|
||||
var arguments: (Peer?, EngineMessageHistoryThread.Info?, PresentationTheme, CGFloat, Bool)?
|
||||
var item: PeerInfoAvatarListItem?
|
||||
|
||||
var itemsUpdated: (([PeerInfoAvatarListItem]) -> Void)?
|
||||
@ -884,14 +922,14 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = items.first
|
||||
strongSelf.itemsUpdated?(items)
|
||||
if let (peer, theme, avatarSize, isExpanded) = strongSelf.arguments {
|
||||
strongSelf.avatarContainerNode.update(peer: peer, item: strongSelf.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, isSettings: strongSelf.isSettings)
|
||||
if let (peer, threadInfo, theme, avatarSize, isExpanded) = strongSelf.arguments {
|
||||
strongSelf.avatarContainerNode.update(peer: peer, threadInfo: threadInfo, item: strongSelf.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, isSettings: strongSelf.isSettings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.pinchSourceNode.activate = { [weak self] sourceNode in
|
||||
guard let strongSelf = self, let (_, _, _, isExpanded) = strongSelf.arguments, isExpanded else {
|
||||
guard let strongSelf = self, let (_, _, _, _, isExpanded) = strongSelf.arguments, isExpanded else {
|
||||
return
|
||||
}
|
||||
let pinchController = PinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
|
||||
@ -910,11 +948,11 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
func update(size: CGSize, avatarSize: CGFloat, isExpanded: Bool, peer: Peer?, theme: PresentationTheme, transition: ContainedViewLayoutTransition) {
|
||||
self.arguments = (peer, theme, avatarSize, isExpanded)
|
||||
func update(size: CGSize, avatarSize: CGFloat, isExpanded: Bool, peer: Peer?, threadInfo: EngineMessageHistoryThread.Info?, theme: PresentationTheme, transition: ContainedViewLayoutTransition) {
|
||||
self.arguments = (peer, threadInfo, theme, avatarSize, isExpanded)
|
||||
self.pinchSourceNode.update(size: size, transition: transition)
|
||||
self.pinchSourceNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.avatarContainerNode.update(peer: peer, item: self.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, isSettings: self.isSettings)
|
||||
self.avatarContainerNode.update(peer: peer, threadInfo: threadInfo, item: self.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, isSettings: self.isSettings)
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
@ -1996,6 +2034,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
private let isOpenedFromChat: Bool
|
||||
private let isSettings: Bool
|
||||
private let videoCallsEnabled: Bool
|
||||
private let forumTopicThreadId: Int64?
|
||||
|
||||
private(set) var isAvatarExpanded: Bool
|
||||
var skipCollapseCompletion = false
|
||||
@ -2056,12 +2095,13 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
var emojiStatusPackDisposable = MetaDisposable()
|
||||
var emojiStatusFileAndPackTitle = Promise<(TelegramMediaFile, LoadedStickerPack)?>()
|
||||
|
||||
init(context: AccountContext, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, isMediaOnly: Bool, isSettings: Bool) {
|
||||
init(context: AccountContext, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, isMediaOnly: Bool, isSettings: Bool, forumTopicThreadId: Int64?) {
|
||||
self.context = context
|
||||
self.isAvatarExpanded = avatarInitiallyExpanded
|
||||
self.isOpenedFromChat = isOpenedFromChat
|
||||
self.isSettings = isSettings
|
||||
self.videoCallsEnabled = true
|
||||
self.forumTopicThreadId = forumTopicThreadId
|
||||
|
||||
self.avatarListNode = PeerInfoAvatarListNode(context: context, readyWhenGalleryLoads: avatarInitiallyExpanded, isSettings: isSettings)
|
||||
|
||||
@ -2304,7 +2344,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
private var currentCredibilityIcon: CredibilityIcon?
|
||||
|
||||
private var currentPanelStatusData: PeerInfoStatusData?
|
||||
func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, paneContainerY: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, notificationSettings: TelegramPeerNotificationSettings?, statusData: PeerInfoStatusData?, panelStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?), isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, metrics: LayoutMetrics, transition: ContainedViewLayoutTransition, additive: Bool) -> CGFloat {
|
||||
func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, paneContainerY: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, threadInfo: EngineMessageHistoryThread.Info?, notificationSettings: TelegramPeerNotificationSettings?, statusData: PeerInfoStatusData?, panelStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?), isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, metrics: LayoutMetrics, transition: ContainedViewLayoutTransition, additive: Bool) -> CGFloat {
|
||||
self.state = state
|
||||
self.peer = peer
|
||||
self.avatarListNode.listContainerNode.peer = peer
|
||||
@ -2527,7 +2567,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
let expandedAvatarListHeight = min(width, containerHeight - expandedAvatarControlsHeight)
|
||||
let expandedAvatarListSize = CGSize(width: width, height: expandedAvatarListHeight)
|
||||
|
||||
let buttonKeys: [PeerInfoHeaderButtonKey] = self.isSettings ? [] : peerInfoHeaderButtons(peer: peer, cachedData: cachedData, isOpenedFromChat: self.isOpenedFromChat, isExpanded: true, videoCallsEnabled: width > 320.0 && self.videoCallsEnabled, isSecretChat: isSecretChat, isContact: isContact)
|
||||
let buttonKeys: [PeerInfoHeaderButtonKey] = self.isSettings ? [] : peerInfoHeaderButtons(peer: peer, cachedData: cachedData, isOpenedFromChat: self.isOpenedFromChat, isExpanded: true, videoCallsEnabled: width > 320.0 && self.videoCallsEnabled, isSecretChat: isSecretChat, isContact: isContact, threadInfo: threadInfo)
|
||||
|
||||
var isPremium = false
|
||||
var isVerified = false
|
||||
@ -2551,6 +2591,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
title = presentationData.strings.Conversation_SavedMessages
|
||||
} else if peer.id == self.context.account.peerId && !self.isSettings {
|
||||
title = presentationData.strings.DialogList_Replies
|
||||
} else if let threadInfo = threadInfo {
|
||||
title = threadInfo.title
|
||||
} else {
|
||||
title = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
}
|
||||
@ -2577,6 +2619,34 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
smallSubtitleString = NSAttributedString(string: subtitle, font: Font.regular(15.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.7))
|
||||
subtitleString = NSAttributedString(string: subtitle, font: Font.regular(17.0), textColor: presentationData.theme.list.itemSecondaryTextColor)
|
||||
usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor)
|
||||
} else if let _ = threadInfo {
|
||||
let subtitleColor: UIColor = presentationData.theme.list.itemSecondaryTextColor
|
||||
|
||||
//TODO:localize
|
||||
var statusText = "in "
|
||||
if let addressName = peer.addressName {
|
||||
statusText += "@\(addressName)"
|
||||
} else {
|
||||
statusText += peer.debugDisplayTitle
|
||||
}
|
||||
|
||||
smallSubtitleString = NSAttributedString(string: statusText, font: Font.regular(15.0), textColor: UIColor(rgb: 0xffffff, alpha: 0.7))
|
||||
subtitleString = NSAttributedString(string: statusText, font: Font.regular(17.0), textColor: subtitleColor)
|
||||
usernameString = NSAttributedString(string: "", font: Font.regular(15.0), textColor: presentationData.theme.list.itemSecondaryTextColor)
|
||||
|
||||
let (maybePanelStatusData, maybeNextPanelStatusData, _) = panelStatusData
|
||||
if let panelStatusData = maybePanelStatusData {
|
||||
let subtitleColor: UIColor
|
||||
if panelStatusData.isActivity {
|
||||
subtitleColor = presentationData.theme.list.itemAccentColor
|
||||
} else {
|
||||
subtitleColor = presentationData.theme.list.itemSecondaryTextColor
|
||||
}
|
||||
panelSubtitleString = NSAttributedString(string: panelStatusData.text, font: Font.regular(17.0), textColor: subtitleColor)
|
||||
}
|
||||
if let nextPanelStatusData = maybeNextPanelStatusData {
|
||||
nextPanelSubtitleString = NSAttributedString(string: nextPanelStatusData.text, font: Font.regular(17.0), textColor: presentationData.theme.list.itemSecondaryTextColor)
|
||||
}
|
||||
} else if let statusData = statusData {
|
||||
let subtitleColor: UIColor
|
||||
if statusData.isActivity {
|
||||
@ -2839,7 +2909,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
})
|
||||
}
|
||||
|
||||
self.avatarListNode.update(size: CGSize(), avatarSize: avatarSize, isExpanded: self.isAvatarExpanded, peer: peer, theme: presentationData.theme, transition: transition)
|
||||
self.avatarListNode.update(size: CGSize(), avatarSize: avatarSize, isExpanded: self.isAvatarExpanded, peer: peer, threadInfo: threadInfo, theme: presentationData.theme, transition: transition)
|
||||
self.editingContentNode.avatarNode.update(peer: peer, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing)
|
||||
self.avatarOverlayNode.update(peer: peer, item: self.avatarListNode.item, updatingAvatar: state.updatingAvatar, uploadProgress: state.avatarUploadProgress, theme: presentationData.theme, avatarSize: avatarSize, isEditing: state.isEditing)
|
||||
if additive {
|
||||
|
||||
@ -386,6 +386,8 @@ private final class PeerInfoPendingPane {
|
||||
openAddMemberAction: @escaping () -> Void,
|
||||
requestPerformPeerMemberAction: @escaping (PeerInfoMember, PeerMembersListAction) -> Void,
|
||||
peerId: PeerId,
|
||||
chatLocation: ChatLocation,
|
||||
chatLocationContextHolder: Atomic<ChatLocationContextHolder?>,
|
||||
key: PeerInfoPaneKey,
|
||||
hasBecomeReady: @escaping (PeerInfoPaneKey) -> Void,
|
||||
parentController: ViewController?,
|
||||
@ -396,7 +398,7 @@ private final class PeerInfoPendingPane {
|
||||
let paneNode: PeerInfoPaneNode
|
||||
switch key {
|
||||
case .media:
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, contentType: .photoOrVideo, captureProtected: captureProtected)
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, contentType: .photoOrVideo, captureProtected: captureProtected)
|
||||
paneNode = visualPaneNode
|
||||
visualPaneNode.openCurrentDate = {
|
||||
openMediaCalendar()
|
||||
@ -405,21 +407,19 @@ private final class PeerInfoPendingPane {
|
||||
paneDidScroll()
|
||||
}
|
||||
case .files:
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, contentType: .files, captureProtected: captureProtected)
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, contentType: .files, captureProtected: captureProtected)
|
||||
paneNode = visualPaneNode
|
||||
//paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: peerId, tagMask: .file)
|
||||
case .links:
|
||||
paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: peerId, tagMask: .webPage)
|
||||
paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: .webPage)
|
||||
case .voice:
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, contentType: .voiceAndVideoMessages, captureProtected: captureProtected)
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, contentType: .voiceAndVideoMessages, captureProtected: captureProtected)
|
||||
paneNode = visualPaneNode
|
||||
//paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: peerId, tagMask: .voiceOrInstantVideo)
|
||||
case .music:
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, contentType: .music, captureProtected: captureProtected)
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, contentType: .music, captureProtected: captureProtected)
|
||||
paneNode = visualPaneNode
|
||||
//paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: peerId, tagMask: .music)
|
||||
case .gifs:
|
||||
let visualPaneNode = PeerInfoGifPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, contentType: .gifs)
|
||||
let visualPaneNode = PeerInfoGifPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, contentType: .gifs)
|
||||
paneNode = visualPaneNode
|
||||
case .groupsInCommon:
|
||||
paneNode = PeerInfoGroupsInCommonPaneNode(context: context, peerId: peerId, chatControllerInteraction: chatControllerInteraction, openPeerContextAction: openPeerContextAction, groupsInCommonContext: data.groupsInCommon!)
|
||||
@ -452,6 +452,8 @@ private final class PeerInfoPendingPane {
|
||||
final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
private let context: AccountContext
|
||||
private let peerId: PeerId
|
||||
private let chatLocation: ChatLocation
|
||||
private let chatLocationContextHolder: Atomic<ChatLocationContextHolder?>
|
||||
private let isMediaOnly: Bool
|
||||
|
||||
weak var parentController: ViewController?
|
||||
@ -509,10 +511,12 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
||||
private var currentAvailablePanes: [PeerInfoPaneKey]?
|
||||
private let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
|
||||
|
||||
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, isMediaOnly: Bool) {
|
||||
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, isMediaOnly: Bool) {
|
||||
self.context = context
|
||||
self.updatedPresentationData = updatedPresentationData
|
||||
self.peerId = peerId
|
||||
self.chatLocation = chatLocation
|
||||
self.chatLocationContextHolder = chatLocationContextHolder
|
||||
self.isMediaOnly = isMediaOnly
|
||||
|
||||
self.additionalBackgroundNode = ASDisplayNode()
|
||||
@ -807,6 +811,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
||||
self?.requestPerformPeerMemberAction?(member, action)
|
||||
},
|
||||
peerId: self.peerId,
|
||||
chatLocation: self.chatLocation,
|
||||
chatLocationContextHolder: self.chatLocationContextHolder,
|
||||
key: key,
|
||||
hasBecomeReady: { [weak self] key in
|
||||
let apply: () -> Void = {
|
||||
|
||||
@ -77,6 +77,7 @@ import AvatarNode
|
||||
import ComponentFlow
|
||||
import EmojiStatusComponent
|
||||
import ChatTitleView
|
||||
import ForumCreateTopicScreen
|
||||
|
||||
protocol PeerInfoScreenItem: AnyObject {
|
||||
var id: AnyHashable { get }
|
||||
@ -423,7 +424,7 @@ private enum PeerInfoMemberAction {
|
||||
private enum PeerInfoContextSubject {
|
||||
case bio
|
||||
case phone(String)
|
||||
case link
|
||||
case link(customLink: String?)
|
||||
}
|
||||
|
||||
private enum PeerInfoSettingsSection {
|
||||
@ -915,7 +916,7 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat
|
||||
return result
|
||||
}
|
||||
|
||||
private func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message]) -> [(AnyHashable, [PeerInfoScreenItem])] {
|
||||
private func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], chatLocation: ChatLocation) -> [(AnyHashable, [PeerInfoScreenItem])] {
|
||||
guard let data = data else {
|
||||
return []
|
||||
}
|
||||
@ -973,7 +974,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
action: { _ in
|
||||
interaction.openUsername(username)
|
||||
}, longTapAction: { sourceNode in
|
||||
interaction.openPeerInfoContextMenu(.link, sourceNode)
|
||||
interaction.openPeerInfoContextMenu(.link(customLink: nil), sourceNode)
|
||||
}, linkItemAction: { type, item in
|
||||
if case .tap = type {
|
||||
if case let .mention(username) = item {
|
||||
@ -1077,6 +1078,46 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
let ItemMembers = 6
|
||||
let ItemMemberRequests = 7
|
||||
|
||||
if let _ = data.threadInfo {
|
||||
let mainUsername: String
|
||||
if let addressName = channel.addressName {
|
||||
mainUsername = addressName
|
||||
} else {
|
||||
mainUsername = "c/\(channel.id.id._internalGetInt64Value())"
|
||||
}
|
||||
|
||||
var threadId: Int64 = 0
|
||||
if case let .replyThread(message) = chatLocation {
|
||||
threadId = Int64(message.messageId.id)
|
||||
}
|
||||
|
||||
let linkText = "https://t.me/\(mainUsername)?topic=\(threadId)"
|
||||
|
||||
items[.peerInfo]!.append(
|
||||
PeerInfoScreenLabeledValueItem(
|
||||
id: ItemUsername,
|
||||
label: presentationData.strings.Channel_LinkItem,
|
||||
text: linkText,
|
||||
textColor: .accent,
|
||||
icon: .qrCode,
|
||||
action: { _ in
|
||||
interaction.openUsername(linkText)
|
||||
}, longTapAction: { sourceNode in
|
||||
interaction.openPeerInfoContextMenu(.link(customLink: linkText), sourceNode)
|
||||
}, linkItemAction: { type, item in
|
||||
if case .tap = type {
|
||||
if case let .mention(username) = item {
|
||||
interaction.openUsername(String(username.suffix(from: username.index(username.startIndex, offsetBy: 1))))
|
||||
}
|
||||
}
|
||||
}, iconAction: {
|
||||
interaction.openQrCode()
|
||||
}, requestLayout: {
|
||||
interaction.requestLayout(false)
|
||||
}
|
||||
)
|
||||
)
|
||||
} else {
|
||||
if let location = (data.cachedData as? CachedChannelData)?.peerGeoLocation {
|
||||
items[.groupLocation]!.append(PeerInfoScreenHeaderItem(id: ItemLocationHeader, text: presentationData.strings.GroupInfo_Location.uppercased()))
|
||||
|
||||
@ -1112,7 +1153,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
action: { _ in
|
||||
interaction.openUsername(username)
|
||||
}, longTapAction: { sourceNode in
|
||||
interaction.openPeerInfoContextMenu(.link, sourceNode)
|
||||
interaction.openPeerInfoContextMenu(.link(customLink: nil), sourceNode)
|
||||
}, linkItemAction: { type, item in
|
||||
if case .tap = type {
|
||||
if case let .mention(username) = item {
|
||||
@ -1183,6 +1224,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let group = data.peer as? TelegramGroup {
|
||||
if let cachedData = data.cachedData as? CachedGroupData {
|
||||
let aboutText: String?
|
||||
@ -1740,6 +1782,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
private let isOpenedFromChat: Bool
|
||||
private let videoCallsEnabled: Bool
|
||||
private let callMessages: [Message]
|
||||
private let chatLocation: ChatLocation
|
||||
private let chatLocationContextHolder: Atomic<ChatLocationContextHolder?>
|
||||
|
||||
let isSettings: Bool
|
||||
private let isMediaOnly: Bool
|
||||
@ -1834,7 +1878,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
private var didSetReady = false
|
||||
|
||||
init(controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool, hintGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?) {
|
||||
init(controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool, hintGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>) {
|
||||
self.controller = controller
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
@ -1845,14 +1889,20 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
self.reactionSourceMessageId = reactionSourceMessageId
|
||||
self.callMessages = callMessages
|
||||
self.isSettings = isSettings
|
||||
self.chatLocation = chatLocation
|
||||
self.chatLocationContextHolder = chatLocationContextHolder
|
||||
self.isMediaOnly = context.account.peerId == peerId && !isSettings
|
||||
|
||||
self.scrollNode = ASScrollNode()
|
||||
self.scrollNode.view.delaysContentTouches = false
|
||||
self.scrollNode.canCancelAllTouchesInViews = true
|
||||
|
||||
self.headerNode = PeerInfoHeaderNode(context: context, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, isMediaOnly: self.isMediaOnly, isSettings: isSettings)
|
||||
self.paneContainerNode = PeerInfoPaneContainerNode(context: context, updatedPresentationData: controller.updatedPresentationData, peerId: peerId, isMediaOnly: self.isMediaOnly)
|
||||
var forumTopicThreadId: Int64?
|
||||
if case let .replyThread(message) = chatLocation {
|
||||
forumTopicThreadId = Int64(message.messageId.id)
|
||||
}
|
||||
self.headerNode = PeerInfoHeaderNode(context: context, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, isMediaOnly: self.isMediaOnly, isSettings: isSettings, forumTopicThreadId: forumTopicThreadId)
|
||||
self.paneContainerNode = PeerInfoPaneContainerNode(context: context, updatedPresentationData: controller.updatedPresentationData, peerId: peerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, isMediaOnly: self.isMediaOnly)
|
||||
|
||||
super.init()
|
||||
|
||||
@ -2765,6 +2815,21 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
switch key {
|
||||
case .edit:
|
||||
if case let .replyThread(message) = strongSelf.chatLocation {
|
||||
let threadId = Int64(message.messageId.id)
|
||||
if let threadInfo = strongSelf.data?.threadInfo {
|
||||
let controller = ForumCreateTopicScreen(context: strongSelf.context, peerId: strongSelf.peerId, mode: .edit(topic: threadInfo))
|
||||
controller.navigationPresentation = .modal
|
||||
let context = strongSelf.context
|
||||
controller.completion = { [weak controller] title, fileId in
|
||||
let _ = (context.engine.peers.editForumChannelTopic(id: peerId, threadId: threadId, title: title, iconFileId: fileId)
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
controller?.dismiss()
|
||||
})
|
||||
}
|
||||
strongSelf.controller?.push(controller)
|
||||
}
|
||||
} else {
|
||||
(strongSelf.controller?.parent as? TabBarController)?.updateIsTabBarHidden(true, transition: .animated(duration: 0.3, curve: .linear))
|
||||
strongSelf.state = strongSelf.state.withIsEditing(true)
|
||||
var updateOnCompletion = false
|
||||
@ -2795,6 +2860,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
})
|
||||
strongSelf.controller?.navigationItem.setLeftBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, style: .plain, target: strongSelf, action: #selector(strongSelf.editingCancelPressed)), animated: true)
|
||||
}
|
||||
case .done, .cancel:
|
||||
strongSelf.view.endEditing(true)
|
||||
if case .done = key {
|
||||
@ -3207,7 +3273,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
strongSelf.controller?.present(emojiStatusSelectionController, in: .window(.root))
|
||||
}
|
||||
} else {
|
||||
screenData = peerInfoScreenData(context: context, peerId: peerId, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, isSettings: self.isSettings, hintGroupInCommon: hintGroupInCommon, existingRequestsContext: requestsContext)
|
||||
screenData = peerInfoScreenData(context: context, peerId: peerId, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, isSettings: self.isSettings, hintGroupInCommon: hintGroupInCommon, existingRequestsContext: requestsContext, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder)
|
||||
|
||||
self.headerNode.displayPremiumIntro = { [weak self] sourceView, peerStatus, emojiStatusFileAndPack, white in
|
||||
guard let strongSelf = self else {
|
||||
@ -4085,8 +4151,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
return .single(items)
|
||||
}
|
||||
|
||||
let allHeaderButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: strongSelf.isOpenedFromChat, isExpanded: false, videoCallsEnabled: strongSelf.videoCallsEnabled, isSecretChat: strongSelf.peerId.namespace == Namespaces.Peer.SecretChat, isContact: strongSelf.data?.isContact ?? false))
|
||||
let headerButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: strongSelf.isOpenedFromChat, isExpanded: true, videoCallsEnabled: strongSelf.videoCallsEnabled, isSecretChat: strongSelf.peerId.namespace == Namespaces.Peer.SecretChat, isContact: strongSelf.data?.isContact ?? false))
|
||||
let allHeaderButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: strongSelf.isOpenedFromChat, isExpanded: false, videoCallsEnabled: strongSelf.videoCallsEnabled, isSecretChat: strongSelf.peerId.namespace == Namespaces.Peer.SecretChat, isContact: strongSelf.data?.isContact ?? false, threadInfo: data.threadInfo))
|
||||
let headerButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: strongSelf.isOpenedFromChat, isExpanded: true, videoCallsEnabled: strongSelf.videoCallsEnabled, isSecretChat: strongSelf.peerId.namespace == Namespaces.Peer.SecretChat, isContact: strongSelf.data?.isContact ?? false, threadInfo: strongSelf.data?.threadInfo))
|
||||
|
||||
let filteredButtons = allHeaderButtons.subtracting(headerButtons)
|
||||
|
||||
@ -4538,6 +4604,19 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
|
||||
private func openChatWithMessageSearch() {
|
||||
if let navigationController = (self.controller?.navigationController as? NavigationController) {
|
||||
if case let .replyThread(currentMessage) = self.chatLocation, let current = navigationController.viewControllers.first(where: { controller in
|
||||
if let controller = controller as? ChatControllerImpl, case let .replyThread(message) = controller.chatLocation, message.messageId == currentMessage.messageId {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}) as? ChatControllerImpl {
|
||||
var viewControllers = navigationController.viewControllers
|
||||
if let index = viewControllers.firstIndex(of: current) {
|
||||
viewControllers.removeSubrange(index + 1 ..< viewControllers.count)
|
||||
}
|
||||
navigationController.setViewControllers(viewControllers, animated: true)
|
||||
current.activateSearch()
|
||||
} else {
|
||||
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(id: self.peerId), keepStack: self.nearbyPeerDistance != nil ? .always : .default, activateMessageSearch: (.everything, ""), peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), completion: { [weak self] _ in
|
||||
if let strongSelf = self, strongSelf.nearbyPeerDistance != nil {
|
||||
var viewControllers = navigationController.viewControllers
|
||||
@ -4552,6 +4631,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func openChatForReporting(_ reason: ReportReason) {
|
||||
if let navigationController = (self.controller?.navigationController as? NavigationController) {
|
||||
@ -4779,7 +4859,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
|
||||
private func openUsername(value: String) {
|
||||
let shareController = ShareController(context: self.context, subject: .url("https://t.me/\(value)"), updatedPresentationData: self.controller?.updatedPresentationData)
|
||||
let url: String
|
||||
if value.hasPrefix("https://") {
|
||||
url = value
|
||||
} else {
|
||||
url = "https://t.me/\(value)"
|
||||
}
|
||||
|
||||
let shareController = ShareController(context: self.context, subject: .url(url), updatedPresentationData: self.controller?.updatedPresentationData)
|
||||
shareController.completed = { [weak self] peerIds in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -5940,10 +6027,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
return nil
|
||||
}
|
||||
}))
|
||||
case .link:
|
||||
if let addressName = peer.addressName {
|
||||
case let .link(customLink):
|
||||
let text: String
|
||||
let content: UndoOverlayContent
|
||||
if let customLink = customLink {
|
||||
text = customLink
|
||||
content = .linkCopied(text: self.presentationData.strings.Conversation_LinkCopied)
|
||||
} else if let addressName = peer.addressName {
|
||||
if peer is TelegramChannel {
|
||||
text = "https://t.me/\(addressName)"
|
||||
content = .linkCopied(text: self.presentationData.strings.Conversation_LinkCopied)
|
||||
@ -5951,6 +6041,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
text = "@" + addressName
|
||||
content = .copy(text: self.presentationData.strings.Conversation_UsernameCopied)
|
||||
}
|
||||
} else {
|
||||
text = "https://t.me/@id\(peer.id.id._internalGetInt64Value())"
|
||||
content = .linkCopied(text: self.presentationData.strings.Conversation_LinkCopied)
|
||||
}
|
||||
|
||||
let contextMenuController = ContextMenuController(actions: [ContextMenuAction(content: .text(title: self.presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: self.presentationData.strings.Conversation_ContextMenuCopy), action: { [weak self] in
|
||||
UIPasteboard.general.string = text
|
||||
|
||||
@ -5966,7 +6061,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func performBioLinkAction(action: TextLinkItemActionType, item: TextLinkItem) {
|
||||
guard let data = self.data, let peer = data.peer, let controller = self.controller else {
|
||||
@ -6460,7 +6554,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
return
|
||||
}
|
||||
|
||||
controller.present(ChatQrCodeScreen(context: self.context, subject: .peer(peer)), in: .window(.root))
|
||||
var threadId: Int64 = 0
|
||||
if case let .replyThread(message) = self.chatLocation {
|
||||
threadId = Int64(message.messageId.id)
|
||||
}
|
||||
|
||||
controller.present(ChatQrCodeScreen(context: self.context, subject: .peer(peer: peer, threadId: threadId)), in: .window(.root))
|
||||
}
|
||||
|
||||
fileprivate func openSettings(section: PeerInfoSettingsSection) {
|
||||
@ -7428,7 +7527,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
let headerInset = sectionInset
|
||||
|
||||
var headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, metrics: layout.metrics, transition: transition, additive: additive)
|
||||
var headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, threadInfo: self.data?.threadInfo, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, metrics: layout.metrics, transition: transition, additive: additive)
|
||||
if !self.isSettings && !self.state.isEditing {
|
||||
headerHeight += 71.0
|
||||
}
|
||||
@ -7448,7 +7547,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
insets.left += sectionInset
|
||||
insets.right += sectionInset
|
||||
|
||||
let items = self.isSettings ? settingsItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, isExpanded: self.headerNode.isAvatarExpanded) : infoItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages)
|
||||
let items = self.isSettings ? settingsItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, isExpanded: self.headerNode.isAvatarExpanded) : infoItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, chatLocation: self.chatLocation)
|
||||
|
||||
contentHeight += headerHeight
|
||||
if !(self.isSettings && self.state.isEditing) {
|
||||
@ -7788,7 +7887,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
let headerInset = sectionInset
|
||||
|
||||
let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, metrics: layout.metrics, transition: transition, additive: additive)
|
||||
let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, threadInfo: self.data?.threadInfo, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, metrics: layout.metrics, transition: transition, additive: additive)
|
||||
}
|
||||
|
||||
let paneAreaExpansionDistance: CGFloat = 32.0
|
||||
@ -8068,6 +8167,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
||||
private let isSettings: Bool
|
||||
private let hintGroupInCommon: PeerId?
|
||||
private weak var requestsContext: PeerInvitationImportersContext?
|
||||
private let chatLocation: ChatLocation
|
||||
private let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
|
||||
|
||||
fileprivate var presentationData: PresentationData
|
||||
private var presentationDataDisposable: Disposable?
|
||||
@ -8102,7 +8203,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
||||
|
||||
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool = false, hintGroupInCommon: PeerId? = nil, requestsContext: PeerInvitationImportersContext? = nil) {
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool = false, hintGroupInCommon: PeerId? = nil, requestsContext: PeerInvitationImportersContext? = nil, forumTopicThread: ChatReplyThreadMessage? = nil) {
|
||||
self.context = context
|
||||
self.updatedPresentationData = updatedPresentationData
|
||||
self.peerId = peerId
|
||||
@ -8115,6 +8216,12 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
||||
self.hintGroupInCommon = hintGroupInCommon
|
||||
self.requestsContext = requestsContext
|
||||
|
||||
if let forumTopicThread = forumTopicThread {
|
||||
self.chatLocation = .replyThread(message: forumTopicThread)
|
||||
} else {
|
||||
self.chatLocation = .peer(id: peerId)
|
||||
}
|
||||
|
||||
self.presentationData = updatedPresentationData?.0 ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let baseNavigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData)
|
||||
@ -8299,6 +8406,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
||||
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
|
||||
}
|
||||
|
||||
if case .peer = self.chatLocation {
|
||||
self.navigationBar?.makeCustomTransitionNode = { [weak self] other, isInteractive in
|
||||
guard let strongSelf = self else {
|
||||
return nil
|
||||
@ -8326,6 +8434,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
self.setStatusBarStyle(avatarInitiallyExpanded ? .White : self.presentationData.theme.rootController.statusBarStyle.style, animated: false)
|
||||
|
||||
@ -8396,7 +8505,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, isSettings: self.isSettings, hintGroupInCommon: self.hintGroupInCommon, requestsContext: requestsContext)
|
||||
self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, isSettings: self.isSettings, hintGroupInCommon: self.hintGroupInCommon, requestsContext: self.requestsContext, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder)
|
||||
self.controllerNode.accountsAndPeers.set(self.accountsAndPeers.get() |> map { $0.1 })
|
||||
self.controllerNode.activeSessionsContextAndCount.set(self.activeSessionsContextAndCount.get())
|
||||
self.cachedDataPromise.set(self.controllerNode.cachedDataPromise.get())
|
||||
@ -8795,7 +8904,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig
|
||||
}
|
||||
let headerInset = sectionInset
|
||||
|
||||
topHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, isMediaOnly: false, contentOffset: 0.0, paneContainerY: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, notificationSettings: self.screenNode.data?.notificationSettings, statusData: self.screenNode.data?.status, panelStatusData: (nil, nil, nil), isSecretChat: self.screenNode.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.screenNode.data?.isContact ?? false, isSettings: self.screenNode.isSettings, state: self.screenNode.state, metrics: layout.metrics, transition: transition, additive: false)
|
||||
topHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, isMediaOnly: false, contentOffset: 0.0, paneContainerY: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, threadInfo: self.screenNode.data?.threadInfo, notificationSettings: self.screenNode.data?.notificationSettings, statusData: self.screenNode.data?.status, panelStatusData: (nil, nil, nil), isSecretChat: self.screenNode.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.screenNode.data?.isContact ?? false, isSettings: self.screenNode.isSettings, state: self.screenNode.state, metrics: layout.metrics, transition: transition, additive: false)
|
||||
}
|
||||
|
||||
let titleScale = (fraction * previousTitleNode.view.bounds.height + (1.0 - fraction) * self.headerNode.titleNodeRawContainer.bounds.height) / previousTitleNode.view.bounds.height
|
||||
|
||||
@ -761,6 +761,8 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDe
|
||||
|
||||
private let context: AccountContext
|
||||
private let peerId: PeerId
|
||||
private let chatLocation: ChatLocation
|
||||
private let chatLocationContextHolder: Atomic<ChatLocationContextHolder?>
|
||||
private let chatControllerInteraction: ChatControllerInteraction
|
||||
private let contentType: ContentType
|
||||
|
||||
@ -809,9 +811,11 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDe
|
||||
return 0.0
|
||||
}
|
||||
|
||||
init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, contentType: ContentType) {
|
||||
init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, contentType: ContentType) {
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
self.chatLocation = chatLocation
|
||||
self.chatLocationContextHolder = chatLocationContextHolder
|
||||
self.chatControllerInteraction = chatControllerInteraction
|
||||
self.contentType = contentType
|
||||
|
||||
|
||||
@ -1468,8 +1468,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return recentSessionsController(context: context, activeSessionsContext: activeSessionsContext, webSessionsContext: context.engine.privacy.webSessions(), websitesOnly: false)
|
||||
}
|
||||
|
||||
public func makeChatQrCodeScreen(context: AccountContext, peer: Peer) -> ViewController {
|
||||
return ChatQrCodeScreen(context: context, subject: .peer(peer))
|
||||
public func makeChatQrCodeScreen(context: AccountContext, peer: Peer, threadId: Int64?) -> ViewController {
|
||||
return ChatQrCodeScreen(context: context, subject: .peer(peer: peer, threadId: threadId))
|
||||
}
|
||||
|
||||
public func makePrivacyAndSecurityController(context: AccountContext) -> ViewController {
|
||||
@ -1532,7 +1532,14 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation
|
||||
if let _ = peer as? TelegramGroup {
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [])
|
||||
} else if let _ = peer as? TelegramChannel {
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [])
|
||||
var forumTopicThread: ChatReplyThreadMessage?
|
||||
switch mode {
|
||||
case let .forumTopic(thread):
|
||||
forumTopicThread = thread
|
||||
default:
|
||||
break
|
||||
}
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [], forumTopicThread: forumTopicThread)
|
||||
} else if peer is TelegramUser {
|
||||
var nearbyPeerDistance: Int32?
|
||||
var reactionSourceMessageId: MessageId?
|
||||
@ -1549,6 +1556,8 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation
|
||||
hintGroupInCommon = id
|
||||
case let .reaction(messageId):
|
||||
reactionSourceMessageId = messageId
|
||||
case .forumTopic:
|
||||
break
|
||||
}
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, reactionSourceMessageId: reactionSourceMessageId, callMessages: callMessages, hintGroupInCommon: hintGroupInCommon)
|
||||
} else if peer is TelegramSecretChat {
|
||||
|
||||
@ -73,7 +73,12 @@ public enum ParsedInternalPeerUrlParameter {
|
||||
}
|
||||
|
||||
public enum ParsedInternalUrl {
|
||||
case peerName(String, ParsedInternalPeerUrlParameter?)
|
||||
public enum UrlPeerReference {
|
||||
case name(String)
|
||||
case id(PeerId)
|
||||
}
|
||||
|
||||
case peer(UrlPeerReference, ParsedInternalPeerUrlParameter?)
|
||||
case peerId(PeerId)
|
||||
case privateMessage(messageId: MessageId, threadId: Int32?, timecode: Double?)
|
||||
case stickerPack(name: String, type: StickerPackUrlType)
|
||||
@ -193,9 +198,9 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
break
|
||||
}
|
||||
}
|
||||
return .peerName(peerName, .attachBotStart(value, startAttach))
|
||||
return .peer(.name(peerName), .attachBotStart(value, startAttach))
|
||||
} else if queryItem.name == "start" {
|
||||
return .peerName(peerName, .botStart(value))
|
||||
return .peer(.name(peerName), .botStart(value))
|
||||
} else if queryItem.name == "startgroup" {
|
||||
var botAdminRights: ResolvedBotAdminRights?
|
||||
for queryItem in queryItems {
|
||||
@ -204,11 +209,11 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
break
|
||||
}
|
||||
}
|
||||
return .peerName(peerName, .groupBotStart(value, botAdminRights))
|
||||
return .peer(.name(peerName), .groupBotStart(value, botAdminRights))
|
||||
} else if queryItem.name == "game" {
|
||||
return nil
|
||||
} else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) {
|
||||
return .peerName(peerName, .voiceChat(value))
|
||||
return .peer(.name(peerName), .voiceChat(value))
|
||||
} else if queryItem.name == "startattach" {
|
||||
var choose: String?
|
||||
for queryItem in queryItems {
|
||||
@ -220,7 +225,7 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
return .startAttach(peerName, value, choose)
|
||||
}
|
||||
} else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) {
|
||||
return .peerName(peerName, .voiceChat(nil))
|
||||
return .peer(.name(peerName), .voiceChat(nil))
|
||||
} else if queryItem.name == "startattach" {
|
||||
var choose: String?
|
||||
for queryItem in queryItems {
|
||||
@ -238,7 +243,7 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
break
|
||||
}
|
||||
}
|
||||
return .peerName(peerName, .groupBotStart("", botAdminRights))
|
||||
return .peer(.name(peerName), .groupBotStart("", botAdminRights))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -269,7 +274,7 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
let component = pathComponents[0].replacingOccurrences(of: "%24", with: "$")
|
||||
return .invoice(component)
|
||||
}
|
||||
return .peerName(peerName, nil)
|
||||
return .peer(.name(peerName), nil)
|
||||
} else if pathComponents.count == 2 || pathComponents.count == 3 {
|
||||
if pathComponents[0] == "addstickers" {
|
||||
return .stickerPack(name: pathComponents[1], type: .stickers)
|
||||
@ -408,7 +413,7 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
if let queryItems = components.queryItems {
|
||||
for queryItem in queryItems {
|
||||
if let value = queryItem.value {
|
||||
if queryItem.name == "thread" {
|
||||
if queryItem.name == "thread" || queryItem.name == "topic" {
|
||||
if let intValue = Int32(value) {
|
||||
threadId = intValue
|
||||
}
|
||||
@ -424,6 +429,29 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else if pathComponents.count == 2 && pathComponents[0] == "c" {
|
||||
if let channelId = Int64(pathComponents[1]) {
|
||||
var threadId: Int32?
|
||||
if let queryItems = components.queryItems {
|
||||
for queryItem in queryItems {
|
||||
if let value = queryItem.value {
|
||||
if queryItem.name == "thread" || queryItem.name == "topic" {
|
||||
if let intValue = Int32(value) {
|
||||
threadId = intValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let threadId = threadId {
|
||||
return .peer(.id(PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))), .replyThread(threadId, threadId))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else if let value = Int32(pathComponents[1]) {
|
||||
var threadId: Int32?
|
||||
var commentId: Int32?
|
||||
@ -431,7 +459,7 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
if let queryItems = components.queryItems {
|
||||
for queryItem in queryItems {
|
||||
if let value = queryItem.value {
|
||||
if queryItem.name == "thread" {
|
||||
if queryItem.name == "thread" || queryItem.name == "topic" {
|
||||
if let intValue = Int32(value) {
|
||||
threadId = intValue
|
||||
}
|
||||
@ -448,11 +476,11 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
||||
}
|
||||
}
|
||||
if let threadId = threadId {
|
||||
return .peerName(peerName, .replyThread(threadId, value))
|
||||
return .peer(.name(peerName), .replyThread(threadId, value))
|
||||
} else if let commentId = commentId {
|
||||
return .peerName(peerName, .replyThread(value, commentId))
|
||||
return .peer(.name(peerName), .replyThread(value, commentId))
|
||||
} else {
|
||||
return .peerName(peerName, .channelMessage(value, timecode))
|
||||
return .peer(.name(peerName), .channelMessage(value, timecode))
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
@ -489,12 +517,36 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
||||
return .single(.peer(nil, .info))
|
||||
}
|
||||
}
|
||||
case let .peerName(name, parameter):
|
||||
return context.engine.peers.resolvePeerByName(name: name)
|
||||
case let .peer(reference, parameter):
|
||||
let resolvedPeer: Signal<Peer?, NoError>
|
||||
switch reference {
|
||||
case let .name(name):
|
||||
resolvedPeer = context.engine.peers.resolvePeerByName(name: name)
|
||||
|> take(1)
|
||||
|> mapToSignal { peer -> Signal<Peer?, NoError> in
|
||||
return .single(peer?._asPeer())
|
||||
}
|
||||
case let .id(id):
|
||||
if id.namespace == Namespaces.Peer.CloudChannel {
|
||||
resolvedPeer = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: id))
|
||||
|> mapToSignal { peer -> Signal<Peer?, NoError> in
|
||||
let foundPeer: Signal<Peer?, NoError>
|
||||
if let peer = peer {
|
||||
foundPeer = .single(peer._asPeer())
|
||||
} else {
|
||||
foundPeer = TelegramEngine(account: context.account).peers.findChannelById(channelId: id.id._internalGetInt64Value())
|
||||
|> map { peer -> Peer? in
|
||||
return peer?._asPeer()
|
||||
}
|
||||
}
|
||||
return foundPeer
|
||||
}
|
||||
} else {
|
||||
resolvedPeer = .single(nil)
|
||||
}
|
||||
}
|
||||
|
||||
return resolvedPeer
|
||||
|> mapToSignal { peer -> Signal<ResolvedUrl?, NoError> in
|
||||
if let peer = peer {
|
||||
if let parameter = parameter {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user