mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-08 08:31:13 +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";
|
||||
|
||||
"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.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
|
||||
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) {
|
||||
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) {
|
||||
|
@ -795,7 +795,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
} else {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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
|
||||
switch contentPeer {
|
||||
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 {
|
||||
initialHideAuthor = true
|
||||
|
@ -45,11 +45,18 @@ private func messageGroupType(messages: [EngineMessage]) -> MessageGroupType {
|
||||
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 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 messageText: String
|
||||
var spoilers: [NSRange]?
|
||||
|
@ -1697,85 +1697,160 @@ public final class ChatListNode: ListView {
|
||||
})
|
||||
|
||||
self.reorderItem = { [weak self] fromIndex, toIndex, transactionOpaqueState -> Signal<Bool, NoError> in
|
||||
if let strongSelf = self, let filteredEntries = (transactionOpaqueState as? ChatListOpaqueTransactionState)?.chatListView.filteredEntries {
|
||||
guard case let .chatList(groupId) = strongSelf.location else {
|
||||
return .single(false)
|
||||
guard let strongSelf = self, let filteredEntries = (transactionOpaqueState as? ChatListOpaqueTransactionState)?.chatListView.filteredEntries else {
|
||||
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 {
|
||||
let fromEntry = filteredEntries[filteredEntries.count - 1 - fromIndex]
|
||||
let toEntry = filteredEntries[filteredEntries.count - 1 - toIndex]
|
||||
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())
|
||||
}
|
||||
|
||||
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)
|
||||
let engine = strongSelf.context.engine
|
||||
return engine.peers.getPinnedItemIds(location: location)
|
||||
|> mapToSignal { itemIds -> Signal<Bool, NoError> in
|
||||
var itemIds = itemIds
|
||||
|
||||
var itemId: EngineChatList.PinnedItem.Id?
|
||||
switch fromEntry {
|
||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
if case let .chatList(index) = index {
|
||||
itemId = .peer(index.messageIndex.id.peerId)
|
||||
}
|
||||
}
|
||||
default:
|
||||
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
|
||||
return engine.peers.getPinnedItemIds(location: location)
|
||||
|> mapToSignal { itemIds -> Signal<Bool, NoError> in
|
||||
var itemIds = itemIds
|
||||
|
||||
var itemId: EngineChatList.PinnedItem.Id?
|
||||
switch fromEntry {
|
||||
case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
if case let .chatList(index) = index {
|
||||
itemId = .peer(index.messageIndex.id.peerId)
|
||||
}
|
||||
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 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 {
|
||||
}
|
||||
if !inserted {
|
||||
itemIds.append(itemId)
|
||||
}
|
||||
return engine.peers.reorderPinnedItemIds(location: location, itemIds: itemIds)
|
||||
} else if beforeAll {
|
||||
itemIds.insert(itemId, at: 0)
|
||||
} 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
|
||||
|
||||
|
@ -4,10 +4,16 @@ import Postbox
|
||||
|
||||
public extension Message {
|
||||
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 {
|
||||
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)")
|
||||
|
||||
assert(false)
|
||||
//assert(false)
|
||||
//preconditionFailure()
|
||||
}
|
||||
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[-232346616] = { return Api.Update.parse_updateChannelMessageViews($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[277713951] = { return Api.Update.parse_updateChannelTooLong($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 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 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 updateChannelTooLong(flags: Int32, channelId: Int64, pts: Int32?)
|
||||
case updateChannelUserTyping(flags: Int32, channelId: Int64, topMsgId: Int32?, fromId: Api.Peer, action: Api.SendMessageAction)
|
||||
@ -927,11 +928,23 @@ public extension Api {
|
||||
break
|
||||
case .updateChannelPinnedTopic(let flags, let channelId, let topicId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-158027602)
|
||||
buffer.appendInt32(422509539)
|
||||
}
|
||||
serializeInt32(flags, 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
|
||||
case .updateChannelReadMessagesContents(let flags, let channelId, let topMsgId, let messages):
|
||||
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))])
|
||||
case .updateChannelPinnedTopic(let flags, let channelId, let 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):
|
||||
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):
|
||||
@ -2307,12 +2322,31 @@ public extension Api {
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
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 _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
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 {
|
||||
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 {
|
||||
static func reorderUsernames(channel: Api.InputChannel, order: [String]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
|
@ -179,6 +179,8 @@ public class ManagedAudioSessionControl {
|
||||
}
|
||||
|
||||
public final class ManagedAudioSession {
|
||||
public private(set) static var shared: ManagedAudioSession?
|
||||
|
||||
private var nextId: Int32 = 0
|
||||
private let queue: Queue
|
||||
private let hasLoudspeaker: Bool
|
||||
@ -256,6 +258,8 @@ public final class ManagedAudioSession {
|
||||
self.isHeadsetPluggedInValue = self.isHeadsetPluggedIn()
|
||||
self.updateCurrentAudioRouteInfo()
|
||||
}
|
||||
|
||||
ManagedAudioSession.shared = self
|
||||
}
|
||||
|
||||
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 {
|
||||
managedAudioSessionLog("ManagedAudioSession setup \(outputMode) for \(type)")
|
||||
var resetToBuiltin = false
|
||||
|
@ -8,6 +8,7 @@ import TelegramCore
|
||||
import SwiftSignalKit
|
||||
import AppBundle
|
||||
import AccountContext
|
||||
import TelegramAudio
|
||||
|
||||
private let sharedProviderDelegate: AnyObject? = {
|
||||
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, *)
|
||||
@ -125,6 +130,9 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
||||
private var setCallMuted: ((UUID, Bool) -> Void)?
|
||||
private var audioSessionActivationChanged: ((Bool) -> Void)?
|
||||
|
||||
private var isAudioSessionActive: Bool = false
|
||||
private var pendingVoiceChatOutputMode: AudioSessionOutputMode?
|
||||
|
||||
private let disposableSet = DisposableSet()
|
||||
|
||||
fileprivate var audioSessionActivePromise: ValuePromise<Bool>?
|
||||
@ -163,7 +171,7 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
||||
private func requestTransaction(_ transaction: CXTransaction, completion: ((Bool) -> Void)? = nil) {
|
||||
self.callController.request(transaction) { error in
|
||||
if let error = error {
|
||||
print("Error requesting transaction: \(error)")
|
||||
print("Error requesting transaction \(transaction): \(error)")
|
||||
}
|
||||
completion?(error == nil)
|
||||
}
|
||||
@ -238,6 +246,12 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
||||
update.supportsDTMF = false
|
||||
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
|
||||
completion?(error as NSError?)
|
||||
})
|
||||
@ -321,13 +335,28 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
|
||||
print("provider didActivate default? \(audioSession === AVAudioSession.sharedInstance())")
|
||||
self.isAudioSessionActive = true
|
||||
self.audioSessionActivationChanged?(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) {
|
||||
self.isAudioSessionActive = false
|
||||
self.audioSessionActivationChanged?(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
|
||||
}
|
||||
strongSelf.audioOutputStateValue = (availableOutputs, currentOutput)
|
||||
if let currentOutput = currentOutput {
|
||||
strongSelf.currentAudioOutputValue = currentOutput
|
||||
}
|
||||
|
||||
var signal: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> = .single((availableOutputs, currentOutput))
|
||||
if !didReceiveAudioOutputs {
|
||||
@ -437,7 +440,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
let audioSessionActive: Signal<Bool, NoError>
|
||||
if let callKitIntegration = strongSelf.callKitIntegration {
|
||||
audioSessionActive = callKitIntegration.audioSessionActive
|
||||
|> filter { $0 }
|
||||
/*|> filter { $0 }
|
||||
|> timeout(2.0, queue: Queue.mainQueue(), alternate: Signal { subscriber in
|
||||
if let strongSelf = self, let _ = strongSelf.audioSessionControl {
|
||||
//audioSessionControl.activate({ _ in })
|
||||
@ -445,7 +448,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
subscriber.putNext(true)
|
||||
subscriber.putCompletion()
|
||||
return EmptyDisposable
|
||||
})
|
||||
})*/
|
||||
} else {
|
||||
audioSessionControl.activate({ _ in })
|
||||
audioSessionActive = .single(true)
|
||||
@ -534,8 +537,12 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
}
|
||||
|
||||
if let audioSessionControl = audioSessionControl, previous == nil || previousControl == nil {
|
||||
audioSessionControl.setOutputMode(.custom(self.currentAudioOutputValue))
|
||||
audioSessionControl.setup(synchronous: true)
|
||||
if let callKitIntegration = self.callKitIntegration {
|
||||
callKitIntegration.applyVoiceChatOutputMode(outputMode: .custom(self.currentAudioOutputValue))
|
||||
} else {
|
||||
audioSessionControl.setOutputMode(.custom(self.currentAudioOutputValue))
|
||||
audioSessionControl.setup(synchronous: true)
|
||||
}
|
||||
}
|
||||
|
||||
let mappedVideoState: PresentationCallState.VideoState
|
||||
@ -1031,7 +1038,11 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
))
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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()
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self else {
|
||||
@ -2966,7 +2966,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
|
||||
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.screencastJoinDisposable.set((screencastCallContext.joinPayload
|
||||
|
@ -93,7 +93,8 @@ enum AccountStateMutationOperation {
|
||||
case ReadSecretOutbox(peerId: PeerId, maxTimestamp: Int32, actionTimestamp: Int32)
|
||||
case AddPeerInputActivity(chatPeerId: PeerActivitySpace, peerId: PeerId?, activity: PeerInputActivity?)
|
||||
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 UpdateMessageImpressionCount(MessageId, Int32)
|
||||
case UpdateMessageForwardsCount(MessageId, Int32)
|
||||
@ -475,8 +476,12 @@ struct AccountMutableState {
|
||||
self.addOperation(.UpdatePinnedItemIds(groupId, operation))
|
||||
}
|
||||
|
||||
mutating func addUpdatePinnedTopic(peerId: PeerId, threadId: Int64?) {
|
||||
self.addOperation(.UpdatePinnedTopic(peerId: peerId, threadId: threadId))
|
||||
mutating func addUpdatePinnedTopic(peerId: PeerId, threadId: Int64, isPinned: Bool) {
|
||||
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])) {
|
||||
@ -545,7 +550,7 @@ struct AccountMutableState {
|
||||
|
||||
mutating func addOperation(_ operation: AccountStateMutationOperation) {
|
||||
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
|
||||
case let .AddMessages(messages, location):
|
||||
for message in messages {
|
||||
|
@ -367,21 +367,16 @@ func _internal_setForumChannelTopicClosed(account: Account, id: EnginePeer.Id, t
|
||||
|
||||
public enum SetForumChannelTopicPinnedError {
|
||||
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
|
||||
guard let inputChannel = transaction.getPeer(id).flatMap(apiInputChannel) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if isPinned {
|
||||
transaction.setPeerPinnedThreads(peerId: id, threadIds: [threadId])
|
||||
} else {
|
||||
if transaction.getPeerPinnedThreads(peerId: id).contains(threadId) {
|
||||
transaction.setPeerPinnedThreads(peerId: id, threadIds: [])
|
||||
}
|
||||
}
|
||||
transaction.setPeerPinnedThreads(peerId: id, threadIds: threadIds)
|
||||
|
||||
return inputChannel
|
||||
}
|
||||
@ -390,13 +385,11 @@ func _internal_setForumChannelTopicPinned(account: Account, id: EnginePeer.Id, t
|
||||
guard let inputChannel = inputChannel else {
|
||||
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,
|
||||
topicId: Int32(clamping: threadId),
|
||||
pinned: isPinned ? .boolTrue : .boolFalse
|
||||
order: threadIds.map(Int32.init(clamping:))
|
||||
))
|
||||
|> mapError { _ -> SetForumChannelTopicPinnedError in
|
||||
return .generic
|
||||
|
@ -114,6 +114,11 @@ private func peerIdsRequiringLocalChatStateFromUpdates(_ updates: [Api.Update])
|
||||
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))
|
||||
peerIds.insert(peerId)
|
||||
}
|
||||
case let .updateFolderPeers(folderPeers, _, _):
|
||||
for peer in folderPeers {
|
||||
switch peer {
|
||||
@ -337,11 +342,16 @@ private func peerIdsRequiringLocalChatStateFromDifference(_ difference: Api.upda
|
||||
peerIds.insert(messageId.peerId)
|
||||
}
|
||||
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))
|
||||
peerIds.insert(peerId)
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
case .differenceEmpty:
|
||||
@ -359,11 +369,16 @@ private func peerIdsRequiringLocalChatStateFromDifference(_ difference: Api.upda
|
||||
peerIds.insert(messageId.peerId)
|
||||
}
|
||||
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))
|
||||
peerIds.insert(peerId)
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
case .differenceTooLong:
|
||||
@ -725,6 +740,20 @@ private func sortedUpdates(_ updates: [Api.Update]) -> [Api.Update] {
|
||||
} else {
|
||||
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, _, _, _):
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||
if updatesByChannel[peerId] == nil {
|
||||
@ -860,6 +889,15 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
|
||||
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):
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||
if let previousState = updatedState.channelStates[peerId] {
|
||||
@ -1375,8 +1413,9 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
|
||||
} else {
|
||||
updatedState.addUpdatePinnedItemIds(groupId: groupId, operation: .sync)
|
||||
}
|
||||
case let .updateChannelPinnedTopic(_, channelId, topicId):
|
||||
updatedState.addUpdatePinnedTopic(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)), threadId: topicId.flatMap(Int64.init))
|
||||
case let .updateChannelPinnedTopic(flags, channelId, topicId):
|
||||
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, _, _):
|
||||
updatedState.addReadMessagesContents((nil, messages))
|
||||
case let .updateChannelReadMessagesContents(_, channelId, topMsgId, messages):
|
||||
@ -2847,7 +2886,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
|
||||
var currentAddScheduledMessages: OptimizeAddMessagesState?
|
||||
for operation in operations {
|
||||
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 {
|
||||
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
|
||||
}
|
||||
@ -3757,12 +3796,18 @@ func replayFinalState(
|
||||
case .sync:
|
||||
addSynchronizePinnedChatsOperation(transaction: transaction, groupId: groupId)
|
||||
}
|
||||
case let .UpdatePinnedTopic(peerId, threadId):
|
||||
if let threadId = threadId {
|
||||
transaction.setPeerPinnedThreads(peerId: peerId, threadIds: [threadId])
|
||||
case let .UpdatePinnedTopic(peerId, threadId, isPinned):
|
||||
var currentThreadIds = transaction.getPeerPinnedThreads(peerId: peerId)
|
||||
if isPinned {
|
||||
if !currentThreadIds.contains(threadId) {
|
||||
currentThreadIds.insert(threadId, at: 0)
|
||||
}
|
||||
} 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):
|
||||
let (peerId, messageIds) = peerIdAndMessageIds
|
||||
|
||||
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
||||
|
||||
public class Serialization: NSObject, MTSerialization {
|
||||
public func currentLayer() -> UInt {
|
||||
return 148
|
||||
return 149
|
||||
}
|
||||
|
||||
public func parseMessage(_ data: Data!) -> Any! {
|
||||
|
@ -858,8 +858,34 @@ public extension TelegramEngine {
|
||||
|> ignoreValues
|
||||
}
|
||||
|
||||
public func setForumChannelTopicPinned(id: EnginePeer.Id, threadId: Int64, isPinned: Bool) -> Signal<Never, SetForumChannelTopicPinnedError> {
|
||||
return _internal_setForumChannelTopicPinned(account: self.account, id: id, threadId: threadId, isPinned: isPinned)
|
||||
public func toggleForumChannelTopicPinned(id: EnginePeer.Id, threadId: Int64) -> Signal<Never, SetForumChannelTopicPinnedError> {
|
||||
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> {
|
||||
|
@ -30,7 +30,7 @@ public extension Peer {
|
||||
|
||||
if let restrictionInfo = restrictionInfo {
|
||||
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) {
|
||||
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
|
||||
var updated = updated
|
||||
|
||||
updated = updated.updatedInterfaceState({ _ in return interfaceState })
|
||||
|
||||
updated = updated.updatedKeyboardButtonsMessage(combinedInitialData.buttonKeyboardMessage)
|
||||
updated = updated.updatedKeyboardButtonsMessage(buttonKeyboardMessage)
|
||||
updated = updated.updatedPinnedMessageId(pinnedMessageId)
|
||||
updated = updated.updatedPinnedMessage(pinnedMessage)
|
||||
updated = updated.updatedPeerIsBlocked(peerIsBlocked)
|
||||
|
@ -1396,7 +1396,12 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
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)
|
||||
|
||||
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 {
|
||||
switch group.membership {
|
||||
case .Removed, .Left:
|
||||
|
@ -1167,7 +1167,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
|
||||
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[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 {
|
||||
replyInfoApply = makeReplyInfoLayout(ChatMessageReplyInfoNode.Arguments(
|
||||
presentationData: item.presentationData,
|
||||
|
@ -1418,7 +1418,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
}
|
||||
} else if let attribute = attribute as? ReplyMessageAttribute {
|
||||
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 {
|
||||
replyMessage = firstMessage.associatedMessages[attribute.messageId]
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ final class ChatMessageAccessibilityData {
|
||||
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 (_, _, 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
|
||||
|
||||
|
@ -97,8 +97,6 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
let previousMediaReference = maybeNode?.previousMediaReference
|
||||
|
||||
return { arguments in
|
||||
//presentationData, strings, context, type, message, parentMessage, constrainedSize
|
||||
|
||||
let fontSize = floor(arguments.presentationData.fontSize.baseDisplaySize * 14.0 / 17.0)
|
||||
let titleFont = Font.medium(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 titleColor: UIColor
|
||||
|
@ -611,7 +611,6 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
|
||||
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[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 {
|
||||
replyInfoApply = makeReplyInfoLayout(ChatMessageReplyInfoNode.Arguments(
|
||||
presentationData: item.presentationData,
|
||||
|
@ -621,6 +621,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
||||
if let components = URLComponents(string: "/?" + query) {
|
||||
var channelId: Int64?
|
||||
var postId: Int32?
|
||||
var threadId: Int64?
|
||||
if let queryItems = components.queryItems {
|
||||
for queryItem in queryItems {
|
||||
if let value = queryItem.value {
|
||||
@ -628,12 +629,22 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
||||
channelId = Int64(value)
|
||||
} else if queryItem.name == "post" {
|
||||
postId = Int32(value)
|
||||
} else if queryItem.name == "thread" {
|
||||
threadId = Int64(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let channelId = channelId, let postId = postId {
|
||||
convertedUrl = "https://t.me/c/\(channelId)/\(postId)"
|
||||
if let channelId = channelId {
|
||||
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 startAttach: String?
|
||||
var choose: String?
|
||||
var threadId: Int64?
|
||||
if let queryItems = components.queryItems {
|
||||
for queryItem in queryItems {
|
||||
if let value = queryItem.value {
|
||||
@ -677,6 +689,8 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
||||
startAttach = value
|
||||
} else if queryItem.name == "choose" {
|
||||
choose = value
|
||||
} else if queryItem.name == "thread" {
|
||||
threadId = Int64(value)
|
||||
}
|
||||
} else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) {
|
||||
voiceChat = ""
|
||||
@ -694,8 +708,15 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
||||
convertedUrl = "https://t.me/+\(phone)"
|
||||
} else if let domain = domain {
|
||||
var result = "https://t.me/\(domain)"
|
||||
if let post = post, let postValue = Int(post) {
|
||||
result += "/\(postValue)"
|
||||
if let threadId = threadId {
|
||||
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 {
|
||||
result += "?start=\(start)"
|
||||
|
@ -416,7 +416,9 @@ public final class OngoingGroupCallContext {
|
||||
|
||||
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
|
||||
|
||||
var networkStateUpdatedImpl: ((GroupCallNetworkState) -> Void)?
|
||||
@ -571,9 +573,18 @@ public final class OngoingGroupCallContext {
|
||||
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 {
|
||||
self.audioSessionActiveDisposable.dispose()
|
||||
}
|
||||
|
||||
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
|
||||
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 nativeGetDerivedState() -> Data
|
||||
func addExternalAudioData(data: Data)
|
||||
func nativeSetIsAudioSessionActive(isActive: Bool)
|
||||
}
|
||||
|
||||
private final class OngoingCallThreadLocalContextHolder {
|
||||
@ -381,6 +382,9 @@ extension OngoingCallThreadLocalContext: OngoingCallThreadLocalContextProtocol {
|
||||
|
||||
func addExternalAudioData(data: Data) {
|
||||
}
|
||||
|
||||
func nativeSetIsAudioSessionActive(isActive: Bool) {
|
||||
}
|
||||
}
|
||||
|
||||
public final class OngoingCallVideoCapturer {
|
||||
@ -573,6 +577,10 @@ extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProt
|
||||
func addExternalAudioData(data: Data) {
|
||||
self.addExternalAudioData(data)
|
||||
}
|
||||
|
||||
func nativeSetIsAudioSessionActive(isActive: Bool) {
|
||||
self.setManualAudioSessionIsActive(isActive)
|
||||
}
|
||||
}
|
||||
|
||||
private extension OngoingCallContextState.State {
|
||||
@ -726,6 +734,7 @@ public final class OngoingCallContext {
|
||||
}
|
||||
|
||||
private let audioSessionDisposable = MetaDisposable()
|
||||
private let audioSessionActiveDisposable = MetaDisposable()
|
||||
private var networkTypeDisposable: Disposable?
|
||||
|
||||
public static var maxLayer: Int32 {
|
||||
@ -886,7 +895,7 @@ public final class OngoingCallContext {
|
||||
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))
|
||||
context.stateChanged = { [weak callSessionManager] state, videoState, remoteVideoState, remoteAudioState, remoteBatteryLevel, _ in
|
||||
@ -950,6 +959,16 @@ public final class OngoingCallContext {
|
||||
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
|
||||
|> deliverOn(queue)).start(next: { networkType in
|
||||
self?.withContext { context in
|
||||
@ -1010,6 +1029,7 @@ public final class OngoingCallContext {
|
||||
}
|
||||
|
||||
self.audioSessionDisposable.dispose()
|
||||
self.audioSessionActiveDisposable.dispose()
|
||||
self.networkTypeDisposable?.dispose()
|
||||
}
|
||||
|
||||
@ -1256,6 +1276,8 @@ private final class CallSignalingConnectionImpl: CallSignalingConnection {
|
||||
}
|
||||
|
||||
func start() {
|
||||
OngoingCallThreadLocalContextWebrtc.logMessage("CallSignaling: Connecting...")
|
||||
|
||||
self.connection.start(queue: self.queue.queue)
|
||||
self.receivePacketHeader()
|
||||
}
|
||||
@ -1399,48 +1421,91 @@ private final class CallSignalingConnectionImpl: CallSignalingConnection {
|
||||
}
|
||||
|
||||
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 peerTag: Data
|
||||
private let dataReceived: (Data) -> Void
|
||||
|
||||
private var isRunning: Bool = false
|
||||
|
||||
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) {
|
||||
self.queue = queue
|
||||
self.peerTag = peerTag
|
||||
self.dataReceived = dataReceived
|
||||
|
||||
for server in servers {
|
||||
if server.hasTcp {
|
||||
let id = self.nextConnectionId
|
||||
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
|
||||
}
|
||||
self.spawnConnection(host: server.ip, port: UInt16(server.port))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func start() {
|
||||
if self.isRunning {
|
||||
return
|
||||
}
|
||||
self.isRunning = true
|
||||
|
||||
for (_, connection) in self.connections {
|
||||
connection.start()
|
||||
connection.connection.start()
|
||||
}
|
||||
}
|
||||
|
||||
func stop() {
|
||||
if !self.isRunning {
|
||||
return
|
||||
}
|
||||
self.isRunning = false
|
||||
|
||||
for (_, connection) in self.connections {
|
||||
connection.stop()
|
||||
connection.connection.stop()
|
||||
}
|
||||
}
|
||||
|
||||
func send(payloadData: Data) {
|
||||
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 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)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)setManualAudioSessionIsActive:(bool)isAudioSessionActive;
|
||||
|
||||
- (void)setConnectionMode:(OngoingCallConnectionMode)connectionMode keepBroadcastConnectedIfWasEnabled:(bool)keepBroadcastConnectedIfWasEnabled isUnifiedBroadcast:(bool)isUnifiedBroadcast;
|
||||
|
||||
- (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/RTCAudioSession.h"
|
||||
#include "platform/darwin/iOS/RTCAudioSessionConfiguration.h"
|
||||
|
||||
#endif
|
||||
|
||||
#import "group/GroupInstanceImpl.h"
|
||||
@ -705,6 +708,8 @@ tgcalls::VideoCaptureInterfaceObject *GetVideoCaptureAssumingSameThread(tgcalls:
|
||||
id<OngoingCallThreadLocalContextQueueWebrtc> _queue;
|
||||
int32_t _contextId;
|
||||
|
||||
bool _useManualAudioSessionControl;
|
||||
|
||||
OngoingCallNetworkTypeWebrtc _networkType;
|
||||
NSTimeInterval _callReceiveTimeout;
|
||||
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];
|
||||
if (self != nil) {
|
||||
_version = version;
|
||||
@ -852,6 +872,25 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
||||
|
||||
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;
|
||||
_callRingTimeout = 90.0;
|
||||
_callConnectTimeout = 30.0;
|
||||
@ -1094,6 +1133,17 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
||||
- (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 {
|
||||
if (completion) {
|
||||
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;
|
||||
|
||||
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 {
|
||||
if (_instance) {
|
||||
tgcalls::GroupConnectionMode mappedConnectionMode;
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 53bb1711ae0b3810d34edb1c81982b18d70c5506
|
||||
Subproject commit 0aa4b1277fd018e56bf194d72b5405e397c6918b
|
Loading…
x
Reference in New Issue
Block a user