Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2020-10-17 22:09:57 +04:00
commit ce911f3f9b
38 changed files with 1884 additions and 73 deletions

View File

@ -1 +1 @@
8tRwQybvfoDddhSIfdMSOMv4FZd9LSHiWmObmx6d7rE=
gCh0ST/jBZ+NM8mvcBcsd12A5FMFT4q6fETcWd5elO0=

View File

@ -22,15 +22,20 @@ public enum PeerMessagesMediaPlaylistId: Equatable, SharedMediaPlaylistId {
}
public enum PeerMessagesPlaylistLocation: Equatable, SharedMediaPlaylistLocation {
case messages(peerId: PeerId, tagMask: MessageTags, at: MessageId)
case messages(chatLocation: ChatLocation, tagMask: MessageTags, at: MessageId)
case singleMessage(MessageId)
case recentActions(Message)
case custom(messages: Signal<([Message], Int32, Bool), NoError>, at: MessageId, loadMore: (() -> Void)?)
public var playlistId: PeerMessagesMediaPlaylistId {
switch self {
case let .messages(peerId, _, _):
return .peer(peerId)
case let .messages(chatLocation, _, _):
switch chatLocation {
case let .peer(peerId):
return .peer(peerId)
case let .replyThread(replyThreaMessage):
return .peer(replyThreaMessage.messageId.peerId)
}
case let .singleMessage(id):
return .peer(id.peerId)
case let .recentActions(message):
@ -59,8 +64,8 @@ public enum PeerMessagesPlaylistLocation: Equatable, SharedMediaPlaylistLocation
public static func ==(lhs: PeerMessagesPlaylistLocation, rhs: PeerMessagesPlaylistLocation) -> Bool {
switch lhs {
case let .messages(peerId, tagMask, at):
if case .messages(peerId, tagMask, at) = rhs {
case let .messages(chatLocation, tagMask, at):
if case .messages(chatLocation, tagMask, at) = rhs {
return true
} else {
return false

View File

@ -13,6 +13,7 @@ import ItemListUI
import PresentationDataUtils
import OverlayStatusController
import AccountContext
import TelegramCallsUI
@objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate {
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
@ -74,6 +75,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
case alternativeFolderTabs(Bool)
case playerEmbedding(Bool)
case playlistPlayback(Bool)
case voiceConference
case preferredVideoCodec(Int, String, String?, Bool)
case disableVideoAspectScaling(Bool)
case enableVoipTcp(Bool)
@ -90,7 +92,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return DebugControllerSection.logging.rawValue
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
return DebugControllerSection.experiments.rawValue
case .clearTips, .reimport, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .alternativeFolderTabs, .playerEmbedding, .playlistPlayback:
case .clearTips, .reimport, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .alternativeFolderTabs, .playerEmbedding, .playlistPlayback, .voiceConference:
return DebugControllerSection.experiments.rawValue
case .preferredVideoCodec:
return DebugControllerSection.videoExperiments.rawValue
@ -155,8 +157,10 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return 24
case .playlistPlayback:
return 25
case .voiceConference:
return 26
case let .preferredVideoCodec(index, _, _, _):
return 26 + index
return 27 + index
case .disableVideoAspectScaling:
return 100
case .enableVoipTcp:
@ -648,6 +652,15 @@ private enum DebugControllerEntry: ItemListNodeEntry {
})
}).start()
})
case .voiceConference:
return ItemListDisclosureItem(presentationData: presentationData, title: "Voice Conference (Test)", label: "", sectionId: self.section, style: .blocks, action: {
guard let context = arguments.context else {
return
}
let controller = GroupCallController(context: context)
controller.navigationPresentation = .modal
arguments.pushController(controller)
})
case let .preferredVideoCodec(_, title, value, isSelected):
return ItemListCheckboxItem(presentationData: presentationData, title: title, style: .right, checked: isSelected, zeroSeparatorInsets: false, sectionId: self.section, action: {
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
@ -725,6 +738,8 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
entries.append(.voiceConference)
let codecs: [(String, String?)] = [
("No Preference", nil),
("H265", "H265"),

View File

@ -0,0 +1,147 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import TelegramPresentationData
import TelegramUIPreferences
import TelegramVoip
import TelegramAudio
import AccountContext
public final class GroupCallController: ViewController {
private final class Node: ViewControllerTracingNode {
private let context: AccountContext
private let presentationData: PresentationData
private var callContext: GroupCallContext?
private var callDisposable: Disposable?
private var memberCountDisposable: Disposable?
private var isMutedDisposable: Disposable?
private let audioSessionActive = Promise<Bool>(false)
private var memberCount: Int = 0
private let memberCountNode: ImmediateTextNode
private var isMuted: Bool = false
private let isMutedNode: ImmediateTextNode
private let muteButton: HighlightableButtonNode
private var validLayout: ContainerViewLayout?
init(context: AccountContext) {
self.context = context
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.memberCountNode = ImmediateTextNode()
self.isMutedNode = ImmediateTextNode()
self.muteButton = HighlightableButtonNode()
super.init()
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
self.addSubnode(self.memberCountNode)
self.muteButton.addSubnode(self.isMutedNode)
self.addSubnode(self.muteButton)
let audioSessionActive = self.audioSessionActive
self.callDisposable = self.context.sharedContext.mediaManager.audioSession.push(audioSessionType: .voiceCall, manualActivate: { audioSessionControl in
audioSessionControl.activate({ _ in })
audioSessionActive.set(.single(true))
}, deactivate: {
return Signal { subscriber in
subscriber.putCompletion()
return EmptyDisposable
}
}, availableOutputsChanged: { _, _ in
})
let callContext = GroupCallContext(audioSessionActive: self.audioSessionActive.get())
self.callContext = callContext
self.memberCountDisposable = (callContext.memberCount
|> deliverOnMainQueue).start(next: { [weak self] value in
guard let strongSelf = self else {
return
}
strongSelf.memberCount = value
if let layout = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout, transition: .immediate)
}
})
self.isMutedDisposable = (callContext.isMuted
|> deliverOnMainQueue).start(next: { [weak self] value in
guard let strongSelf = self else {
return
}
strongSelf.isMuted = value
if let layout = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout, transition: .immediate)
}
})
self.muteButton.addTarget(self, action: #selector(self.muteButtonPressed), forControlEvents: .touchUpInside)
}
deinit {
self.callDisposable?.dispose()
self.memberCountDisposable?.dispose()
}
@objc private func muteButtonPressed() {
self.callContext?.toggleIsMuted()
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
self.validLayout = layout
self.memberCountNode.attributedText = NSAttributedString(string: "Members: \(self.memberCount)", font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
self.isMutedNode.attributedText = NSAttributedString(string: self.isMuted ? "Unmute" : "Mute", font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemAccentColor)
let textSize = self.memberCountNode.updateLayout(CGSize(width: layout.size.width - 16.0 * 2.0, height: 100.0))
let isMutedSize = self.isMutedNode.updateLayout(CGSize(width: layout.size.width - 16.0 * 2.0, height: 100.0))
let textFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - textSize.width) / 2.0), y: floor((layout.size.height - textSize.width) / 2.0)), size: textSize)
transition.updateFrameAdditiveToCenter(node: self.memberCountNode, frame: textFrame)
let isMutedFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - isMutedSize.width) / 2.0), y: textFrame.maxY + 12.0), size: isMutedSize)
transition.updateFrame(node: self.muteButton, frame: isMutedFrame)
self.isMutedNode.frame = CGRect(origin: CGPoint(), size: isMutedFrame.size)
}
}
private let context: AccountContext
private let presentationData: PresentationData
private var controllerNode: Node {
return self.displayNode as! Node
}
public init(context: AccountContext) {
self.context = context
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func loadDisplayNode() {
self.displayNode = Node(context: self.context)
self.displayNodeDidLoad()
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
self.controllerNode.containerLayoutUpdated(layout, transition: transition)
}
}

