mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-16 02:20:07 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
ec4283159b
@ -8242,5 +8242,8 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"Undo.DeletedTopic" = "Topic Deleted";
|
"Undo.DeletedTopic" = "Topic Deleted";
|
||||||
|
|
||||||
|
"ChatList.MaxThreadPinsFinalText_1" = "Sorry, you can't pin more than **%@** thread to the top. Unpin some that are currently pinned.";
|
||||||
|
"ChatList.MaxThreadPinsFinalText_any" = "Sorry, you can't pin more than **%@** threads to the top. Unpin some that are currently pinned.";
|
||||||
|
|
||||||
"EmojiSearch.SearchTopicIconsPlaceholder" = "Search Topic Icons";
|
"EmojiSearch.SearchTopicIconsPlaceholder" = "Search Topic Icons";
|
||||||
"EmojiSearch.SearchTopicIconsEmptyResult" = "No emoji found";
|
"EmojiSearch.SearchTopicIconsEmptyResult" = "No emoji found";
|
||||||
|
@ -519,7 +519,19 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
|
|||||||
items.append(.action(ContextMenuActionItem(text: isPinned ? presentationData.strings.ChatList_Context_Unpin : presentationData.strings.ChatList_Context_Pin, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin": "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
items.append(.action(ContextMenuActionItem(text: isPinned ? presentationData.strings.ChatList_Context_Unpin : presentationData.strings.ChatList_Context_Pin, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin": "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||||
f(.default)
|
f(.default)
|
||||||
|
|
||||||
let _ = context.engine.peers.setForumChannelTopicPinned(id: peerId, threadId: threadId, isPinned: !isPinned).start()
|
let _ = (context.engine.peers.toggleForumChannelTopicPinned(id: peerId, threadId: threadId)
|
||||||
|
|> deliverOnMainQueue).start(error: { error in
|
||||||
|
switch error {
|
||||||
|
case let .limitReached(count):
|
||||||
|
if let chatListController = chatListController {
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let text = presentationData.strings.ChatList_MaxThreadPinsFinalText(Int32(count))
|
||||||
|
chatListController.present(textAlertController(context: context, title: presentationData.strings.Premium_LimitReached, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})], parseMarkdown: true), in: .window(.root))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3874,7 +3874,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func setPeerThreadPinned(peerId: EnginePeer.Id, threadId: Int64, isPinned: Bool) {
|
private func setPeerThreadPinned(peerId: EnginePeer.Id, threadId: Int64, isPinned: Bool) {
|
||||||
self.actionDisposables.add(self.context.engine.peers.setForumChannelTopicPinned(id: peerId, threadId: threadId, isPinned: isPinned).start())
|
self.actionDisposables.add(self.context.engine.peers.toggleForumChannelTopicPinned(id: peerId, threadId: threadId).start())
|
||||||
}
|
}
|
||||||
|
|
||||||
public func maybeAskForPeerChatRemoval(peer: EngineRenderedPeer, joined: Bool = false, deleteGloballyIfPossible: Bool = false, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) {
|
public func maybeAskForPeerChatRemoval(peer: EngineRenderedPeer, joined: Bool = false, deleteGloballyIfPossible: Bool = false, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) {
|
||||||
|
@ -795,7 +795,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
} else {
|
} else {
|
||||||
result += item.presentationData.strings.VoiceOver_ChatList_OutgoingMessage
|
result += item.presentationData.strings.VoiceOver_ChatList_OutgoingMessage
|
||||||
}
|
}
|
||||||
let (_, initialHideAuthor, messageText, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, messages: messages, chatPeer: peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
|
let (_, initialHideAuthor, messageText, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: messages, chatPeer: peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
|
||||||
if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, case .user = author {
|
if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, case .user = author {
|
||||||
result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageFrom(author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).string)"
|
result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageFrom(author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).string)"
|
||||||
}
|
}
|
||||||
@ -829,7 +829,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
} else {
|
} else {
|
||||||
result += item.presentationData.strings.VoiceOver_ChatList_OutgoingMessage
|
result += item.presentationData.strings.VoiceOver_ChatList_OutgoingMessage
|
||||||
}
|
}
|
||||||
let (_, initialHideAuthor, messageText, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, messages: messages, chatPeer: peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
|
let (_, initialHideAuthor, messageText, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: messages, chatPeer: peer, accountPeerId: item.context.account.peerId, isPeerGroup: false)
|
||||||
if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, case .user = author {
|
if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, case .user = author {
|
||||||
result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageFrom(author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).string)"
|
result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageFrom(author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).string)"
|
||||||
}
|
}
|
||||||
@ -1387,7 +1387,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
var hideAuthor = false
|
var hideAuthor = false
|
||||||
switch contentPeer {
|
switch contentPeer {
|
||||||
case let .chat(itemPeer):
|
case let .chat(itemPeer):
|
||||||
var (peer, initialHideAuthor, messageText, spoilers, customEmojiRanges) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, messages: messages, chatPeer: itemPeer, accountPeerId: item.context.account.peerId, enableMediaEmoji: !enableChatListPhotos, isPeerGroup: isPeerGroup)
|
var (peer, initialHideAuthor, messageText, spoilers, customEmojiRanges) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: messages, chatPeer: itemPeer, accountPeerId: item.context.account.peerId, enableMediaEmoji: !enableChatListPhotos, isPeerGroup: isPeerGroup)
|
||||||
|
|
||||||
if case let .psa(_, maybePsaText) = promoInfo, let psaText = maybePsaText {
|
if case let .psa(_, maybePsaText) = promoInfo, let psaText = maybePsaText {
|
||||||
initialHideAuthor = true
|
initialHideAuthor = true
|
||||||
|
@ -45,11 +45,18 @@ private func messageGroupType(messages: [EngineMessage]) -> MessageGroupType {
|
|||||||
return currentType
|
return currentType
|
||||||
}
|
}
|
||||||
|
|
||||||
public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, messages: [EngineMessage], chatPeer: EngineRenderedPeer, accountPeerId: EnginePeer.Id, enableMediaEmoji: Bool = true, isPeerGroup: Bool = false) -> (peer: EnginePeer?, hideAuthor: Bool, messageText: String, spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?) {
|
public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, contentSettings: ContentSettings, messages: [EngineMessage], chatPeer: EngineRenderedPeer, accountPeerId: EnginePeer.Id, enableMediaEmoji: Bool = true, isPeerGroup: Bool = false) -> (peer: EnginePeer?, hideAuthor: Bool, messageText: String, spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?) {
|
||||||
let peer: EnginePeer?
|
let peer: EnginePeer?
|
||||||
|
|
||||||
let message = messages.last
|
let message = messages.last
|
||||||
|
|
||||||
|
if let restrictionReason = message?._asMessage().restrictionReason(platform: "ios", contentSettings: contentSettings) {
|
||||||
|
return (nil, false, restrictionReason, nil, nil)
|
||||||
|
}
|
||||||
|
if let restrictionReason = chatPeer.chatMainPeer?.restrictionText(platform: "ios", contentSettings: contentSettings) {
|
||||||
|
return (nil, false, restrictionReason, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
var hideAuthor = false
|
var hideAuthor = false
|
||||||
var messageText: String
|
var messageText: String
|
||||||
var spoilers: [NSRange]?
|
var spoilers: [NSRange]?
|
||||||
|
@ -1697,85 +1697,160 @@ public final class ChatListNode: ListView {
|
|||||||
})
|
})
|
||||||
|
|
||||||
self.reorderItem = { [weak self] fromIndex, toIndex, transactionOpaqueState -> Signal<Bool, NoError> in
|
self.reorderItem = { [weak self] fromIndex, toIndex, transactionOpaqueState -> Signal<Bool, NoError> in
|
||||||
if let strongSelf = self, let filteredEntries = (transactionOpaqueState as? ChatListOpaqueTransactionState)?.chatListView.filteredEntries {
|
guard let strongSelf = self, let filteredEntries = (transactionOpaqueState as? ChatListOpaqueTransactionState)?.chatListView.filteredEntries else {
|
||||||
guard case let .chatList(groupId) = strongSelf.location else {
|
return .single(false)
|
||||||
return .single(false)
|
}
|
||||||
|
guard fromIndex >= 0 && fromIndex < filteredEntries.count && toIndex >= 0 && toIndex < filteredEntries.count else {
|
||||||
|
return .single(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch strongSelf.location {
|
||||||
|
case let .chatList(groupId):
|
||||||
|
let fromEntry = filteredEntries[filteredEntries.count - 1 - fromIndex]
|
||||||
|
let toEntry = filteredEntries[filteredEntries.count - 1 - toIndex]
|
||||||
|
|
||||||
|
var referenceId: EngineChatList.PinnedItem.Id?
|
||||||
|
var beforeAll = false
|
||||||
|
switch toEntry {
|
||||||
|
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, promoInfo, _, _, _):
|
||||||
|
if promoInfo != nil {
|
||||||
|
beforeAll = true
|
||||||
|
} else {
|
||||||
|
if case let .chatList(chatListIndex) = index {
|
||||||
|
referenceId = .peer(chatListIndex.messageIndex.id.peerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if fromIndex >= 0 && fromIndex < filteredEntries.count && toIndex >= 0 && toIndex < filteredEntries.count {
|
if case let .index(index) = fromEntry.sortIndex, case let .chatList(chatListIndex) = index, let _ = chatListIndex.pinningIndex {
|
||||||
let fromEntry = filteredEntries[filteredEntries.count - 1 - fromIndex]
|
let location: TogglePeerChatPinnedLocation
|
||||||
let toEntry = filteredEntries[filteredEntries.count - 1 - toIndex]
|
if let chatListFilter = chatListFilter {
|
||||||
|
location = .filter(chatListFilter.id)
|
||||||
|
} else {
|
||||||
|
location = .group(groupId._asGroup())
|
||||||
|
}
|
||||||
|
|
||||||
var referenceId: EngineChatList.PinnedItem.Id?
|
let engine = strongSelf.context.engine
|
||||||
var beforeAll = false
|
return engine.peers.getPinnedItemIds(location: location)
|
||||||
switch toEntry {
|
|> mapToSignal { itemIds -> Signal<Bool, NoError> in
|
||||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, promoInfo, _, _, _):
|
var itemIds = itemIds
|
||||||
if promoInfo != nil {
|
|
||||||
beforeAll = true
|
var itemId: EngineChatList.PinnedItem.Id?
|
||||||
} else {
|
switch fromEntry {
|
||||||
if case let .chatList(chatListIndex) = index {
|
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
referenceId = .peer(chatListIndex.messageIndex.id.peerId)
|
if case let .chatList(index) = index {
|
||||||
|
itemId = .peer(index.messageIndex.id.peerId)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
|
||||||
|
|
||||||
if case let .index(index) = fromEntry.sortIndex, case let .chatList(chatListIndex) = index, let _ = chatListIndex.pinningIndex {
|
|
||||||
let location: TogglePeerChatPinnedLocation
|
|
||||||
if let chatListFilter = chatListFilter {
|
|
||||||
location = .filter(chatListFilter.id)
|
|
||||||
} else {
|
|
||||||
location = .group(groupId._asGroup())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let engine = strongSelf.context.engine
|
if let itemId = itemId {
|
||||||
return engine.peers.getPinnedItemIds(location: location)
|
itemIds = itemIds.filter({ $0 != itemId })
|
||||||
|> mapToSignal { itemIds -> Signal<Bool, NoError> in
|
if let referenceId = referenceId {
|
||||||
var itemIds = itemIds
|
var inserted = false
|
||||||
|
for i in 0 ..< itemIds.count {
|
||||||
var itemId: EngineChatList.PinnedItem.Id?
|
if itemIds[i] == referenceId {
|
||||||
switch fromEntry {
|
if fromIndex < toIndex {
|
||||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
itemIds.insert(itemId, at: i + 1)
|
||||||
if case let .chatList(index) = index {
|
} else {
|
||||||
itemId = .peer(index.messageIndex.id.peerId)
|
itemIds.insert(itemId, at: i)
|
||||||
}
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if let itemId = itemId {
|
|
||||||
itemIds = itemIds.filter({ $0 != itemId })
|
|
||||||
if let referenceId = referenceId {
|
|
||||||
var inserted = false
|
|
||||||
for i in 0 ..< itemIds.count {
|
|
||||||
if itemIds[i] == referenceId {
|
|
||||||
if fromIndex < toIndex {
|
|
||||||
itemIds.insert(itemId, at: i + 1)
|
|
||||||
} else {
|
|
||||||
itemIds.insert(itemId, at: i)
|
|
||||||
}
|
|
||||||
inserted = true
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
inserted = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
if !inserted {
|
}
|
||||||
itemIds.append(itemId)
|
if !inserted {
|
||||||
}
|
|
||||||
} else if beforeAll {
|
|
||||||
itemIds.insert(itemId, at: 0)
|
|
||||||
} else {
|
|
||||||
itemIds.append(itemId)
|
itemIds.append(itemId)
|
||||||
}
|
}
|
||||||
return engine.peers.reorderPinnedItemIds(location: location, itemIds: itemIds)
|
} else if beforeAll {
|
||||||
|
itemIds.insert(itemId, at: 0)
|
||||||
} else {
|
} else {
|
||||||
return .single(false)
|
itemIds.append(itemId)
|
||||||
}
|
}
|
||||||
|
return engine.peers.reorderPinnedItemIds(location: location, itemIds: itemIds)
|
||||||
|
} else {
|
||||||
|
return .single(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return .single(false)
|
||||||
|
}
|
||||||
|
case let .forum(peerId):
|
||||||
|
let fromEntry = filteredEntries[filteredEntries.count - 1 - fromIndex]
|
||||||
|
let toEntry = filteredEntries[filteredEntries.count - 1 - toIndex]
|
||||||
|
|
||||||
|
var referenceId: Int64?
|
||||||
|
var beforeAll = false
|
||||||
|
switch toEntry {
|
||||||
|
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, promoInfo, _, _, _):
|
||||||
|
if promoInfo != nil {
|
||||||
|
beforeAll = true
|
||||||
|
} else {
|
||||||
|
if case let .forum(_, _, threadId, _, _) = index {
|
||||||
|
referenceId = threadId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if case let .index(index) = fromEntry.sortIndex, case let .forum(pinningIndex, _, _, _, _) = index, case .index = pinningIndex {
|
||||||
|
let engine = strongSelf.context.engine
|
||||||
|
return engine.peers.getForumChannelPinnedTopics(id: peerId)
|
||||||
|
|> mapToSignal { itemIds -> Signal<Bool, NoError> in
|
||||||
|
var itemIds = itemIds
|
||||||
|
|
||||||
|
var itemId: Int64?
|
||||||
|
switch fromEntry {
|
||||||
|
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
|
if case let .forum(_, _, threadId, _, _) = index {
|
||||||
|
itemId = threadId
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if let itemId = itemId {
|
||||||
|
itemIds = itemIds.filter({ $0 != itemId })
|
||||||
|
if let referenceId = referenceId {
|
||||||
|
var inserted = false
|
||||||
|
for i in 0 ..< itemIds.count {
|
||||||
|
if itemIds[i] == referenceId {
|
||||||
|
if fromIndex < toIndex {
|
||||||
|
itemIds.insert(itemId, at: i + 1)
|
||||||
|
} else {
|
||||||
|
itemIds.insert(itemId, at: i)
|
||||||
|
}
|
||||||
|
inserted = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !inserted {
|
||||||
|
itemIds.append(itemId)
|
||||||
|
}
|
||||||
|
} else if beforeAll {
|
||||||
|
itemIds.insert(itemId, at: 0)
|
||||||
|
} else {
|
||||||
|
itemIds.append(itemId)
|
||||||
|
}
|
||||||
|
return engine.peers.setForumChannelPinnedTopics(id: peerId, threadIds: itemIds)
|
||||||
|
|> map { _ -> Bool in
|
||||||
|
}
|
||||||
|
|> `catch` { _ -> Signal<Bool, NoError> in
|
||||||
|
return .single(false)
|
||||||
|
}
|
||||||
|
|> then(Signal<Bool, NoError>.single(true))
|
||||||
|
} else {
|
||||||
|
return .single(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .single(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return .single(false)
|
|
||||||
}
|
}
|
||||||
var startedScrollingAtUpperBound = false
|
var startedScrollingAtUpperBound = false
|
||||||
|
|
||||||
|
@ -4,10 +4,16 @@ import Postbox
|
|||||||
|
|
||||||
public extension Message {
|
public extension Message {
|
||||||
func isRestricted(platform: String, contentSettings: ContentSettings) -> Bool {
|
func isRestricted(platform: String, contentSettings: ContentSettings) -> Bool {
|
||||||
|
return self.restrictionReason(platform: platform, contentSettings: contentSettings) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func restrictionReason(platform: String, contentSettings: ContentSettings) -> String? {
|
||||||
if let attribute = self.restrictedContentAttribute {
|
if let attribute = self.restrictedContentAttribute {
|
||||||
return attribute.platformText(platform: platform, contentSettings: contentSettings) != nil
|
if let value = attribute.platformText(platform: platform, contentSettings: contentSettings) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,7 +268,7 @@ private final class ChatListViewSpaceState {
|
|||||||
}
|
}
|
||||||
postboxLog("allIndices not unique, repeated: \(debugRepeatedIndices)")
|
postboxLog("allIndices not unique, repeated: \(debugRepeatedIndices)")
|
||||||
|
|
||||||
assert(false)
|
//assert(false)
|
||||||
//preconditionFailure()
|
//preconditionFailure()
|
||||||
}
|
}
|
||||||
if Set(allEntityIds).count != allEntityIds.count {
|
if Set(allEntityIds).count != allEntityIds.count {
|
||||||
|
@ -778,7 +778,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[-761649164] = { return Api.Update.parse_updateChannelMessageForwards($0) }
|
dict[-761649164] = { return Api.Update.parse_updateChannelMessageForwards($0) }
|
||||||
dict[-232346616] = { return Api.Update.parse_updateChannelMessageViews($0) }
|
dict[-232346616] = { return Api.Update.parse_updateChannelMessageViews($0) }
|
||||||
dict[-1738720581] = { return Api.Update.parse_updateChannelParticipant($0) }
|
dict[-1738720581] = { return Api.Update.parse_updateChannelParticipant($0) }
|
||||||
dict[-158027602] = { return Api.Update.parse_updateChannelPinnedTopic($0) }
|
dict[422509539] = { return Api.Update.parse_updateChannelPinnedTopic($0) }
|
||||||
|
dict[-31881726] = { return Api.Update.parse_updateChannelPinnedTopics($0) }
|
||||||
dict[-366410403] = { return Api.Update.parse_updateChannelReadMessagesContents($0) }
|
dict[-366410403] = { return Api.Update.parse_updateChannelReadMessagesContents($0) }
|
||||||
dict[277713951] = { return Api.Update.parse_updateChannelTooLong($0) }
|
dict[277713951] = { return Api.Update.parse_updateChannelTooLong($0) }
|
||||||
dict[-1937192669] = { return Api.Update.parse_updateChannelUserTyping($0) }
|
dict[-1937192669] = { return Api.Update.parse_updateChannelUserTyping($0) }
|
||||||
|
@ -671,7 +671,8 @@ public extension Api {
|
|||||||
case updateChannelMessageForwards(channelId: Int64, id: Int32, forwards: Int32)
|
case updateChannelMessageForwards(channelId: Int64, id: Int32, forwards: Int32)
|
||||||
case updateChannelMessageViews(channelId: Int64, id: Int32, views: Int32)
|
case updateChannelMessageViews(channelId: Int64, id: Int32, views: Int32)
|
||||||
case updateChannelParticipant(flags: Int32, channelId: Int64, date: Int32, actorId: Int64, userId: Int64, prevParticipant: Api.ChannelParticipant?, newParticipant: Api.ChannelParticipant?, invite: Api.ExportedChatInvite?, qts: Int32)
|
case updateChannelParticipant(flags: Int32, channelId: Int64, date: Int32, actorId: Int64, userId: Int64, prevParticipant: Api.ChannelParticipant?, newParticipant: Api.ChannelParticipant?, invite: Api.ExportedChatInvite?, qts: Int32)
|
||||||
case updateChannelPinnedTopic(flags: Int32, channelId: Int64, topicId: Int32?)
|
case updateChannelPinnedTopic(flags: Int32, channelId: Int64, topicId: Int32)
|
||||||
|
case updateChannelPinnedTopics(flags: Int32, channelId: Int64, order: [Int32]?)
|
||||||
case updateChannelReadMessagesContents(flags: Int32, channelId: Int64, topMsgId: Int32?, messages: [Int32])
|
case updateChannelReadMessagesContents(flags: Int32, channelId: Int64, topMsgId: Int32?, messages: [Int32])
|
||||||
case updateChannelTooLong(flags: Int32, channelId: Int64, pts: Int32?)
|
case updateChannelTooLong(flags: Int32, channelId: Int64, pts: Int32?)
|
||||||
case updateChannelUserTyping(flags: Int32, channelId: Int64, topMsgId: Int32?, fromId: Api.Peer, action: Api.SendMessageAction)
|
case updateChannelUserTyping(flags: Int32, channelId: Int64, topMsgId: Int32?, fromId: Api.Peer, action: Api.SendMessageAction)
|
||||||
@ -927,11 +928,23 @@ public extension Api {
|
|||||||
break
|
break
|
||||||
case .updateChannelPinnedTopic(let flags, let channelId, let topicId):
|
case .updateChannelPinnedTopic(let flags, let channelId, let topicId):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(-158027602)
|
buffer.appendInt32(422509539)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt64(channelId, buffer: buffer, boxed: false)
|
serializeInt64(channelId, buffer: buffer, boxed: false)
|
||||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(topicId!, buffer: buffer, boxed: false)}
|
serializeInt32(topicId, buffer: buffer, boxed: false)
|
||||||
|
break
|
||||||
|
case .updateChannelPinnedTopics(let flags, let channelId, let order):
|
||||||
|
if boxed {
|
||||||
|
buffer.appendInt32(-31881726)
|
||||||
|
}
|
||||||
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
|
serializeInt64(channelId, buffer: buffer, boxed: false)
|
||||||
|
if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261)
|
||||||
|
buffer.appendInt32(Int32(order!.count))
|
||||||
|
for item in order! {
|
||||||
|
serializeInt32(item, buffer: buffer, boxed: false)
|
||||||
|
}}
|
||||||
break
|
break
|
||||||
case .updateChannelReadMessagesContents(let flags, let channelId, let topMsgId, let messages):
|
case .updateChannelReadMessagesContents(let flags, let channelId, let topMsgId, let messages):
|
||||||
if boxed {
|
if boxed {
|
||||||
@ -1741,6 +1754,8 @@ public extension Api {
|
|||||||
return ("updateChannelParticipant", [("flags", String(describing: flags)), ("channelId", String(describing: channelId)), ("date", String(describing: date)), ("actorId", String(describing: actorId)), ("userId", String(describing: userId)), ("prevParticipant", String(describing: prevParticipant)), ("newParticipant", String(describing: newParticipant)), ("invite", String(describing: invite)), ("qts", String(describing: qts))])
|
return ("updateChannelParticipant", [("flags", String(describing: flags)), ("channelId", String(describing: channelId)), ("date", String(describing: date)), ("actorId", String(describing: actorId)), ("userId", String(describing: userId)), ("prevParticipant", String(describing: prevParticipant)), ("newParticipant", String(describing: newParticipant)), ("invite", String(describing: invite)), ("qts", String(describing: qts))])
|
||||||
case .updateChannelPinnedTopic(let flags, let channelId, let topicId):
|
case .updateChannelPinnedTopic(let flags, let channelId, let topicId):
|
||||||
return ("updateChannelPinnedTopic", [("flags", String(describing: flags)), ("channelId", String(describing: channelId)), ("topicId", String(describing: topicId))])
|
return ("updateChannelPinnedTopic", [("flags", String(describing: flags)), ("channelId", String(describing: channelId)), ("topicId", String(describing: topicId))])
|
||||||
|
case .updateChannelPinnedTopics(let flags, let channelId, let order):
|
||||||
|
return ("updateChannelPinnedTopics", [("flags", String(describing: flags)), ("channelId", String(describing: channelId)), ("order", String(describing: order))])
|
||||||
case .updateChannelReadMessagesContents(let flags, let channelId, let topMsgId, let messages):
|
case .updateChannelReadMessagesContents(let flags, let channelId, let topMsgId, let messages):
|
||||||
return ("updateChannelReadMessagesContents", [("flags", String(describing: flags)), ("channelId", String(describing: channelId)), ("topMsgId", String(describing: topMsgId)), ("messages", String(describing: messages))])
|
return ("updateChannelReadMessagesContents", [("flags", String(describing: flags)), ("channelId", String(describing: channelId)), ("topMsgId", String(describing: topMsgId)), ("messages", String(describing: messages))])
|
||||||
case .updateChannelTooLong(let flags, let channelId, let pts):
|
case .updateChannelTooLong(let flags, let channelId, let pts):
|
||||||
@ -2307,12 +2322,31 @@ public extension Api {
|
|||||||
var _2: Int64?
|
var _2: Int64?
|
||||||
_2 = reader.readInt64()
|
_2 = reader.readInt64()
|
||||||
var _3: Int32?
|
var _3: Int32?
|
||||||
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
|
_3 = reader.readInt32()
|
||||||
|
let _c1 = _1 != nil
|
||||||
|
let _c2 = _2 != nil
|
||||||
|
let _c3 = _3 != nil
|
||||||
|
if _c1 && _c2 && _c3 {
|
||||||
|
return Api.Update.updateChannelPinnedTopic(flags: _1!, channelId: _2!, topicId: _3!)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static func parse_updateChannelPinnedTopics(_ reader: BufferReader) -> Update? {
|
||||||
|
var _1: Int32?
|
||||||
|
_1 = reader.readInt32()
|
||||||
|
var _2: Int64?
|
||||||
|
_2 = reader.readInt64()
|
||||||
|
var _3: [Int32]?
|
||||||
|
if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() {
|
||||||
|
_3 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||||
|
} }
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||||
if _c1 && _c2 && _c3 {
|
if _c1 && _c2 && _c3 {
|
||||||
return Api.Update.updateChannelPinnedTopic(flags: _1!, channelId: _2!, topicId: _3)
|
return Api.Update.updateChannelPinnedTopics(flags: _1!, channelId: _2!, order: _3)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -2443,6 +2443,27 @@ public extension Api.functions.channels {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public extension Api.functions.channels {
|
||||||
|
static func reorderPinnedForumTopics(flags: Int32, channel: Api.InputChannel, order: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||||
|
let buffer = Buffer()
|
||||||
|
buffer.appendInt32(693150095)
|
||||||
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
|
channel.serialize(buffer, true)
|
||||||
|
buffer.appendInt32(481674261)
|
||||||
|
buffer.appendInt32(Int32(order.count))
|
||||||
|
for item in order {
|
||||||
|
serializeInt32(item, buffer: buffer, boxed: false)
|
||||||
|
}
|
||||||
|
return (FunctionDescription(name: "channels.reorderPinnedForumTopics", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||||
|
let reader = BufferReader(buffer)
|
||||||
|
var result: Api.Updates?
|
||||||
|
if let signature = reader.readInt32() {
|
||||||
|
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
public extension Api.functions.channels {
|
public extension Api.functions.channels {
|
||||||
static func reorderUsernames(channel: Api.InputChannel, order: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
static func reorderUsernames(channel: Api.InputChannel, order: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
|
@ -179,6 +179,8 @@ public class ManagedAudioSessionControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class ManagedAudioSession {
|
public final class ManagedAudioSession {
|
||||||
|
public private(set) static var shared: ManagedAudioSession?
|
||||||
|
|
||||||
private var nextId: Int32 = 0
|
private var nextId: Int32 = 0
|
||||||
private let queue: Queue
|
private let queue: Queue
|
||||||
private let hasLoudspeaker: Bool
|
private let hasLoudspeaker: Bool
|
||||||
@ -256,6 +258,8 @@ public final class ManagedAudioSession {
|
|||||||
self.isHeadsetPluggedInValue = self.isHeadsetPluggedIn()
|
self.isHeadsetPluggedInValue = self.isHeadsetPluggedIn()
|
||||||
self.updateCurrentAudioRouteInfo()
|
self.updateCurrentAudioRouteInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ManagedAudioSession.shared = self
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -784,6 +788,61 @@ public final class ManagedAudioSession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func applyVoiceChatOutputModeInCurrentAudioSession(outputMode: AudioSessionOutputMode) {
|
||||||
|
managedAudioSessionLog("applyVoiceChatOutputModeInCurrentAudioSession \(outputMode)")
|
||||||
|
|
||||||
|
do {
|
||||||
|
var resetToBuiltin = false
|
||||||
|
switch outputMode {
|
||||||
|
case .system:
|
||||||
|
resetToBuiltin = true
|
||||||
|
case let .custom(output):
|
||||||
|
switch output {
|
||||||
|
case .builtin:
|
||||||
|
resetToBuiltin = true
|
||||||
|
case .speaker:
|
||||||
|
if let routes = AVAudioSession.sharedInstance().availableInputs {
|
||||||
|
for route in routes {
|
||||||
|
if route.portType == .builtInMic {
|
||||||
|
let _ = try? AVAudioSession.sharedInstance().setPreferredInput(route)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker)
|
||||||
|
case .headphones:
|
||||||
|
break
|
||||||
|
case let .port(port):
|
||||||
|
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.none)
|
||||||
|
if let routes = AVAudioSession.sharedInstance().availableInputs {
|
||||||
|
for route in routes {
|
||||||
|
if route.uid == port.uid {
|
||||||
|
let _ = try? AVAudioSession.sharedInstance().setPreferredInput(route)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .speakerIfNoHeadphones:
|
||||||
|
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.none)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resetToBuiltin {
|
||||||
|
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.none)
|
||||||
|
if let routes = AVAudioSession.sharedInstance().availableInputs {
|
||||||
|
for route in routes {
|
||||||
|
if route.portType == .builtInMic {
|
||||||
|
let _ = try? AVAudioSession.sharedInstance().setPreferredInput(route)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch let e {
|
||||||
|
managedAudioSessionLog("applyVoiceChatOutputModeInCurrentAudioSession error: \(e)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func setupOutputMode(_ outputMode: AudioSessionOutputMode, type: ManagedAudioSessionType) throws {
|
private func setupOutputMode(_ outputMode: AudioSessionOutputMode, type: ManagedAudioSessionType) throws {
|
||||||
managedAudioSessionLog("ManagedAudioSession setup \(outputMode) for \(type)")
|
managedAudioSessionLog("ManagedAudioSession setup \(outputMode) for \(type)")
|
||||||
var resetToBuiltin = false
|
var resetToBuiltin = false
|
||||||
|
@ -8,6 +8,7 @@ import TelegramCore
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import AppBundle
|
import AppBundle
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
import TelegramAudio
|
||||||
|
|
||||||
private let sharedProviderDelegate: AnyObject? = {
|
private let sharedProviderDelegate: AnyObject? = {
|
||||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||||
@ -107,6 +108,10 @@ public final class CallKitIntegration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func applyVoiceChatOutputMode(outputMode: AudioSessionOutputMode) {
|
||||||
|
(sharedProviderDelegate as? CallKitProviderDelegate)?.applyVoiceChatOutputMode(outputMode: outputMode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOSApplicationExtension 10.0, iOS 10.0, *)
|
@available(iOSApplicationExtension 10.0, iOS 10.0, *)
|
||||||
@ -125,6 +130,9 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
|||||||
private var setCallMuted: ((UUID, Bool) -> Void)?
|
private var setCallMuted: ((UUID, Bool) -> Void)?
|
||||||
private var audioSessionActivationChanged: ((Bool) -> Void)?
|
private var audioSessionActivationChanged: ((Bool) -> Void)?
|
||||||
|
|
||||||
|
private var isAudioSessionActive: Bool = false
|
||||||
|
private var pendingVoiceChatOutputMode: AudioSessionOutputMode?
|
||||||
|
|
||||||
private let disposableSet = DisposableSet()
|
private let disposableSet = DisposableSet()
|
||||||
|
|
||||||
fileprivate var audioSessionActivePromise: ValuePromise<Bool>?
|
fileprivate var audioSessionActivePromise: ValuePromise<Bool>?
|
||||||
@ -163,7 +171,7 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
|||||||
private func requestTransaction(_ transaction: CXTransaction, completion: ((Bool) -> Void)? = nil) {
|
private func requestTransaction(_ transaction: CXTransaction, completion: ((Bool) -> Void)? = nil) {
|
||||||
self.callController.request(transaction) { error in
|
self.callController.request(transaction) { error in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
print("Error requesting transaction: \(error)")
|
print("Error requesting transaction \(transaction): \(error)")
|
||||||
}
|
}
|
||||||
completion?(error == nil)
|
completion?(error == nil)
|
||||||
}
|
}
|
||||||
@ -238,6 +246,12 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
|||||||
update.supportsDTMF = false
|
update.supportsDTMF = false
|
||||||
update.hasVideo = isVideo
|
update.hasVideo = isVideo
|
||||||
|
|
||||||
|
do {
|
||||||
|
try AVAudioSession.sharedInstance().setMode(.default)
|
||||||
|
} catch let e {
|
||||||
|
print("AVAudioSession.sharedInstance().setMode(.default) error \(e)")
|
||||||
|
}
|
||||||
|
|
||||||
self.provider.reportNewIncomingCall(with: uuid, update: update, completion: { error in
|
self.provider.reportNewIncomingCall(with: uuid, update: update, completion: { error in
|
||||||
completion?(error as NSError?)
|
completion?(error as NSError?)
|
||||||
})
|
})
|
||||||
@ -321,13 +335,28 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
|
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
|
||||||
|
print("provider didActivate default? \(audioSession === AVAudioSession.sharedInstance())")
|
||||||
|
self.isAudioSessionActive = true
|
||||||
self.audioSessionActivationChanged?(true)
|
self.audioSessionActivationChanged?(true)
|
||||||
self.audioSessionActivePromise?.set(true)
|
self.audioSessionActivePromise?.set(true)
|
||||||
|
|
||||||
|
if let outputMode = self.pendingVoiceChatOutputMode {
|
||||||
|
self.pendingVoiceChatOutputMode = nil
|
||||||
|
ManagedAudioSession.shared?.applyVoiceChatOutputModeInCurrentAudioSession(outputMode: outputMode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
|
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
|
||||||
|
self.isAudioSessionActive = false
|
||||||
self.audioSessionActivationChanged?(false)
|
self.audioSessionActivationChanged?(false)
|
||||||
self.audioSessionActivePromise?.set(false)
|
self.audioSessionActivePromise?.set(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applyVoiceChatOutputMode(outputMode: AudioSessionOutputMode) {
|
||||||
|
if self.isAudioSessionActive {
|
||||||
|
ManagedAudioSession.shared?.applyVoiceChatOutputModeInCurrentAudioSession(outputMode: outputMode)
|
||||||
|
} else {
|
||||||
|
self.pendingVoiceChatOutputMode = outputMode
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,6 +413,9 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.audioOutputStateValue = (availableOutputs, currentOutput)
|
strongSelf.audioOutputStateValue = (availableOutputs, currentOutput)
|
||||||
|
if let currentOutput = currentOutput {
|
||||||
|
strongSelf.currentAudioOutputValue = currentOutput
|
||||||
|
}
|
||||||
|
|
||||||
var signal: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> = .single((availableOutputs, currentOutput))
|
var signal: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> = .single((availableOutputs, currentOutput))
|
||||||
if !didReceiveAudioOutputs {
|
if !didReceiveAudioOutputs {
|
||||||
@ -437,7 +440,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
let audioSessionActive: Signal<Bool, NoError>
|
let audioSessionActive: Signal<Bool, NoError>
|
||||||
if let callKitIntegration = strongSelf.callKitIntegration {
|
if let callKitIntegration = strongSelf.callKitIntegration {
|
||||||
audioSessionActive = callKitIntegration.audioSessionActive
|
audioSessionActive = callKitIntegration.audioSessionActive
|
||||||
|> filter { $0 }
|
/*|> filter { $0 }
|
||||||
|> timeout(2.0, queue: Queue.mainQueue(), alternate: Signal { subscriber in
|
|> timeout(2.0, queue: Queue.mainQueue(), alternate: Signal { subscriber in
|
||||||
if let strongSelf = self, let _ = strongSelf.audioSessionControl {
|
if let strongSelf = self, let _ = strongSelf.audioSessionControl {
|
||||||
//audioSessionControl.activate({ _ in })
|
//audioSessionControl.activate({ _ in })
|
||||||
@ -445,7 +448,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
subscriber.putNext(true)
|
subscriber.putNext(true)
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
return EmptyDisposable
|
return EmptyDisposable
|
||||||
})
|
})*/
|
||||||
} else {
|
} else {
|
||||||
audioSessionControl.activate({ _ in })
|
audioSessionControl.activate({ _ in })
|
||||||
audioSessionActive = .single(true)
|
audioSessionActive = .single(true)
|
||||||
@ -534,8 +537,12 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let audioSessionControl = audioSessionControl, previous == nil || previousControl == nil {
|
if let audioSessionControl = audioSessionControl, previous == nil || previousControl == nil {
|
||||||
audioSessionControl.setOutputMode(.custom(self.currentAudioOutputValue))
|
if let callKitIntegration = self.callKitIntegration {
|
||||||
audioSessionControl.setup(synchronous: true)
|
callKitIntegration.applyVoiceChatOutputMode(outputMode: .custom(self.currentAudioOutputValue))
|
||||||
|
} else {
|
||||||
|
audioSessionControl.setOutputMode(.custom(self.currentAudioOutputValue))
|
||||||
|
audioSessionControl.setup(synchronous: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mappedVideoState: PresentationCallState.VideoState
|
let mappedVideoState: PresentationCallState.VideoState
|
||||||
@ -1031,7 +1038,11 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
))
|
))
|
||||||
|
|
||||||
if let audioSessionControl = self.audioSessionControl {
|
if let audioSessionControl = self.audioSessionControl {
|
||||||
audioSessionControl.setOutputMode(.custom(output))
|
if let callKitIntegration = self.callKitIntegration {
|
||||||
|
callKitIntegration.applyVoiceChatOutputMode(outputMode: .custom(self.currentAudioOutputValue))
|
||||||
|
} else {
|
||||||
|
audioSessionControl.setOutputMode(.custom(output))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1648,7 +1648,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
outgoingAudioBitrateKbit = Int32(value)
|
outgoingAudioBitrateKbit = Int32(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
genericCallContext = .call(OngoingGroupCallContext(video: self.videoCapturer, requestMediaChannelDescriptions: { [weak self] ssrcs, completion in
|
genericCallContext = .call(OngoingGroupCallContext(audioSessionActive: self.audioSessionActive.get(), video: self.videoCapturer, requestMediaChannelDescriptions: { [weak self] ssrcs, completion in
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -2966,7 +2966,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
|
|
||||||
self.hasScreencast = true
|
self.hasScreencast = true
|
||||||
|
|
||||||
let screencastCallContext = OngoingGroupCallContext(video: self.screencastCapturer, requestMediaChannelDescriptions: { _, _ in EmptyDisposable }, rejoinNeeded: { }, outgoingAudioBitrateKbit: nil, videoContentType: .screencast, enableNoiseSuppression: false, disableAudioInput: true, preferX264: false, logPath: "")
|
let screencastCallContext = OngoingGroupCallContext(audioSessionActive: .single(true), video: self.screencastCapturer, requestMediaChannelDescriptions: { _, _ in EmptyDisposable }, rejoinNeeded: { }, outgoingAudioBitrateKbit: nil, videoContentType: .screencast, enableNoiseSuppression: false, disableAudioInput: true, preferX264: false, logPath: "")
|
||||||
self.screencastCallContext = screencastCallContext
|
self.screencastCallContext = screencastCallContext
|
||||||
|
|
||||||
self.screencastJoinDisposable.set((screencastCallContext.joinPayload
|
self.screencastJoinDisposable.set((screencastCallContext.joinPayload
|
||||||
|
@ -93,7 +93,8 @@ enum AccountStateMutationOperation {
|
|||||||
case ReadSecretOutbox(peerId: PeerId, maxTimestamp: Int32, actionTimestamp: Int32)
|
case ReadSecretOutbox(peerId: PeerId, maxTimestamp: Int32, actionTimestamp: Int32)
|
||||||
case AddPeerInputActivity(chatPeerId: PeerActivitySpace, peerId: PeerId?, activity: PeerInputActivity?)
|
case AddPeerInputActivity(chatPeerId: PeerActivitySpace, peerId: PeerId?, activity: PeerInputActivity?)
|
||||||
case UpdatePinnedItemIds(PeerGroupId, AccountStateUpdatePinnedItemIdsOperation)
|
case UpdatePinnedItemIds(PeerGroupId, AccountStateUpdatePinnedItemIdsOperation)
|
||||||
case UpdatePinnedTopic(peerId: PeerId, threadId: Int64?)
|
case UpdatePinnedTopic(peerId: PeerId, threadId: Int64, isPinned: Bool)
|
||||||
|
case UpdatePinnedTopicOrder(peerId: PeerId, threadIds: [Int64])
|
||||||
case ReadMessageContents((PeerId?, [Int32]))
|
case ReadMessageContents((PeerId?, [Int32]))
|
||||||
case UpdateMessageImpressionCount(MessageId, Int32)
|
case UpdateMessageImpressionCount(MessageId, Int32)
|
||||||
case UpdateMessageForwardsCount(MessageId, Int32)
|
case UpdateMessageForwardsCount(MessageId, Int32)
|
||||||
@ -475,8 +476,12 @@ struct AccountMutableState {
|
|||||||
self.addOperation(.UpdatePinnedItemIds(groupId, operation))
|
self.addOperation(.UpdatePinnedItemIds(groupId, operation))
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func addUpdatePinnedTopic(peerId: PeerId, threadId: Int64?) {
|
mutating func addUpdatePinnedTopic(peerId: PeerId, threadId: Int64, isPinned: Bool) {
|
||||||
self.addOperation(.UpdatePinnedTopic(peerId: peerId, threadId: threadId))
|
self.addOperation(.UpdatePinnedTopic(peerId: peerId, threadId: threadId, isPinned: isPinned))
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func addUpdatePinnedTopicOrder(peerId: PeerId, threadIds: [Int64]) {
|
||||||
|
self.addOperation(.UpdatePinnedTopicOrder(peerId: peerId, threadIds: threadIds))
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func addReadMessagesContents(_ peerIdsAndMessageIds: (PeerId?, [Int32])) {
|
mutating func addReadMessagesContents(_ peerIdsAndMessageIds: (PeerId?, [Int32])) {
|
||||||
@ -545,7 +550,7 @@ struct AccountMutableState {
|
|||||||
|
|
||||||
mutating func addOperation(_ operation: AccountStateMutationOperation) {
|
mutating func addOperation(_ operation: AccountStateMutationOperation) {
|
||||||
switch operation {
|
switch operation {
|
||||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedTopic, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic:
|
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic:
|
||||||
break
|
break
|
||||||
case let .AddMessages(messages, location):
|
case let .AddMessages(messages, location):
|
||||||
for message in messages {
|
for message in messages {
|
||||||
|
@ -367,21 +367,16 @@ func _internal_setForumChannelTopicClosed(account: Account, id: EnginePeer.Id, t
|
|||||||
|
|
||||||
public enum SetForumChannelTopicPinnedError {
|
public enum SetForumChannelTopicPinnedError {
|
||||||
case generic
|
case generic
|
||||||
|
case limitReached(Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _internal_setForumChannelTopicPinned(account: Account, id: EnginePeer.Id, threadId: Int64, isPinned: Bool) -> Signal<Never, SetForumChannelTopicPinnedError> {
|
func _internal_setForumChannelPinnedTopics(account: Account, id: EnginePeer.Id, threadIds: [Int64]) -> Signal<Never, SetForumChannelTopicPinnedError> {
|
||||||
return account.postbox.transaction { transaction -> Api.InputChannel? in
|
return account.postbox.transaction { transaction -> Api.InputChannel? in
|
||||||
guard let inputChannel = transaction.getPeer(id).flatMap(apiInputChannel) else {
|
guard let inputChannel = transaction.getPeer(id).flatMap(apiInputChannel) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if isPinned {
|
transaction.setPeerPinnedThreads(peerId: id, threadIds: threadIds)
|
||||||
transaction.setPeerPinnedThreads(peerId: id, threadIds: [threadId])
|
|
||||||
} else {
|
|
||||||
if transaction.getPeerPinnedThreads(peerId: id).contains(threadId) {
|
|
||||||
transaction.setPeerPinnedThreads(peerId: id, threadIds: [])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return inputChannel
|
return inputChannel
|
||||||
}
|
}
|
||||||
@ -390,13 +385,11 @@ func _internal_setForumChannelTopicPinned(account: Account, id: EnginePeer.Id, t
|
|||||||
guard let inputChannel = inputChannel else {
|
guard let inputChannel = inputChannel else {
|
||||||
return .fail(.generic)
|
return .fail(.generic)
|
||||||
}
|
}
|
||||||
var flags: Int32 = 0
|
|
||||||
flags |= (1 << 2)
|
|
||||||
|
|
||||||
return account.network.request(Api.functions.channels.updatePinnedForumTopic(
|
return account.network.request(Api.functions.channels.reorderPinnedForumTopics(
|
||||||
|
flags: 1 << 0,
|
||||||
channel: inputChannel,
|
channel: inputChannel,
|
||||||
topicId: Int32(clamping: threadId),
|
order: threadIds.map(Int32.init(clamping:))
|
||||||
pinned: isPinned ? .boolTrue : .boolFalse
|
|
||||||
))
|
))
|
||||||
|> mapError { _ -> SetForumChannelTopicPinnedError in
|
|> mapError { _ -> SetForumChannelTopicPinnedError in
|
||||||
return .generic
|
return .generic
|
||||||
|
@ -114,6 +114,11 @@ private func peerIdsRequiringLocalChatStateFromUpdates(_ updates: [Api.Update])
|
|||||||
case let .updateChannelTooLong(_, channelId, _):
|
case let .updateChannelTooLong(_, channelId, _):
|
||||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||||
peerIds.insert(peerId)
|
peerIds.insert(peerId)
|
||||||
|
case let .updateChannelPinnedTopics(_, channelId, order):
|
||||||
|
if order == nil {
|
||||||
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||||
|
peerIds.insert(peerId)
|
||||||
|
}
|
||||||
case let .updateFolderPeers(folderPeers, _, _):
|
case let .updateFolderPeers(folderPeers, _, _):
|
||||||
for peer in folderPeers {
|
for peer in folderPeers {
|
||||||
switch peer {
|
switch peer {
|
||||||
@ -337,11 +342,16 @@ private func peerIdsRequiringLocalChatStateFromDifference(_ difference: Api.upda
|
|||||||
peerIds.insert(messageId.peerId)
|
peerIds.insert(messageId.peerId)
|
||||||
}
|
}
|
||||||
switch update {
|
switch update {
|
||||||
case let .updateChannelTooLong(_, channelId, _):
|
case let .updateChannelTooLong(_, channelId, _):
|
||||||
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||||
|
peerIds.insert(peerId)
|
||||||
|
case let .updateChannelPinnedTopics(_, channelId, order):
|
||||||
|
if order == nil {
|
||||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||||
peerIds.insert(peerId)
|
peerIds.insert(peerId)
|
||||||
default:
|
}
|
||||||
break
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case .differenceEmpty:
|
case .differenceEmpty:
|
||||||
@ -359,11 +369,16 @@ private func peerIdsRequiringLocalChatStateFromDifference(_ difference: Api.upda
|
|||||||
peerIds.insert(messageId.peerId)
|
peerIds.insert(messageId.peerId)
|
||||||
}
|
}
|
||||||
switch update {
|
switch update {
|
||||||
case let .updateChannelTooLong(_, channelId, _):
|
case let .updateChannelTooLong(_, channelId, _):
|
||||||
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||||
|
peerIds.insert(peerId)
|
||||||
|
case let .updateChannelPinnedTopics(_, channelId, order):
|
||||||
|
if order == nil {
|
||||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||||
peerIds.insert(peerId)
|
peerIds.insert(peerId)
|
||||||
default:
|
}
|
||||||
break
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case .differenceTooLong:
|
case .differenceTooLong:
|
||||||
@ -725,6 +740,20 @@ private func sortedUpdates(_ updates: [Api.Update]) -> [Api.Update] {
|
|||||||
} else {
|
} else {
|
||||||
updatesByChannel[peerId]!.append(update)
|
updatesByChannel[peerId]!.append(update)
|
||||||
}
|
}
|
||||||
|
case let .updateChannelPinnedTopic(_, channelId, _):
|
||||||
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||||
|
if updatesByChannel[peerId] == nil {
|
||||||
|
updatesByChannel[peerId] = [update]
|
||||||
|
} else {
|
||||||
|
updatesByChannel[peerId]!.append(update)
|
||||||
|
}
|
||||||
|
case let .updateChannelPinnedTopics(_, channelId, _):
|
||||||
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||||
|
if updatesByChannel[peerId] == nil {
|
||||||
|
updatesByChannel[peerId] = [update]
|
||||||
|
} else {
|
||||||
|
updatesByChannel[peerId]!.append(update)
|
||||||
|
}
|
||||||
case let .updateDeleteChannelMessages(channelId, _, _, _):
|
case let .updateDeleteChannelMessages(channelId, _, _, _):
|
||||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||||
if updatesByChannel[peerId] == nil {
|
if updatesByChannel[peerId] == nil {
|
||||||
@ -860,6 +889,15 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
|
|||||||
channelsToPoll.insert(peerId)
|
channelsToPoll.insert(peerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case let .updateChannelPinnedTopics(_, channelId, order):
|
||||||
|
if let order = order {
|
||||||
|
updatedState.addUpdatePinnedTopicOrder(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), threadIds: order.map(Int64.init))
|
||||||
|
} else {
|
||||||
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||||
|
if !channelsToPoll.contains(peerId) {
|
||||||
|
channelsToPoll.insert(peerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
case let .updateDeleteChannelMessages(channelId, messages, pts: pts, ptsCount):
|
case let .updateDeleteChannelMessages(channelId, messages, pts: pts, ptsCount):
|
||||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||||
if let previousState = updatedState.channelStates[peerId] {
|
if let previousState = updatedState.channelStates[peerId] {
|
||||||
@ -1375,8 +1413,9 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
|
|||||||
} else {
|
} else {
|
||||||
updatedState.addUpdatePinnedItemIds(groupId: groupId, operation: .sync)
|
updatedState.addUpdatePinnedItemIds(groupId: groupId, operation: .sync)
|
||||||
}
|
}
|
||||||
case let .updateChannelPinnedTopic(_, channelId, topicId):
|
case let .updateChannelPinnedTopic(flags, channelId, topicId):
|
||||||
updatedState.addUpdatePinnedTopic(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), threadId: topicId.flatMap(Int64.init))
|
let isPinned = (flags & (1 << 0)) != 0
|
||||||
|
updatedState.addUpdatePinnedTopic(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), threadId: Int64(topicId), isPinned: isPinned)
|
||||||
case let .updateReadMessagesContents(messages, _, _):
|
case let .updateReadMessagesContents(messages, _, _):
|
||||||
updatedState.addReadMessagesContents((nil, messages))
|
updatedState.addReadMessagesContents((nil, messages))
|
||||||
case let .updateChannelReadMessagesContents(_, channelId, topMsgId, messages):
|
case let .updateChannelReadMessagesContents(_, channelId, topMsgId, messages):
|
||||||
@ -2847,7 +2886,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
|
|||||||
var currentAddScheduledMessages: OptimizeAddMessagesState?
|
var currentAddScheduledMessages: OptimizeAddMessagesState?
|
||||||
for operation in operations {
|
for operation in operations {
|
||||||
switch operation {
|
switch operation {
|
||||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedTopic, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic:
|
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic:
|
||||||
if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
|
if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
|
||||||
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
|
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
|
||||||
}
|
}
|
||||||
@ -3757,12 +3796,18 @@ func replayFinalState(
|
|||||||
case .sync:
|
case .sync:
|
||||||
addSynchronizePinnedChatsOperation(transaction: transaction, groupId: groupId)
|
addSynchronizePinnedChatsOperation(transaction: transaction, groupId: groupId)
|
||||||
}
|
}
|
||||||
case let .UpdatePinnedTopic(peerId, threadId):
|
case let .UpdatePinnedTopic(peerId, threadId, isPinned):
|
||||||
if let threadId = threadId {
|
var currentThreadIds = transaction.getPeerPinnedThreads(peerId: peerId)
|
||||||
transaction.setPeerPinnedThreads(peerId: peerId, threadIds: [threadId])
|
if isPinned {
|
||||||
|
if !currentThreadIds.contains(threadId) {
|
||||||
|
currentThreadIds.insert(threadId, at: 0)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
transaction.setPeerPinnedThreads(peerId: peerId, threadIds: [])
|
currentThreadIds.removeAll(where: { $0 == threadId })
|
||||||
}
|
}
|
||||||
|
transaction.setPeerPinnedThreads(peerId: peerId, threadIds: currentThreadIds)
|
||||||
|
case let .UpdatePinnedTopicOrder(peerId, threadIds):
|
||||||
|
transaction.setPeerPinnedThreads(peerId: peerId, threadIds: threadIds)
|
||||||
case let .ReadMessageContents(peerIdAndMessageIds):
|
case let .ReadMessageContents(peerIdAndMessageIds):
|
||||||
let (peerId, messageIds) = peerIdAndMessageIds
|
let (peerId, messageIds) = peerIdAndMessageIds
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
|||||||
|
|
||||||
public class Serialization: NSObject, MTSerialization {
|
public class Serialization: NSObject, MTSerialization {
|
||||||
public func currentLayer() -> UInt {
|
public func currentLayer() -> UInt {
|
||||||
return 148
|
return 149
|
||||||
}
|
}
|
||||||
|
|
||||||
public func parseMessage(_ data: Data!) -> Any! {
|
public func parseMessage(_ data: Data!) -> Any! {
|
||||||
|
@ -858,8 +858,34 @@ public extension TelegramEngine {
|
|||||||
|> ignoreValues
|
|> ignoreValues
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setForumChannelTopicPinned(id: EnginePeer.Id, threadId: Int64, isPinned: Bool) -> Signal<Never, SetForumChannelTopicPinnedError> {
|
public func toggleForumChannelTopicPinned(id: EnginePeer.Id, threadId: Int64) -> Signal<Never, SetForumChannelTopicPinnedError> {
|
||||||
return _internal_setForumChannelTopicPinned(account: self.account, id: id, threadId: threadId, isPinned: isPinned)
|
return self.account.postbox.transaction { transaction -> [Int64] in
|
||||||
|
return transaction.getPeerPinnedThreads(peerId: id)
|
||||||
|
}
|
||||||
|
|> castError(SetForumChannelTopicPinnedError.self)
|
||||||
|
|> mapToSignal { threadIds -> Signal<Never, SetForumChannelTopicPinnedError> in
|
||||||
|
var threadIds = threadIds
|
||||||
|
if threadIds.contains(threadId) {
|
||||||
|
threadIds.removeAll(where: { $0 == threadId })
|
||||||
|
} else {
|
||||||
|
if threadIds.count + 1 > 5 {
|
||||||
|
return .fail(.limitReached(5))
|
||||||
|
}
|
||||||
|
threadIds.insert(threadId, at: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return _internal_setForumChannelPinnedTopics(account: self.account, id: id, threadIds: threadIds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getForumChannelPinnedTopics(id: EnginePeer.Id) -> Signal<[Int64], NoError> {
|
||||||
|
return self.account.postbox.transaction { transcation -> [Int64] in
|
||||||
|
return transcation.getPeerPinnedThreads(peerId: id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func setForumChannelPinnedTopics(id: EnginePeer.Id, threadIds: [Int64]) -> Signal<Never, SetForumChannelTopicPinnedError> {
|
||||||
|
return _internal_setForumChannelPinnedTopics(account: self.account, id: id, threadIds: threadIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func forumChannelTopicNotificationExceptions(id: EnginePeer.Id) -> Signal<[EngineMessageHistoryThread.NotificationException], NoError> {
|
public func forumChannelTopicNotificationExceptions(id: EnginePeer.Id) -> Signal<[EngineMessageHistoryThread.NotificationException], NoError> {
|
||||||
|
@ -30,7 +30,7 @@ public extension Peer {
|
|||||||
|
|
||||||
if let restrictionInfo = restrictionInfo {
|
if let restrictionInfo = restrictionInfo {
|
||||||
for rule in restrictionInfo.rules {
|
for rule in restrictionInfo.rules {
|
||||||
if rule.platform == "all" || rule.platform == platform {
|
if rule.platform == "all" || rule.platform == platform || contentSettings.addContentRestrictionReasons.contains(rule.platform) {
|
||||||
if !contentSettings.ignoreContentRestrictionReasons.contains(rule.reason) {
|
if !contentSettings.ignoreContentRestrictionReasons.contains(rule.reason) {
|
||||||
return rule.text
|
return rule.text
|
||||||
}
|
}
|
||||||
|
@ -6132,12 +6132,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var buttonKeyboardMessage = combinedInitialData.buttonKeyboardMessage
|
||||||
|
if let buttonKeyboardMessageValue = buttonKeyboardMessage, buttonKeyboardMessageValue.isRestricted(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with({ $0 })) {
|
||||||
|
buttonKeyboardMessage = nil
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: false, { updated in
|
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: false, { updated in
|
||||||
var updated = updated
|
var updated = updated
|
||||||
|
|
||||||
updated = updated.updatedInterfaceState({ _ in return interfaceState })
|
updated = updated.updatedInterfaceState({ _ in return interfaceState })
|
||||||
|
|
||||||
updated = updated.updatedKeyboardButtonsMessage(combinedInitialData.buttonKeyboardMessage)
|
updated = updated.updatedKeyboardButtonsMessage(buttonKeyboardMessage)
|
||||||
updated = updated.updatedPinnedMessageId(pinnedMessageId)
|
updated = updated.updatedPinnedMessageId(pinnedMessageId)
|
||||||
updated = updated.updatedPinnedMessage(pinnedMessage)
|
updated = updated.updatedPinnedMessage(pinnedMessage)
|
||||||
updated = updated.updatedPeerIsBlocked(peerIsBlocked)
|
updated = updated.updatedPeerIsBlocked(peerIsBlocked)
|
||||||
|
@ -1396,7 +1396,12 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
forceUpdateAll = true
|
forceUpdateAll = true
|
||||||
}
|
}
|
||||||
|
|
||||||
let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: reverse, chatLocation: chatLocation, controllerInteraction: controllerInteraction, scrollPosition: updatedScrollPosition, scrollAnimationCurve: scrollAnimationCurve, initialData: initialData?.initialData, keyboardButtonsMessage: view.topTaggedMessages.first, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: flashIndicators, updatedMessageSelection: previousSelectedMessages != selectedMessages, messageTransitionNode: messageTransitionNode(), allUpdated: updateAllOnEachVersion || forceUpdateAll)
|
var keyboardButtonsMessage = view.topTaggedMessages.first
|
||||||
|
if let keyboardButtonsMessageValue = keyboardButtonsMessage, keyboardButtonsMessageValue.isRestricted(platform: "ios", contentSettings: context.currentContentSettings.with({ $0 })) {
|
||||||
|
keyboardButtonsMessage = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: reverse, chatLocation: chatLocation, controllerInteraction: controllerInteraction, scrollPosition: updatedScrollPosition, scrollAnimationCurve: scrollAnimationCurve, initialData: initialData?.initialData, keyboardButtonsMessage: keyboardButtonsMessage, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: flashIndicators, updatedMessageSelection: previousSelectedMessages != selectedMessages, messageTransitionNode: messageTransitionNode(), allUpdated: updateAllOnEachVersion || forceUpdateAll)
|
||||||
var mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, transition: rawTransition)
|
var mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, transition: rawTransition)
|
||||||
|
|
||||||
if disableAnimations {
|
if disableAnimations {
|
||||||
|
@ -181,17 +181,6 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if chatPresentationInterfaceState.interfaceState.replyMessageId == nil {
|
|
||||||
if let currentPanel = (currentPanel as? ChatRestrictedInputPanelNode) ?? (currentSecondaryPanel as? ChatRestrictedInputPanelNode) {
|
|
||||||
return (currentPanel, nil)
|
|
||||||
} else {
|
|
||||||
let panel = ChatRestrictedInputPanelNode()
|
|
||||||
panel.context = context
|
|
||||||
panel.interfaceInteraction = interfaceInteraction
|
|
||||||
return (panel, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,6 +237,22 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if channel.flags.contains(.isForum) {
|
||||||
|
if let _ = chatPresentationInterfaceState.threadData {
|
||||||
|
} else {
|
||||||
|
if chatPresentationInterfaceState.interfaceState.replyMessageId == nil {
|
||||||
|
if let currentPanel = (currentPanel as? ChatRestrictedInputPanelNode) ?? (currentSecondaryPanel as? ChatRestrictedInputPanelNode) {
|
||||||
|
return (currentPanel, nil)
|
||||||
|
} else {
|
||||||
|
let panel = ChatRestrictedInputPanelNode()
|
||||||
|
panel.context = context
|
||||||
|
panel.interfaceInteraction = interfaceInteraction
|
||||||
|
return (panel, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if let group = peer as? TelegramGroup {
|
} else if let group = peer as? TelegramGroup {
|
||||||
switch group.membership {
|
switch group.membership {
|
||||||
case .Removed, .Left:
|
case .Removed, .Left:
|
||||||
|
@ -1167,7 +1167,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
|
|
||||||
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
|
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
|
||||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId {
|
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId {
|
||||||
} else if let threadId = item.message.threadId, Int64(replyAttribute.messageId.id) == threadId, let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum) {
|
|
||||||
} else {
|
} else {
|
||||||
replyInfoApply = makeReplyInfoLayout(ChatMessageReplyInfoNode.Arguments(
|
replyInfoApply = makeReplyInfoLayout(ChatMessageReplyInfoNode.Arguments(
|
||||||
presentationData: item.presentationData,
|
presentationData: item.presentationData,
|
||||||
|
@ -1418,7 +1418,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
}
|
}
|
||||||
} else if let attribute = attribute as? ReplyMessageAttribute {
|
} else if let attribute = attribute as? ReplyMessageAttribute {
|
||||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == attribute.messageId {
|
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == attribute.messageId {
|
||||||
} else if let threadId = firstMessage.threadId, Int64(attribute.messageId.id) == threadId, let channel = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum) {
|
|
||||||
} else {
|
} else {
|
||||||
replyMessage = firstMessage.associatedMessages[attribute.messageId]
|
replyMessage = firstMessage.associatedMessages[attribute.messageId]
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,7 @@ final class ChatMessageAccessibilityData {
|
|||||||
if let chatPeer = message.peers[item.message.id.peerId] {
|
if let chatPeer = message.peers[item.message.id.peerId] {
|
||||||
let authorName = message.author.flatMap(EnginePeer.init)?.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
let authorName = message.author.flatMap(EnginePeer.init)?.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
||||||
|
|
||||||
let (_, _, messageText, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, messages: [EngineMessage(message)], chatPeer: EngineRenderedPeer(peer: EnginePeer(chatPeer)), accountPeerId: item.context.account.peerId)
|
let (_, _, messageText, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: [EngineMessage(message)], chatPeer: EngineRenderedPeer(peer: EnginePeer(chatPeer)), accountPeerId: item.context.account.peerId)
|
||||||
|
|
||||||
var text = messageText
|
var text = messageText
|
||||||
|
|
||||||
|
@ -97,8 +97,6 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
let previousMediaReference = maybeNode?.previousMediaReference
|
let previousMediaReference = maybeNode?.previousMediaReference
|
||||||
|
|
||||||
return { arguments in
|
return { arguments in
|
||||||
//presentationData, strings, context, type, message, parentMessage, constrainedSize
|
|
||||||
|
|
||||||
let fontSize = floor(arguments.presentationData.fontSize.baseDisplaySize * 14.0 / 17.0)
|
let fontSize = floor(arguments.presentationData.fontSize.baseDisplaySize * 14.0 / 17.0)
|
||||||
let titleFont = Font.medium(fontSize)
|
let titleFont = Font.medium(fontSize)
|
||||||
let textFont = Font.regular(fontSize)
|
let textFont = Font.regular(fontSize)
|
||||||
@ -114,7 +112,12 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (textString, isMedia, isText) = descriptionStringForMessage(contentSettings: arguments.context.currentContentSettings.with { $0 }, message: EngineMessage(arguments.message), strings: arguments.strings, nameDisplayOrder: arguments.presentationData.nameDisplayOrder, dateTimeFormat: arguments.presentationData.dateTimeFormat, accountPeerId: arguments.context.account.peerId)
|
var (textString, isMedia, isText) = descriptionStringForMessage(contentSettings: arguments.context.currentContentSettings.with { $0 }, message: EngineMessage(arguments.message), strings: arguments.strings, nameDisplayOrder: arguments.presentationData.nameDisplayOrder, dateTimeFormat: arguments.presentationData.dateTimeFormat, accountPeerId: arguments.context.account.peerId)
|
||||||
|
|
||||||
|
if let threadId = arguments.parentMessage.threadId, Int64(arguments.message.id.id) == threadId, let channel = arguments.parentMessage.peers[arguments.parentMessage.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum), let threadInfo = arguments.parentMessage.associatedThreadInfo {
|
||||||
|
titleString = "\(threadInfo.title)"
|
||||||
|
textString = NSAttributedString()
|
||||||
|
}
|
||||||
|
|
||||||
let placeholderColor: UIColor = arguments.message.effectivelyIncoming(arguments.context.account.peerId) ? arguments.presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : arguments.presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor
|
let placeholderColor: UIColor = arguments.message.effectivelyIncoming(arguments.context.account.peerId) ? arguments.presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : arguments.presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor
|
||||||
let titleColor: UIColor
|
let titleColor: UIColor
|
||||||
|
@ -611,7 +611,6 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
|
|
||||||
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
|
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
|
||||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId {
|
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId {
|
||||||
} else if let threadId = item.message.threadId, Int64(replyAttribute.messageId.id) == threadId, let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum) {
|
|
||||||
} else {
|
} else {
|
||||||
replyInfoApply = makeReplyInfoLayout(ChatMessageReplyInfoNode.Arguments(
|
replyInfoApply = makeReplyInfoLayout(ChatMessageReplyInfoNode.Arguments(
|
||||||
presentationData: item.presentationData,
|
presentationData: item.presentationData,
|
||||||
|
@ -621,6 +621,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
if let components = URLComponents(string: "/?" + query) {
|
if let components = URLComponents(string: "/?" + query) {
|
||||||
var channelId: Int64?
|
var channelId: Int64?
|
||||||
var postId: Int32?
|
var postId: Int32?
|
||||||
|
var threadId: Int64?
|
||||||
if let queryItems = components.queryItems {
|
if let queryItems = components.queryItems {
|
||||||
for queryItem in queryItems {
|
for queryItem in queryItems {
|
||||||
if let value = queryItem.value {
|
if let value = queryItem.value {
|
||||||
@ -628,12 +629,22 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
channelId = Int64(value)
|
channelId = Int64(value)
|
||||||
} else if queryItem.name == "post" {
|
} else if queryItem.name == "post" {
|
||||||
postId = Int32(value)
|
postId = Int32(value)
|
||||||
|
} else if queryItem.name == "thread" {
|
||||||
|
threadId = Int64(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let channelId = channelId, let postId = postId {
|
if let channelId = channelId {
|
||||||
convertedUrl = "https://t.me/c/\(channelId)/\(postId)"
|
if let postId = postId {
|
||||||
|
if let threadId = threadId {
|
||||||
|
convertedUrl = "https://t.me/c/\(channelId)/\(threadId)/\(postId)"
|
||||||
|
} else {
|
||||||
|
convertedUrl = "https://t.me/c/\(channelId)/\(postId)"
|
||||||
|
}
|
||||||
|
} else if let threadId = threadId {
|
||||||
|
convertedUrl = "https://t.me/c/\(channelId)/\(threadId)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -652,6 +663,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
var attach: String?
|
var attach: String?
|
||||||
var startAttach: String?
|
var startAttach: String?
|
||||||
var choose: String?
|
var choose: String?
|
||||||
|
var threadId: Int64?
|
||||||
if let queryItems = components.queryItems {
|
if let queryItems = components.queryItems {
|
||||||
for queryItem in queryItems {
|
for queryItem in queryItems {
|
||||||
if let value = queryItem.value {
|
if let value = queryItem.value {
|
||||||
@ -677,6 +689,8 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
startAttach = value
|
startAttach = value
|
||||||
} else if queryItem.name == "choose" {
|
} else if queryItem.name == "choose" {
|
||||||
choose = value
|
choose = value
|
||||||
|
} else if queryItem.name == "thread" {
|
||||||
|
threadId = Int64(value)
|
||||||
}
|
}
|
||||||
} else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) {
|
} else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) {
|
||||||
voiceChat = ""
|
voiceChat = ""
|
||||||
@ -694,8 +708,15 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
convertedUrl = "https://t.me/+\(phone)"
|
convertedUrl = "https://t.me/+\(phone)"
|
||||||
} else if let domain = domain {
|
} else if let domain = domain {
|
||||||
var result = "https://t.me/\(domain)"
|
var result = "https://t.me/\(domain)"
|
||||||
if let post = post, let postValue = Int(post) {
|
if let threadId = threadId {
|
||||||
result += "/\(postValue)"
|
result += "/\(threadId)"
|
||||||
|
if let post = post, let postValue = Int(post) {
|
||||||
|
result += "/\(postValue)"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let post = post, let postValue = Int(post) {
|
||||||
|
result += "/\(postValue)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let start = start {
|
if let start = start {
|
||||||
result += "?start=\(start)"
|
result += "?start=\(start)"
|
||||||
|
@ -416,7 +416,9 @@ public final class OngoingGroupCallContext {
|
|||||||
|
|
||||||
private let broadcastPartsSource = Atomic<BroadcastPartSource?>(value: nil)
|
private let broadcastPartsSource = Atomic<BroadcastPartSource?>(value: nil)
|
||||||
|
|
||||||
init(queue: Queue, inputDeviceId: String, outputDeviceId: String, video: OngoingCallVideoCapturer?, requestMediaChannelDescriptions: @escaping (Set<UInt32>, @escaping ([MediaChannelDescription]) -> Void) -> Disposable, rejoinNeeded: @escaping () -> Void, outgoingAudioBitrateKbit: Int32?, videoContentType: VideoContentType, enableNoiseSuppression: Bool, disableAudioInput: Bool, preferX264: Bool, logPath: String) {
|
private let audioSessionActiveDisposable = MetaDisposable()
|
||||||
|
|
||||||
|
init(queue: Queue, inputDeviceId: String, outputDeviceId: String, audioSessionActive: Signal<Bool, NoError>, video: OngoingCallVideoCapturer?, requestMediaChannelDescriptions: @escaping (Set<UInt32>, @escaping ([MediaChannelDescription]) -> Void) -> Disposable, rejoinNeeded: @escaping () -> Void, outgoingAudioBitrateKbit: Int32?, videoContentType: VideoContentType, enableNoiseSuppression: Bool, disableAudioInput: Bool, preferX264: Bool, logPath: String) {
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
|
|
||||||
var networkStateUpdatedImpl: ((GroupCallNetworkState) -> Void)?
|
var networkStateUpdatedImpl: ((GroupCallNetworkState) -> Void)?
|
||||||
@ -571,9 +573,18 @@ public final class OngoingGroupCallContext {
|
|||||||
strongSelf.joinPayload.set(.single((payload, ssrc)))
|
strongSelf.joinPayload.set(.single((payload, ssrc)))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
self.audioSessionActiveDisposable.set((audioSessionActive
|
||||||
|
|> deliverOn(queue)).start(next: { [weak self] isActive in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.context.setManualAudioSessionIsActive(isActive)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
self.audioSessionActiveDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
func setJoinResponse(payload: String) {
|
func setJoinResponse(payload: String) {
|
||||||
@ -936,10 +947,10 @@ public final class OngoingGroupCallContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(inputDeviceId: String = "", outputDeviceId: String = "", video: OngoingCallVideoCapturer?, requestMediaChannelDescriptions: @escaping (Set<UInt32>, @escaping ([MediaChannelDescription]) -> Void) -> Disposable, rejoinNeeded: @escaping () -> Void, outgoingAudioBitrateKbit: Int32?, videoContentType: VideoContentType, enableNoiseSuppression: Bool, disableAudioInput: Bool, preferX264: Bool, logPath: String) {
|
public init(inputDeviceId: String = "", outputDeviceId: String = "", audioSessionActive: Signal<Bool, NoError>, video: OngoingCallVideoCapturer?, requestMediaChannelDescriptions: @escaping (Set<UInt32>, @escaping ([MediaChannelDescription]) -> Void) -> Disposable, rejoinNeeded: @escaping () -> Void, outgoingAudioBitrateKbit: Int32?, videoContentType: VideoContentType, enableNoiseSuppression: Bool, disableAudioInput: Bool, preferX264: Bool, logPath: String) {
|
||||||
let queue = self.queue
|
let queue = self.queue
|
||||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||||
return Impl(queue: queue, inputDeviceId: inputDeviceId, outputDeviceId: outputDeviceId, video: video, requestMediaChannelDescriptions: requestMediaChannelDescriptions, rejoinNeeded: rejoinNeeded, outgoingAudioBitrateKbit: outgoingAudioBitrateKbit, videoContentType: videoContentType, enableNoiseSuppression: enableNoiseSuppression, disableAudioInput: disableAudioInput, preferX264: preferX264, logPath: logPath)
|
return Impl(queue: queue, inputDeviceId: inputDeviceId, outputDeviceId: outputDeviceId, audioSessionActive: audioSessionActive, video: video, requestMediaChannelDescriptions: requestMediaChannelDescriptions, rejoinNeeded: rejoinNeeded, outgoingAudioBitrateKbit: outgoingAudioBitrateKbit, videoContentType: videoContentType, enableNoiseSuppression: enableNoiseSuppression, disableAudioInput: disableAudioInput, preferX264: preferX264, logPath: logPath)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,6 +326,7 @@ private protocol OngoingCallThreadLocalContextProtocol: AnyObject {
|
|||||||
func nativeVersion() -> String
|
func nativeVersion() -> String
|
||||||
func nativeGetDerivedState() -> Data
|
func nativeGetDerivedState() -> Data
|
||||||
func addExternalAudioData(data: Data)
|
func addExternalAudioData(data: Data)
|
||||||
|
func nativeSetIsAudioSessionActive(isActive: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class OngoingCallThreadLocalContextHolder {
|
private final class OngoingCallThreadLocalContextHolder {
|
||||||
@ -381,6 +382,9 @@ extension OngoingCallThreadLocalContext: OngoingCallThreadLocalContextProtocol {
|
|||||||
|
|
||||||
func addExternalAudioData(data: Data) {
|
func addExternalAudioData(data: Data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nativeSetIsAudioSessionActive(isActive: Bool) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class OngoingCallVideoCapturer {
|
public final class OngoingCallVideoCapturer {
|
||||||
@ -573,6 +577,10 @@ extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProt
|
|||||||
func addExternalAudioData(data: Data) {
|
func addExternalAudioData(data: Data) {
|
||||||
self.addExternalAudioData(data)
|
self.addExternalAudioData(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nativeSetIsAudioSessionActive(isActive: Bool) {
|
||||||
|
self.setManualAudioSessionIsActive(isActive)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension OngoingCallContextState.State {
|
private extension OngoingCallContextState.State {
|
||||||
@ -726,6 +734,7 @@ public final class OngoingCallContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private let audioSessionDisposable = MetaDisposable()
|
private let audioSessionDisposable = MetaDisposable()
|
||||||
|
private let audioSessionActiveDisposable = MetaDisposable()
|
||||||
private var networkTypeDisposable: Disposable?
|
private var networkTypeDisposable: Disposable?
|
||||||
|
|
||||||
public static var maxLayer: Int32 {
|
public static var maxLayer: Int32 {
|
||||||
@ -886,7 +895,7 @@ public final class OngoingCallContext {
|
|||||||
callSessionManager.sendSignalingData(internalId: internalId, data: data)
|
callSessionManager.sendSignalingData(internalId: internalId, data: data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, videoCapturer: video?.impl, preferredVideoCodec: preferredVideoCodec, audioInputDeviceId: "")
|
}, videoCapturer: video?.impl, preferredVideoCodec: preferredVideoCodec, audioInputDeviceId: "", useManualAudioSessionControl: true)
|
||||||
|
|
||||||
strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context))
|
strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context))
|
||||||
context.stateChanged = { [weak callSessionManager] state, videoState, remoteVideoState, remoteAudioState, remoteBatteryLevel, _ in
|
context.stateChanged = { [weak callSessionManager] state, videoState, remoteVideoState, remoteAudioState, remoteBatteryLevel, _ in
|
||||||
@ -950,6 +959,16 @@ public final class OngoingCallContext {
|
|||||||
self?.audioLevelPromise.set(.single(level))
|
self?.audioLevelPromise.set(.single(level))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strongSelf.audioSessionActiveDisposable.set((audioSessionActive
|
||||||
|
|> deliverOn(queue)).start(next: { isActive in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.withContext { context in
|
||||||
|
context.nativeSetIsAudioSessionActive(isActive: isActive)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
strongSelf.networkTypeDisposable = (updatedNetworkType
|
strongSelf.networkTypeDisposable = (updatedNetworkType
|
||||||
|> deliverOn(queue)).start(next: { networkType in
|
|> deliverOn(queue)).start(next: { networkType in
|
||||||
self?.withContext { context in
|
self?.withContext { context in
|
||||||
@ -1010,6 +1029,7 @@ public final class OngoingCallContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.audioSessionDisposable.dispose()
|
self.audioSessionDisposable.dispose()
|
||||||
|
self.audioSessionActiveDisposable.dispose()
|
||||||
self.networkTypeDisposable?.dispose()
|
self.networkTypeDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1256,6 +1276,8 @@ private final class CallSignalingConnectionImpl: CallSignalingConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
|
OngoingCallThreadLocalContextWebrtc.logMessage("CallSignaling: Connecting...")
|
||||||
|
|
||||||
self.connection.start(queue: self.queue.queue)
|
self.connection.start(queue: self.queue.queue)
|
||||||
self.receivePacketHeader()
|
self.receivePacketHeader()
|
||||||
}
|
}
|
||||||
@ -1399,48 +1421,91 @@ private final class CallSignalingConnectionImpl: CallSignalingConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class CallSignalingConnectionManager {
|
private final class CallSignalingConnectionManager {
|
||||||
|
private final class ConnectionContext {
|
||||||
|
let connection: CallSignalingConnection
|
||||||
|
let host: String
|
||||||
|
let port: UInt16
|
||||||
|
|
||||||
|
init(connection: CallSignalingConnection, host: String, port: UInt16) {
|
||||||
|
self.connection = connection
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private let queue: Queue
|
private let queue: Queue
|
||||||
|
private let peerTag: Data
|
||||||
|
private let dataReceived: (Data) -> Void
|
||||||
|
|
||||||
|
private var isRunning: Bool = false
|
||||||
|
|
||||||
private var nextConnectionId: Int = 0
|
private var nextConnectionId: Int = 0
|
||||||
private var connections: [Int: CallSignalingConnection] = [:]
|
private var connections: [Int: ConnectionContext] = [:]
|
||||||
|
|
||||||
init(queue: Queue, peerTag: Data, servers: [OngoingCallConnectionDescriptionWebrtc], dataReceived: @escaping (Data) -> Void) {
|
init(queue: Queue, peerTag: Data, servers: [OngoingCallConnectionDescriptionWebrtc], dataReceived: @escaping (Data) -> Void) {
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
|
self.peerTag = peerTag
|
||||||
|
self.dataReceived = dataReceived
|
||||||
|
|
||||||
for server in servers {
|
for server in servers {
|
||||||
if server.hasTcp {
|
if server.hasTcp {
|
||||||
let id = self.nextConnectionId
|
self.spawnConnection(host: server.ip, port: UInt16(server.port))
|
||||||
self.nextConnectionId += 1
|
|
||||||
if #available(iOS 12.0, *) {
|
|
||||||
let connection = CallSignalingConnectionImpl(queue: queue, host: server.ip, port: UInt16(server.port), peerTag: peerTag, dataReceived: { data in
|
|
||||||
dataReceived(data)
|
|
||||||
}, isClosed: { [weak self] in
|
|
||||||
guard let strongSelf = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let _ = strongSelf
|
|
||||||
})
|
|
||||||
connections[id] = connection
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
|
if self.isRunning {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.isRunning = true
|
||||||
|
|
||||||
for (_, connection) in self.connections {
|
for (_, connection) in self.connections {
|
||||||
connection.start()
|
connection.connection.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func stop() {
|
func stop() {
|
||||||
|
if !self.isRunning {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.isRunning = false
|
||||||
|
|
||||||
for (_, connection) in self.connections {
|
for (_, connection) in self.connections {
|
||||||
connection.stop()
|
connection.connection.stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func send(payloadData: Data) {
|
func send(payloadData: Data) {
|
||||||
for (_, connection) in self.connections {
|
for (_, connection) in self.connections {
|
||||||
connection.send(payloadData: payloadData)
|
connection.connection.send(payloadData: payloadData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func spawnConnection(host: String, port: UInt16) {
|
||||||
|
let id = self.nextConnectionId
|
||||||
|
self.nextConnectionId += 1
|
||||||
|
if #available(iOS 12.0, *) {
|
||||||
|
let dataReceived = self.dataReceived
|
||||||
|
let connection = CallSignalingConnectionImpl(queue: queue, host: host, port: port, peerTag: self.peerTag, dataReceived: { data in
|
||||||
|
dataReceived(data)
|
||||||
|
}, isClosed: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.handleConnectionFailed(id: id)
|
||||||
|
})
|
||||||
|
self.connections[id] = ConnectionContext(connection: connection, host: host, port: port)
|
||||||
|
if self.isRunning {
|
||||||
|
connection.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleConnectionFailed(id: Int) {
|
||||||
|
if let connection = self.connections.removeValue(forKey: id) {
|
||||||
|
connection.connection.stop()
|
||||||
|
self.spawnConnection(host: connection.host, port: connection.port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,7 +206,24 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
|
|||||||
@property (nonatomic, copy) void (^ _Nullable signalBarsChanged)(int32_t);
|
@property (nonatomic, copy) void (^ _Nullable signalBarsChanged)(int32_t);
|
||||||
@property (nonatomic, copy) void (^ _Nullable audioLevelUpdated)(float);
|
@property (nonatomic, copy) void (^ _Nullable audioLevelUpdated)(float);
|
||||||
|
|
||||||
- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing connections:(NSArray<OngoingCallConnectionDescriptionWebrtc *> * _Nonnull)connections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P allowTCP:(BOOL)allowTCP enableStunMarking:(BOOL)enableStunMarking logPath:(NSString * _Nonnull)logPath statsLogPath:(NSString * _Nonnull)statsLogPath sendSignalingData:(void (^ _Nonnull)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec audioInputDeviceId: (NSString * _Nonnull)audioInputDeviceId;
|
- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue
|
||||||
|
proxy:(VoipProxyServerWebrtc * _Nullable)proxy
|
||||||
|
networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving
|
||||||
|
derivedState:(NSData * _Nonnull)derivedState
|
||||||
|
key:(NSData * _Nonnull)key
|
||||||
|
isOutgoing:(bool)isOutgoing
|
||||||
|
connections:(NSArray<OngoingCallConnectionDescriptionWebrtc *> * _Nonnull)connections maxLayer:(int32_t)maxLayer
|
||||||
|
allowP2P:(BOOL)allowP2P
|
||||||
|
allowTCP:(BOOL)allowTCP
|
||||||
|
enableStunMarking:(BOOL)enableStunMarking
|
||||||
|
logPath:(NSString * _Nonnull)logPath
|
||||||
|
statsLogPath:(NSString * _Nonnull)statsLogPath
|
||||||
|
sendSignalingData:(void (^ _Nonnull)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer
|
||||||
|
preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec
|
||||||
|
audioInputDeviceId:(NSString * _Nonnull)audioInputDeviceId
|
||||||
|
useManualAudioSessionControl:(bool)useManualAudioSessionControl;
|
||||||
|
|
||||||
|
- (void)setManualAudioSessionIsActive:(bool)isAudioSessionActive;
|
||||||
|
|
||||||
- (void)beginTermination;
|
- (void)beginTermination;
|
||||||
- (void)stop:(void (^_Nullable)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile))completion;
|
- (void)stop:(void (^_Nullable)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile))completion;
|
||||||
@ -360,6 +377,8 @@ typedef NS_ENUM(int32_t, OngoingGroupCallRequestedVideoQuality) {
|
|||||||
|
|
||||||
- (void)stop;
|
- (void)stop;
|
||||||
|
|
||||||
|
- (void)setManualAudioSessionIsActive:(bool)isAudioSessionActive;
|
||||||
|
|
||||||
- (void)setConnectionMode:(OngoingCallConnectionMode)connectionMode keepBroadcastConnectedIfWasEnabled:(bool)keepBroadcastConnectedIfWasEnabled isUnifiedBroadcast:(bool)isUnifiedBroadcast;
|
- (void)setConnectionMode:(OngoingCallConnectionMode)connectionMode keepBroadcastConnectedIfWasEnabled:(bool)keepBroadcastConnectedIfWasEnabled isUnifiedBroadcast:(bool)isUnifiedBroadcast;
|
||||||
|
|
||||||
- (void)emitJoinPayload:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion;
|
- (void)emitJoinPayload:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion;
|
||||||
|
@ -28,6 +28,9 @@
|
|||||||
|
|
||||||
#include "platform/darwin/iOS/tgcalls_audio_device_module_ios.h"
|
#include "platform/darwin/iOS/tgcalls_audio_device_module_ios.h"
|
||||||
|
|
||||||
|
#include "platform/darwin/iOS/RTCAudioSession.h"
|
||||||
|
#include "platform/darwin/iOS/RTCAudioSessionConfiguration.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#import "group/GroupInstanceImpl.h"
|
#import "group/GroupInstanceImpl.h"
|
||||||
@ -705,6 +708,8 @@ tgcalls::VideoCaptureInterfaceObject *GetVideoCaptureAssumingSameThread(tgcalls:
|
|||||||
id<OngoingCallThreadLocalContextQueueWebrtc> _queue;
|
id<OngoingCallThreadLocalContextQueueWebrtc> _queue;
|
||||||
int32_t _contextId;
|
int32_t _contextId;
|
||||||
|
|
||||||
|
bool _useManualAudioSessionControl;
|
||||||
|
|
||||||
OngoingCallNetworkTypeWebrtc _networkType;
|
OngoingCallNetworkTypeWebrtc _networkType;
|
||||||
NSTimeInterval _callReceiveTimeout;
|
NSTimeInterval _callReceiveTimeout;
|
||||||
NSTimeInterval _callRingTimeout;
|
NSTimeInterval _callRingTimeout;
|
||||||
@ -843,7 +848,22 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing connections:(NSArray<OngoingCallConnectionDescriptionWebrtc *> * _Nonnull)connections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P allowTCP:(BOOL)allowTCP enableStunMarking:(BOOL)enableStunMarking logPath:(NSString * _Nonnull)logPath statsLogPath:(NSString * _Nonnull)statsLogPath sendSignalingData:(void (^)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec audioInputDeviceId: (NSString * _Nonnull)audioInputDeviceId {
|
- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue
|
||||||
|
proxy:(VoipProxyServerWebrtc * _Nullable)proxy
|
||||||
|
networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving
|
||||||
|
derivedState:(NSData * _Nonnull)derivedState
|
||||||
|
key:(NSData * _Nonnull)key
|
||||||
|
isOutgoing:(bool)isOutgoing
|
||||||
|
connections:(NSArray<OngoingCallConnectionDescriptionWebrtc *> * _Nonnull)connections maxLayer:(int32_t)maxLayer
|
||||||
|
allowP2P:(BOOL)allowP2P
|
||||||
|
allowTCP:(BOOL)allowTCP
|
||||||
|
enableStunMarking:(BOOL)enableStunMarking
|
||||||
|
logPath:(NSString * _Nonnull)logPath
|
||||||
|
statsLogPath:(NSString * _Nonnull)statsLogPath
|
||||||
|
sendSignalingData:(void (^ _Nonnull)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer
|
||||||
|
preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec
|
||||||
|
audioInputDeviceId:(NSString * _Nonnull)audioInputDeviceId
|
||||||
|
useManualAudioSessionControl:(bool)useManualAudioSessionControl {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self != nil) {
|
if (self != nil) {
|
||||||
_version = version;
|
_version = version;
|
||||||
@ -852,6 +872,25 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||||||
|
|
||||||
assert([[OngoingCallThreadLocalContextWebrtc versionsWithIncludeReference:true] containsObject:version]);
|
assert([[OngoingCallThreadLocalContextWebrtc versionsWithIncludeReference:true] containsObject:version]);
|
||||||
|
|
||||||
|
_useManualAudioSessionControl = useManualAudioSessionControl;
|
||||||
|
[RTCAudioSession sharedInstance].useManualAudio = true;
|
||||||
|
|
||||||
|
#ifdef WEBRTC_IOS
|
||||||
|
RTCAudioSessionConfiguration *sharedConfiguration = [RTCAudioSessionConfiguration webRTCConfiguration];
|
||||||
|
if (useManualAudioSessionControl) {
|
||||||
|
sharedConfiguration.mode = AVAudioSessionModeDefault;
|
||||||
|
} else {
|
||||||
|
sharedConfiguration.mode = AVAudioSessionModeVoiceChat;
|
||||||
|
}
|
||||||
|
sharedConfiguration.categoryOptions |= AVAudioSessionCategoryOptionMixWithOthers;
|
||||||
|
sharedConfiguration.outputNumberOfChannels = 1;
|
||||||
|
[RTCAudioSessionConfiguration setWebRTCConfiguration:sharedConfiguration];
|
||||||
|
|
||||||
|
/*[RTCAudioSession sharedInstance].useManualAudio = true;
|
||||||
|
[[RTCAudioSession sharedInstance] audioSessionDidActivate:[AVAudioSession sharedInstance]];
|
||||||
|
[RTCAudioSession sharedInstance].isAudioEnabled = true;*/
|
||||||
|
#endif
|
||||||
|
|
||||||
_callReceiveTimeout = 20.0;
|
_callReceiveTimeout = 20.0;
|
||||||
_callRingTimeout = 90.0;
|
_callRingTimeout = 90.0;
|
||||||
_callConnectTimeout = 30.0;
|
_callConnectTimeout = 30.0;
|
||||||
@ -1094,6 +1133,17 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||||||
- (void)beginTermination {
|
- (void)beginTermination {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setManualAudioSessionIsActive:(bool)isAudioSessionActive {
|
||||||
|
if (_useManualAudioSessionControl) {
|
||||||
|
if (isAudioSessionActive) {
|
||||||
|
[[RTCAudioSession sharedInstance] audioSessionDidActivate:[AVAudioSession sharedInstance]];
|
||||||
|
} else {
|
||||||
|
[[RTCAudioSession sharedInstance] audioSessionDidDeactivate:[AVAudioSession sharedInstance]];
|
||||||
|
}
|
||||||
|
[RTCAudioSession sharedInstance].isAudioEnabled = isAudioSessionActive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+ (void)stopWithTerminationResult:(OngoingCallThreadLocalContextWebrtcTerminationResult *)terminationResult completion:(void (^)(NSString *, int64_t, int64_t, int64_t, int64_t))completion {
|
+ (void)stopWithTerminationResult:(OngoingCallThreadLocalContextWebrtcTerminationResult *)terminationResult completion:(void (^)(NSString *, int64_t, int64_t, int64_t, int64_t))completion {
|
||||||
if (completion) {
|
if (completion) {
|
||||||
if (terminationResult) {
|
if (terminationResult) {
|
||||||
@ -1429,6 +1479,22 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WEBRTC_IOS
|
||||||
|
RTCAudioSessionConfiguration *sharedConfiguration = [RTCAudioSessionConfiguration webRTCConfiguration];
|
||||||
|
sharedConfiguration.mode = AVAudioSessionModeVoiceChat;
|
||||||
|
sharedConfiguration.categoryOptions |= AVAudioSessionCategoryOptionMixWithOthers;
|
||||||
|
if (disableAudioInput) {
|
||||||
|
sharedConfiguration.outputNumberOfChannels = 2;
|
||||||
|
} else {
|
||||||
|
sharedConfiguration.outputNumberOfChannels = 1;
|
||||||
|
}
|
||||||
|
[RTCAudioSessionConfiguration setWebRTCConfiguration:sharedConfiguration];
|
||||||
|
|
||||||
|
/*[RTCAudioSession sharedInstance].useManualAudio = true;
|
||||||
|
[[RTCAudioSession sharedInstance] audioSessionDidActivate:[AVAudioSession sharedInstance]];
|
||||||
|
[RTCAudioSession sharedInstance].isAudioEnabled = true;*/
|
||||||
|
#endif
|
||||||
|
|
||||||
std::vector<tgcalls::VideoCodecName> videoCodecPreferences;
|
std::vector<tgcalls::VideoCodecName> videoCodecPreferences;
|
||||||
|
|
||||||
int minOutgoingVideoBitrateKbit = 500;
|
int minOutgoingVideoBitrateKbit = 500;
|
||||||
@ -1612,6 +1678,15 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setManualAudioSessionIsActive:(bool)isAudioSessionActive {
|
||||||
|
if (isAudioSessionActive) {
|
||||||
|
[[RTCAudioSession sharedInstance] audioSessionDidActivate:[AVAudioSession sharedInstance]];
|
||||||
|
} else {
|
||||||
|
[[RTCAudioSession sharedInstance] audioSessionDidDeactivate:[AVAudioSession sharedInstance]];
|
||||||
|
}
|
||||||
|
[RTCAudioSession sharedInstance].isAudioEnabled = isAudioSessionActive;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setConnectionMode:(OngoingCallConnectionMode)connectionMode keepBroadcastConnectedIfWasEnabled:(bool)keepBroadcastConnectedIfWasEnabled isUnifiedBroadcast:(bool)isUnifiedBroadcast {
|
- (void)setConnectionMode:(OngoingCallConnectionMode)connectionMode keepBroadcastConnectedIfWasEnabled:(bool)keepBroadcastConnectedIfWasEnabled isUnifiedBroadcast:(bool)isUnifiedBroadcast {
|
||||||
if (_instance) {
|
if (_instance) {
|
||||||
tgcalls::GroupConnectionMode mappedConnectionMode;
|
tgcalls::GroupConnectionMode mappedConnectionMode;
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 53bb1711ae0b3810d34edb1c81982b18d70c5506
|
Subproject commit 0aa4b1277fd018e56bf194d72b5405e397c6918b
|
Loading…
x
Reference in New Issue
Block a user