View File

@ -170,6 +170,10 @@ public final class PrincipalThemeEssentialGraphics {
public let outgoingDateAndStatusRepliesIcon: UIImage
public let mediaRepliesIcon: UIImage
public let freeRepliesIcon: UIImage
public let incomingDateAndStatusPinnedIcon: UIImage
public let outgoingDateAndStatusPinnedIcon: UIImage
public let mediaPinnedIcon: UIImage
public let freePinnedIcon: UIImage
public let dateStaticBackground: UIImage
public let dateFloatingBackground: UIImage
@ -333,6 +337,12 @@ public final class PrincipalThemeEssentialGraphics {
self.mediaRepliesIcon = generateTintedImage(image: repliesImage, color: .white)!
self.freeRepliesIcon = generateTintedImage(image: repliesImage, color: serviceColor.primaryText)!
let pinnedImage = UIImage(bundleImageName: "Chat/Message/Pinned")!
self.incomingDateAndStatusPinnedIcon = generateTintedImage(image: pinnedImage, color: theme.message.incoming.secondaryTextColor)!
self.outgoingDateAndStatusPinnedIcon = generateTintedImage(image: pinnedImage, color: theme.message.outgoing.secondaryTextColor)!
self.mediaPinnedIcon = generateTintedImage(image: pinnedImage, color: .white)!
self.freePinnedIcon = generateTintedImage(image: pinnedImage, color: serviceColor.primaryText)!
self.radialIndicatorFileIconIncoming = emptyImage
self.radialIndicatorFileIconOutgoing = emptyImage
} else {
@ -438,6 +448,12 @@ public final class PrincipalThemeEssentialGraphics {
self.mediaRepliesIcon = generateTintedImage(image: repliesImage, color: .white)!
self.freeRepliesIcon = generateTintedImage(image: repliesImage, color: serviceColor.primaryText)!
let pinnedImage = UIImage(bundleImageName: "Chat/Message/Pinned")!
self.incomingDateAndStatusPinnedIcon = generateTintedImage(image: pinnedImage, color: theme.message.incoming.secondaryTextColor)!
self.outgoingDateAndStatusPinnedIcon = generateTintedImage(image: pinnedImage, color: theme.message.outgoing.secondaryTextColor)!
self.mediaPinnedIcon = generateTintedImage(image: pinnedImage, color: .white)!
self.freePinnedIcon = generateTintedImage(image: pinnedImage, color: serviceColor.primaryText)!
self.radialIndicatorFileIconIncoming = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/RadialProgressIconDocument"), color: .black)!
self.radialIndicatorFileIconOutgoing = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/RadialProgressIconDocument"), color: .black)!
}

View File

@ -1,9 +1,9 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
},
"properties" : {
"provides-namespace" : true
}
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "messagepin.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -358,6 +358,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
public var purposefulAction: (() -> Void)?
private let scrolledToMessageId = ValuePromise<MessageId?>(nil, ignoreRepeated: true)
public init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?> = Atomic<ChatLocationContextHolder?>(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, mode: ChatControllerPresentationMode = .standard(previewing: false), peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil) {
let _ = ChatControllerCount.modify { value in
return value + 1
@ -3258,11 +3260,36 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
|> restartIfError
struct ReferenceMessage {
var id: MessageId
var isScrolled: Bool
}
let referenceMessage: Signal<ReferenceMessage?, NoError>
if latest {
referenceMessage = .single(nil)
} else {
referenceMessage = combineLatest(
queue: Queue.mainQueue(),
self.scrolledToMessageId.get(),
self.chatDisplayNode.historyNode.topVisibleMessageRange.get()
)
|> map { scrolledToMessageId, topVisibleMessageRange -> ReferenceMessage? in
if let scrolledToMessageId = scrolledToMessageId {
return ReferenceMessage(id: scrolledToMessageId, isScrolled: true)
} else if let topVisibleMessageRange = topVisibleMessageRange {
return ReferenceMessage(id: topVisibleMessageRange.upperBound, isScrolled: false)
} else {
return nil
}
}
}
topPinnedMessage = combineLatest(
replyHistory,
latest ? .single(nil) : self.chatDisplayNode.historyNode.topVisibleMessageRange.get()
referenceMessage
)
|> map { update, topVisibleMessageRange -> ChatPinnedMessage? in
|> map { update, referenceMessage -> ChatPinnedMessage? in
var message: ChatPinnedMessage?
switch update {
case .Loading:
@ -3278,9 +3305,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var matches = false
if message == nil {
matches = true
} else if let topVisibleMessageRange = topVisibleMessageRange {
if entry.message.id <= topVisibleMessageRange.upperBound {
matches = true
} else if let referenceMessage = referenceMessage {
if referenceMessage.isScrolled {
if entry.message.id < referenceMessage.id {
matches = true
}
} else {
if entry.message.id <= referenceMessage.id {
matches = true
}
}
} else {
matches = true
@ -3318,6 +3351,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
self.chatDisplayNode.historyNode.beganDragging = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.scrolledToMessageId.set(nil)
}
self.chatDisplayNode.peerView = self.peerView
let initialData = self.chatDisplayNode.historyNode.initialData
@ -3645,6 +3686,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let highlightedState = ChatInterfaceHighlightedState(messageStableId: message.stableId)
controllerInteraction.highlightedState = highlightedState
strongSelf.updateItemNodesHighlightedStates(animated: false)
strongSelf.scrolledToMessageId.set(index.id)
strongSelf.messageContextDisposable.set((Signal<Void, NoError>.complete() |> delay(0.7, queue: Queue.mainQueue())).start(completed: {
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
@ -4874,7 +4916,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
}
}, unpinMessage: { [weak self] id in
}, unpinMessage: { [weak self] id, askForConfirmation in
if let strongSelf = self {
if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
var canManagePin = false
@ -4896,7 +4938,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
if canManagePin {
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_UnpinMessageAlert, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Conversation_Unpin, action: {
let action: () -> Void = {
guard let strongSelf = self else {
return
}
if let strongSelf = self {
let disposable: MetaDisposable
if let current = strongSelf.unpinMessageDisposable {
@ -4907,7 +4952,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
disposable.set(requestUpdatePinnedMessage(account: strongSelf.context.account, peerId: peer.id, update: .clear(id: id)).start())
}
}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {})], actionLayout: .vertical), in: .window(.root))
}
if askForConfirmation {
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Conversation_UnpinMessageAlert, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Conversation_Unpin, action: {
action()
}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {})], actionLayout: .vertical), in: .window(.root))
} else {
action()
}
} else {
if let pinnedMessage = strongSelf.presentationInterfaceState.pinnedMessage {
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {

View File

@ -526,6 +526,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
var maxVisibleMessageIndexUpdated: ((MessageIndex) -> Void)?
var scrolledToIndex: ((MessageHistoryAnchorIndex) -> Void)?
var beganDragging: (() -> Void)?
private let hasVisiblePlayableItemNodesPromise = ValuePromise<Bool>(false, ignoreRepeated: true)
var hasVisiblePlayableItemNodes: Signal<Bool, NoError> {
@ -1121,6 +1122,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
self.beganInteractiveDragging = { [weak self] in
self?.isInteractivelyScrollingValue = true
self?.isInteractivelyScrollingPromise.set(true)
self?.beganDragging?()
}
self.didEndScrolling = { [weak self] in

View File

@ -664,7 +664,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_Unpin, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unpin"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in
interfaceInteraction.unpinMessage(pinnedSelectedMessageId)
interfaceInteraction.unpinMessage(pinnedSelectedMessageId, false)
f(.default)
})))
} else {

View File

@ -702,7 +702,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .minimal, reactionCount: dateReactionCount)
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies)
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned))
var viaBotApply: (TextNodeLayout, () -> TextNode)?
var replyInfoApply: (CGSize, () -> ChatMessageReplyInfoNode)?

View File

@ -572,7 +572,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
}
}
statusSizeAndApply = statusLayout(context, presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies)
statusSizeAndApply = statusLayout(context, presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies, message.tags.contains(.pinned))
}
default:
break

View File

@ -609,9 +609,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
if let strongSelf = self {
for contentNode in strongSelf.contentNodes {
var translatedPoint: CGPoint?
let convertedNodeFrame = contentNode.convert(contentNode.bounds, to: strongSelf)
let convertedNodeFrame = contentNode.view.convert(contentNode.bounds, to: strongSelf.view)
if let point = point, convertedNodeFrame.insetBy(dx: -4.0, dy: -4.0).contains(point) {
translatedPoint = strongSelf.convert(point, to: contentNode)
translatedPoint = strongSelf.view.convert(point, to: contentNode.view)
}
contentNode.updateTouchesAtPoint(translatedPoint)
}
@ -905,7 +905,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
forwardInfoLayout: (ChatPresentationData, PresentationStrings, ChatMessageForwardInfoType, Peer?, String?, String?, CGSize) -> (CGSize, (CGFloat) -> ChatMessageForwardInfoNode),
replyInfoLayout: (ChatPresentationData, PresentationStrings, AccountContext, ChatMessageReplyInfoType, Message, CGSize) -> (CGSize, () -> ChatMessageReplyInfoNode),
actionButtonsLayout: (AccountContext, ChatPresentationThemeData, PresentationChatBubbleCorners, PresentationStrings, ReplyMarkupMessageAttribute, Message, CGFloat) -> (minWidth: CGFloat, layout: (CGFloat) -> (CGSize, (Bool) -> ChatMessageActionButtonsNode)),
mosaicStatusLayout: (AccountContext, ChatPresentationData, Bool, Int?, String, ChatMessageDateAndStatusType, CGSize, [MessageReaction], Int) -> (CGSize, (Bool) -> ChatMessageDateAndStatusNode),
mosaicStatusLayout: (AccountContext, ChatPresentationData, Bool, Int?, String, ChatMessageDateAndStatusType, CGSize, [MessageReaction], Int, Bool) -> (CGSize, (Bool) -> ChatMessageDateAndStatusNode),
currentShareButtonNode: HighlightableButtonNode?,
layoutConstants: ChatMessageItemLayoutConstants,
currentItem: ChatMessageItem?,
@ -1442,7 +1442,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
}
}
mosaicStatusSizeAndApply = mosaicStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: 200.0, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies)
mosaicStatusSizeAndApply = mosaicStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: 200.0, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, message.tags.contains(.pinned))
}
}
@ -2873,7 +2873,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
}
}
loop: for contentNode in self.contentNodes {
let convertedLocation = self.convert(location, to: contentNode)
let convertedLocation = self.view.convert(location, to: contentNode.view)
let tapAction = contentNode.tapActionAtPoint(convertedLocation, gesture: gesture, isEstimating: false)
switch tapAction {
@ -2982,9 +2982,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
var tapMessage: Message? = item.content.firstMessage
var selectAll = true
loop: for contentNode in self.contentNodes {
let convertedLocation = self.convert(location, to: contentNode)
let convertedLocation = self.view.convert(location, to: contentNode.view)
let convertedNodeFrame = contentNode.convert(contentNode.bounds, to: self)
let convertedNodeFrame = contentNode.view.convert(contentNode.bounds, to: self.view)
if !convertedNodeFrame.contains(location) {
continue loop
} else if contentNode is ChatMessageMediaBubbleContentNode {

View File

@ -196,7 +196,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
var statusApply: ((Bool) -> Void)?
if let statusType = statusType {
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: constrainedSize.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies)
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: constrainedSize.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned))
statusSize = size
statusApply = apply
}

View File

@ -180,7 +180,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
self.addSubnode(self.dateNode)
}
func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ edited: Bool, _ impressionCount: Int?, _ dateText: String, _ type: ChatMessageDateAndStatusType, _ constrainedSize: CGSize, _ reactions: [MessageReaction], _ replies: Int) -> (CGSize, (Bool) -> Void) {
func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ edited: Bool, _ impressionCount: Int?, _ dateText: String, _ type: ChatMessageDateAndStatusType, _ constrainedSize: CGSize, _ reactions: [MessageReaction], _ replies: Int, _ isPinned: Bool) -> (CGSize, (Bool) -> Void) {
let dateLayout = TextNode.asyncLayout(self.dateNode)
var checkReadNode = self.checkReadNode
@ -200,7 +200,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
let previousLayoutSize = self.layoutSize
return { context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions, replyCount in
return { context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions, replyCount, isPinned in
let dateColor: UIColor
var backgroundImage: UIImage?
var outgoingStatus: ChatMessageDateAndStatusOutgoingType?
@ -234,6 +234,8 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
}
if replyCount != 0 {
repliesImage = graphics.incomingDateAndStatusRepliesIcon
} else if isPinned {
repliesImage = graphics.incomingDateAndStatusPinnedIcon
}
case let .BubbleOutgoing(status):
dateColor = presentationData.theme.theme.chat.message.outgoing.secondaryTextColor
@ -248,6 +250,8 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
}
if replyCount != 0 {
repliesImage = graphics.outgoingDateAndStatusRepliesIcon
} else if isPinned {
repliesImage = graphics.outgoingDateAndStatusPinnedIcon
}
case .ImageIncoming:
dateColor = presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor
@ -262,6 +266,8 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
}
if replyCount != 0 {
repliesImage = graphics.mediaRepliesIcon
} else if isPinned {
repliesImage = graphics.mediaPinnedIcon
}
case let .ImageOutgoing(status):
dateColor = presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor
@ -277,6 +283,8 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
}
if replyCount != 0 {
repliesImage = graphics.mediaRepliesIcon
} else if isPinned {
repliesImage = graphics.mediaPinnedIcon
}
case .FreeIncoming:
let serviceColor = serviceMessageColorComponents(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
@ -292,6 +300,8 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
}
if replyCount != 0 {
repliesImage = graphics.freeRepliesIcon
} else if isPinned {
repliesImage = graphics.freePinnedIcon
}
case let .FreeOutgoing(status):
let serviceColor = serviceMessageColorComponents(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
@ -308,6 +318,8 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
}
if replyCount != 0 {
repliesImage = graphics.freeRepliesIcon
} else if isPinned {
repliesImage = graphics.freePinnedIcon
}
}
@ -511,6 +523,8 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
let layoutAndApply = makeReplyCountLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: countString, font: dateFont, textColor: dateColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0)))
reactionInset += 14.0 + layoutAndApply.0.size.width + 4.0
replyCountLayoutAndApply = layoutAndApply
} else if isPinned {
reactionInset += 12.0
}
leftInset += reactionInset
@ -817,17 +831,17 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
}
}
static func asyncLayout(_ node: ChatMessageDateAndStatusNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ edited: Bool, _ impressionCount: Int?, _ dateText: String, _ type: ChatMessageDateAndStatusType, _ constrainedSize: CGSize, _ reactions: [MessageReaction], _ replies: Int) -> (CGSize, (Bool) -> ChatMessageDateAndStatusNode) {
static func asyncLayout(_ node: ChatMessageDateAndStatusNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ edited: Bool, _ impressionCount: Int?, _ dateText: String, _ type: ChatMessageDateAndStatusType, _ constrainedSize: CGSize, _ reactions: [MessageReaction], _ replies: Int, _ isPinned: Bool) -> (CGSize, (Bool) -> ChatMessageDateAndStatusNode) {
let currentLayout = node?.asyncLayout()
return { context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions, replies in
return { context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions, replies, isPinned in
let resultNode: ChatMessageDateAndStatusNode
let resultSizeAndApply: (CGSize, (Bool) -> Void)
if let node = node, let currentLayout = currentLayout {
resultNode = node
resultSizeAndApply = currentLayout(context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions, replies)
resultSizeAndApply = currentLayout(context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions, replies, isPinned)
} else {
resultNode = ChatMessageDateAndStatusNode()
resultSizeAndApply = resultNode.asyncLayout()(context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions, replies)
resultSizeAndApply = resultNode.asyncLayout()(context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions, replies, isPinned)
}
return (resultSizeAndApply.0, { animated in

View File

@ -327,7 +327,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
let dateText = stringForMessageTimestampStatus(accountPeerId: context.account.peerId, message: message, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, strings: presentationData.strings, reactionCount: dateReactionCount)
let (size, apply) = statusLayout(context, presentationData, edited, viewCount, dateText, statusType, constrainedSize, dateReactions, dateReplies)
let (size, apply) = statusLayout(context, presentationData, edited, viewCount, dateText, statusType, constrainedSize, dateReactions, dateReplies, message.tags.contains(.pinned))
statusSize = size
statusApply = apply
}

View File

@ -285,7 +285,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
} else {
maxDateAndStatusWidth = width - videoFrame.midX - 85.0
}
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited && !sentViaBot, viewCount, dateText, statusType, CGSize(width: max(1.0, maxDateAndStatusWidth), height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies)
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited && !sentViaBot, viewCount, dateText, statusType, CGSize(width: max(1.0, maxDateAndStatusWidth), height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned))
var contentSize = imageSize
var dateAndStatusOverflow = false

View File

@ -246,7 +246,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
var statusApply: ((Bool) -> Void)?
if let statusType = statusType {
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: constrainedSize.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies)
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: constrainedSize.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned))
statusSize = size
statusApply = apply
}

View File

@ -231,7 +231,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
var statusApply: ((Bool) -> Void)?
if let statusType = statusType {
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: imageSize.width - 30.0, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies)
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: imageSize.width - 30.0, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned))
statusSize = size
statusApply = apply
}

View File

@ -1074,7 +1074,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
var statusApply: ((Bool) -> Void)?
if let statusType = statusType {
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies)
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies, item.message.tags.contains(.pinned))
statusSize = size
statusApply = apply
}

View File

@ -105,7 +105,7 @@ class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNode {
var statusApply: ((Bool) -> Void)?
if let statusType = statusType {
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies)
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies, item.message.tags.contains(.pinned))
statusSize = size
statusApply = apply
}

View File

@ -382,7 +382,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular, reactionCount: dateReactionCount)
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies)
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned))
var viaBotApply: (TextNodeLayout, () -> TextNode)?
var replyInfoApply: (CGSize, () -> ChatMessageReplyInfoNode)?

View File

@ -169,7 +169,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
var statusApply: ((Bool) -> Void)?
if let statusType = statusType {
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies)
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies, item.message.tags.contains(.pinned))
statusSize = size
statusApply = apply
}

View File

@ -95,7 +95,7 @@ final class ChatPanelInterfaceInteraction {
let sendSticker: (FileMediaReference, ASDisplayNode, CGRect) -> Bool
let unblockPeer: () -> Void
let pinMessage: (MessageId) -> Void
let unpinMessage: (MessageId) -> Void
let unpinMessage: (MessageId, Bool) -> Void
let shareAccountContact: () -> Void
let reportPeer: () -> Void
let presentPeerContact: () -> Void
@ -171,7 +171,7 @@ final class ChatPanelInterfaceInteraction {
sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool,
unblockPeer: @escaping () -> Void,
pinMessage: @escaping (MessageId) -> Void,
unpinMessage: @escaping (MessageId) -> Void,
unpinMessage: @escaping (MessageId, Bool) -> Void,
shareAccountContact: @escaping () -> Void,
reportPeer: @escaping () -> Void,
presentPeerContact: @escaping () -> Void,

View File

@ -362,7 +362,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
@objc func closePressed() {
if let interfaceInteraction = self.interfaceInteraction, let message = self.currentMessage {
interfaceInteraction.unpinMessage(message.message.id)
interfaceInteraction.unpinMessage(message.message.id, true)
}
}
}

View File

@ -100,7 +100,7 @@ final class ChatRecentActionsController: TelegramBaseController {
return false
}, unblockPeer: {
}, pinMessage: { _ in
}, unpinMessage: { _ in
}, unpinMessage: { _, _ in
}, shareAccountContact: {
}, reportPeer: {
}, presentPeerContact: {

View File

@ -130,7 +130,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
} else if params.standalone {
location = .recentActions(params.message)
} else {
location = .messages(peerId: params.message.id.peerId, tagMask: .voiceOrInstantVideo, at: params.message.id)
location = .messages(chatLocation: params.chatLocation ?? .peer(params.message.id.peerId), tagMask: .voiceOrInstantVideo, at: params.message.id)
}
playerType = .voice
} else if file.isMusic && params.message.tags.contains(.music) {
@ -139,7 +139,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
} else if params.standalone {
location = .recentActions(params.message)
} else {
location = .messages(peerId: params.message.id.peerId, tagMask: .music, at: params.message.id)
location = .messages(chatLocation: params.chatLocation ?? .peer(params.message.id.peerId), tagMask: .music, at: params.message.id)
}
playerType = .music
} else {
@ -150,7 +150,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
}
playerType = (file.isVoice || file.isInstantVideo) ? .voice : .music
}
params.context.sharedContext.mediaManager.setPlaylist((params.context.account, PeerMessagesMediaPlaylist(postbox: params.context.account.postbox, network: params.context.account.network, location: location)), type: playerType, control: control)
params.context.sharedContext.mediaManager.setPlaylist((params.context.account, PeerMessagesMediaPlaylist(context: params.context, location: location, chatLocationContextHolder: params.chatLocationContextHolder)), type: playerType, control: control)
return true
case let .gallery(gallery):
params.dismissInput()

View File

@ -52,6 +52,7 @@ import PeerInfoUI
import ListMessageItem
import GalleryData
import ChatInterfaceState
import TelegramVoip
protocol PeerInfoScreenItem: class {
var id: AnyHashable { get }
@ -405,7 +406,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
return false
}, unblockPeer: {
}, pinMessage: { _ in
}, unpinMessage: { _ in
}, unpinMessage: { _, _ in
}, shareAccountContact: {
}, reportPeer: {
}, presentPeerContact: {
@ -3153,6 +3154,9 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self.controller?.present(shareController, in: .window(.root))
}
private let groupCallDisposable = MetaDisposable()
private var groupCall: GroupCallContext?
private func requestCall(isVideo: Bool) {
guard let peer = self.data?.peer as? TelegramUser, let cachedUserData = self.data?.cachedData as? CachedUserData else {
return

View File

@ -307,9 +307,9 @@ private struct PlaybackStack {
}
final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
private let postbox: Postbox
private let network: Network
private let context: AccountContext
private let messagesLocation: PeerMessagesPlaylistLocation
private let chatLocationContextHolder: Atomic<ChatLocationContextHolder?>?
var location: SharedMediaPlaylistLocation {
return self.messagesLocation
@ -338,13 +338,13 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
return self.stateValue.get()
}
init(postbox: Postbox, network: Network, location: PeerMessagesPlaylistLocation) {
init(context: AccountContext, location: PeerMessagesPlaylistLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>?) {
assert(Queue.mainQueue().isCurrent())
self.id = location.playlistId
self.postbox = postbox
self.network = network
self.context = context
self.chatLocationContextHolder = chatLocationContextHolder
self.messagesLocation = location
switch self.messagesLocation {
@ -446,7 +446,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
self.currentlyObservedMessageId = item?.message.id
if let id = item?.message.id {
let key: PostboxViewKey = .messages(Set([id]))
self.currentlyObservedMessageDisposable.set((self.postbox.combinedView(keys: [key])
self.currentlyObservedMessageDisposable.set((self.context.account.postbox.combinedView(keys: [key])
|> filter { views in
if let view = views.views[key] as? MessagesView {
if !view.messages.isEmpty {
@ -479,15 +479,15 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
switch anchor {
case let .messageId(messageId):
switch self.messagesLocation {
case let .messages(peerId, tagMask, _):
let historySignal = self.postbox.messageAtId(messageId)
case let .messages(chatLocation, tagMask, _):
let historySignal = self.context.account.postbox.messageAtId(messageId)
|> take(1)
|> mapToSignal { message -> Signal<(Message, [Message])?, NoError> in
guard let message = message else {
return .single(nil)
}
return self.postbox.aroundMessageHistoryViewForLocation(.peer(peerId), anchor: .index(message.index), count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, namespaces: namespaces, orderStatistics: [])
return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic<ChatLocationContextHolder?>(value: nil)), anchor: .index(message.index), count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, namespaces: namespaces, orderStatistics: [])
|> mapToSignal { view -> Signal<(Message, [Message])?, NoError> in
if let (message, aroundMessages, _) = navigatedMessageFromView(view.0, anchorIndex: message.index, position: .exact) {
return .single((message, aroundMessages))
@ -539,7 +539,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
}
}))
default:
self.navigationDisposable.set((self.postbox.messageAtId(messageId)
self.navigationDisposable.set((self.context.account.postbox.messageAtId(messageId)
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] message in
if let strongSelf = self {
@ -559,7 +559,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
}
case let .index(index):
switch self.messagesLocation {
case let .messages(peerId, tagMask, _):
case let .messages(chatLocation, tagMask, _):
let inputIndex: Signal<MessageIndex, NoError>
let looping = self.looping
switch self.order {
@ -567,7 +567,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
inputIndex = .single(index)
case .random:
var playbackStack = self.playbackStack
inputIndex = self.postbox.transaction { transaction -> MessageIndex in
inputIndex = self.context.account.postbox.transaction { transaction -> MessageIndex in
if case let .random(previous) = navigation, previous {
let _ = playbackStack.pop()
while true {
@ -580,12 +580,12 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
}
}
}
return transaction.findRandomMessage(peerId: peerId, namespace: Namespaces.Message.Cloud, tag: tagMask, ignoreIds: (playbackStack.ids, playbackStack.set)) ?? index
return transaction.findRandomMessage(peerId: chatLocation.peerId, namespace: Namespaces.Message.Cloud, tag: tagMask, ignoreIds: (playbackStack.ids, playbackStack.set)) ?? index
}
}
let historySignal = inputIndex
|> mapToSignal { inputIndex -> Signal<(Message, [Message])?, NoError> in
return self.postbox.aroundMessageHistoryViewForLocation(.peer(peerId), anchor: .index(inputIndex), count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, namespaces: namespaces, orderStatistics: [])
return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic<ChatLocationContextHolder?>(value: nil)), anchor: .index(inputIndex), count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, namespaces: namespaces, orderStatistics: [])
|> mapToSignal { view -> Signal<(Message, [Message])?, NoError> in
let position: NavigatedMessageFromViewPosition
switch navigation {
@ -615,7 +615,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
} else {
viewIndex = .lowerBound
}
return self.postbox.aroundMessageHistoryViewForLocation(.peer(peerId), anchor: viewIndex, count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, namespaces: namespaces, orderStatistics: [])
return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic<ChatLocationContextHolder?>(value: nil)), anchor: viewIndex, count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tagMask: tagMask, namespaces: namespaces, orderStatistics: [])
|> mapToSignal { view -> Signal<(Message, [Message])?, NoError> in
let position: NavigatedMessageFromViewPosition
switch navigation {
@ -657,7 +657,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
}
}))
case .singleMessage:
self.navigationDisposable.set((self.postbox.messageAtId(index.id)
self.navigationDisposable.set((self.context.account.postbox.messageAtId(index.id)
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] message in
if let strongSelf = self {
@ -815,7 +815,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist {
default:
break
}
let _ = markMessageContentAsConsumedInteractively(postbox: self.postbox, messageId: item.message.id).start()
let _ = markMessageContentAsConsumedInteractively(postbox: self.context.account.postbox, messageId: item.message.id).start()
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
#ifndef GroupCallThreadLocalContext_h
#define GroupCallThreadLocalContext_h
#import <Foundation/Foundation.h>
#import <TgVoipWebrtc/OngoingCallThreadLocalContext.h>
@interface GroupCallThreadLocalContext : NSObject
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue relaySdpAnswer:(void (^ _Nonnull)(NSString * _Nonnull))relaySdpAnswer;
- (void)emitOffer;
- (void)setOfferSdp:(NSString * _Nonnull)offerSdp isPartial:(bool)isPartial;
- (void)setIsMuted:(bool)isMuted;
@end
#endif

View File

@ -0,0 +1,57 @@
#import <TgVoipWebrtc/GroupCallThreadLocalContext.h>
#import "group/GroupInstanceImpl.h"
@interface GroupCallThreadLocalContext () {
id<OngoingCallThreadLocalContextQueueWebrtc> _queue;
std::unique_ptr<tgcalls::GroupInstanceImpl> _instance;
}
@end
@implementation GroupCallThreadLocalContext
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue relaySdpAnswer:(void (^ _Nonnull)(NSString * _Nonnull))relaySdpAnswer {
self = [super init];
if (self != nil) {
_queue = queue;
tgcalls::GroupInstanceDescriptor descriptor;
__weak GroupCallThreadLocalContext *weakSelf = self;
descriptor.sdpAnswerEmitted = [weakSelf, queue, relaySdpAnswer](std::string const &sdpAnswer) {
NSString *string = [NSString stringWithUTF8String:sdpAnswer.c_str()];
[queue dispatch:^{
__strong GroupCallThreadLocalContext *strongSelf = weakSelf;
if (strongSelf == nil) {
return;
}
relaySdpAnswer(string);
}];
};
_instance.reset(new tgcalls::GroupInstanceImpl(std::move(descriptor)));
}
return self;
}
- (void)emitOffer {
if (_instance) {
_instance->emitOffer();
}
}
- (void)setOfferSdp:(NSString * _Nonnull)offerSdp isPartial:(bool)isPartial {
if (_instance) {
_instance->setOfferSdp([offerSdp UTF8String], isPartial);
}
}
- (void)setIsMuted:(bool)isMuted {
if (_instance) {
_instance->setIsMuted(isMuted);
}
}
@end

View File

@ -1,7 +1,7 @@
#ifndef WEBRTC_IOS
#import "OngoingCallThreadLocalContext.h"
#else
#import <TgVoip/OngoingCallThreadLocalContext.h>
#import <TgVoipWebrtc/OngoingCallThreadLocalContext.h>
#endif

@ -1 +1 @@
Subproject commit 64f96a1b4fcfb8afdb0fb7749082cb42cdad7901
Subproject commit 4f7501d281b851e6302b2a2d7298c733eee82414

View File

@ -1,4 +1,4 @@
use_gn_build = True
use_gn_build = False
webrtc_libs = [
"libwebrtc.a",

@ -1 +1 @@
Subproject commit 11255bcfff3180210a012f368e2d2bcd169b6877
Subproject commit 782743c7931d09c1d2e4a0cf6cd349ee45452f1d