mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
df5e9c7cd3
@ -12,8 +12,8 @@ public enum RequestCallResult {
|
|||||||
case alreadyInProgress(PeerId?)
|
case alreadyInProgress(PeerId?)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum RequestOrJoinGroupCallResult {
|
public enum JoinGroupCallManagerResult {
|
||||||
case requested
|
case joined
|
||||||
case alreadyInProgress(PeerId?)
|
case alreadyInProgress(PeerId?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,5 +278,5 @@ public protocol PresentationCallManager: class {
|
|||||||
var currentGroupCallSignal: Signal<PresentationGroupCall?, NoError> { get }
|
var currentGroupCallSignal: Signal<PresentationGroupCall?, NoError> { get }
|
||||||
|
|
||||||
func requestCall(context: AccountContext, peerId: PeerId, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult
|
func requestCall(context: AccountContext, peerId: PeerId, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult
|
||||||
func requestOrJoinGroupCall(context: AccountContext, peerId: PeerId, initialCall: CachedChannelData.ActiveCall?, endCurrentIfAny: Bool, sourcePanel: ASDisplayNode?) -> RequestOrJoinGroupCallResult
|
func joinGroupCall(context: AccountContext, peerId: PeerId, initialCall: CachedChannelData.ActiveCall, endCurrentIfAny: Bool, sourcePanel: ASDisplayNode?) -> JoinGroupCallManagerResult
|
||||||
}
|
}
|
||||||
|
@ -262,20 +262,19 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
|
|||||||
|
|
||||||
if let callManager = context.sharedContext.callManager {
|
if let callManager = context.sharedContext.callManager {
|
||||||
switch groupCallPanelSource {
|
switch groupCallPanelSource {
|
||||||
case .none:
|
case .none, .all:
|
||||||
break
|
break
|
||||||
default:
|
case let .peer(peerId):
|
||||||
let currentGroupCall: Signal<GroupCallPanelData?, NoError> = callManager.currentGroupCallSignal
|
let currentGroupCall: Signal<GroupCallPanelData?, NoError> = callManager.currentGroupCallSignal
|
||||||
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||||
return lhs?.internalId == rhs?.internalId
|
return lhs?.internalId == rhs?.internalId
|
||||||
})
|
})
|
||||||
|> mapToSignal { call -> Signal<GroupCallPanelData?, NoError> in
|
|> mapToSignal { call -> Signal<GroupCallPanelData?, NoError> in
|
||||||
guard let call = call else {
|
guard let call = call, call.peerId == peerId else {
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
return call.summaryState
|
return call.summaryState
|
||||||
|> filter { $0 != nil }
|
|> filter { $0 != nil }
|
||||||
|> take(1)
|
|
||||||
|> map { summary -> GroupCallPanelData? in
|
|> map { summary -> GroupCallPanelData? in
|
||||||
guard let summary = summary else {
|
guard let summary = summary else {
|
||||||
return nil
|
return nil
|
||||||
@ -289,6 +288,13 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
|
|||||||
groupCall: call
|
groupCall: call
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|> take(until: { summary in
|
||||||
|
if summary != nil {
|
||||||
|
return SignalTakeAction(passthrough: true, complete: true)
|
||||||
|
} else {
|
||||||
|
return SignalTakeAction(passthrough: true, complete: false)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let availableGroupCall: Signal<GroupCallPanelData?, NoError>
|
let availableGroupCall: Signal<GroupCallPanelData?, NoError>
|
||||||
@ -861,7 +867,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func joinGroupCall(peerId: PeerId, info: GroupCallInfo, sourcePanel: GroupCallNavigationAccessoryPanel?) {
|
private func joinGroupCall(peerId: PeerId, info: GroupCallInfo, sourcePanel: GroupCallNavigationAccessoryPanel?) {
|
||||||
let callResult = self.context.sharedContext.callManager?.requestOrJoinGroupCall(context: self.context, peerId: peerId, initialCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash), endCurrentIfAny: false, sourcePanel: sourcePanel)
|
let callResult = self.context.sharedContext.callManager?.joinGroupCall(context: self.context, peerId: peerId, initialCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash), endCurrentIfAny: false, sourcePanel: sourcePanel)
|
||||||
if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult {
|
if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult {
|
||||||
if currentPeerId == peerId {
|
if currentPeerId == peerId {
|
||||||
self.context.sharedContext.navigateToCurrentCall(sourcePanel: sourcePanel)
|
self.context.sharedContext.navigateToCurrentCall(sourcePanel: sourcePanel)
|
||||||
@ -880,7 +886,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
|
|||||||
if let current = current {
|
if let current = current {
|
||||||
strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
|
strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let _ = strongSelf.context.sharedContext.callManager?.requestOrJoinGroupCall(context: strongSelf.context, peerId: peerId, initialCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash), endCurrentIfAny: true, sourcePanel: sourcePanel)
|
let _ = strongSelf.context.sharedContext.callManager?.joinGroupCall(context: strongSelf.context, peerId: peerId, initialCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash), endCurrentIfAny: true, sourcePanel: sourcePanel)
|
||||||
}
|
}
|
||||||
})]), in: .window(.root))
|
})]), in: .window(.root))
|
||||||
} else {
|
} else {
|
||||||
|
@ -593,7 +593,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func requestOrJoinGroupCall(context: AccountContext, peerId: PeerId, initialCall: CachedChannelData.ActiveCall?, endCurrentIfAny: Bool, sourcePanel: ASDisplayNode?) -> RequestOrJoinGroupCallResult {
|
public func joinGroupCall(context: AccountContext, peerId: PeerId, initialCall: CachedChannelData.ActiveCall, endCurrentIfAny: Bool, sourcePanel: ASDisplayNode?) -> JoinGroupCallManagerResult {
|
||||||
let begin: () -> Void = { [weak self] in
|
let begin: () -> Void = { [weak self] in
|
||||||
let _ = self?.startGroupCall(accountContext: context, peerId: peerId, initialCall: initialCall, sourcePanel: sourcePanel).start()
|
let _ = self?.startGroupCall(accountContext: context, peerId: peerId, initialCall: initialCall, sourcePanel: sourcePanel).start()
|
||||||
}
|
}
|
||||||
@ -612,13 +612,13 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
} else {
|
} else {
|
||||||
begin()
|
begin()
|
||||||
}
|
}
|
||||||
return .requested
|
return .joined
|
||||||
}
|
}
|
||||||
|
|
||||||
private func startGroupCall(
|
private func startGroupCall(
|
||||||
accountContext: AccountContext,
|
accountContext: AccountContext,
|
||||||
peerId: PeerId,
|
peerId: PeerId,
|
||||||
initialCall: CachedChannelData.ActiveCall?,
|
initialCall: CachedChannelData.ActiveCall,
|
||||||
internalId: CallSessionInternalId = CallSessionInternalId(),
|
internalId: CallSessionInternalId = CallSessionInternalId(),
|
||||||
sourcePanel: ASDisplayNode?
|
sourcePanel: ASDisplayNode?
|
||||||
) -> Signal<Bool, NoError> {
|
) -> Signal<Bool, NoError> {
|
||||||
|
@ -142,7 +142,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
|
|
||||||
private let getDeviceAccessData: () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void)
|
private let getDeviceAccessData: () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void)
|
||||||
|
|
||||||
private let initialCall: CachedChannelData.ActiveCall?
|
private var initialCall: CachedChannelData.ActiveCall?
|
||||||
public let internalId: CallSessionInternalId
|
public let internalId: CallSessionInternalId
|
||||||
public let peerId: PeerId
|
public let peerId: PeerId
|
||||||
public let peer: Peer?
|
public let peer: Peer?
|
||||||
@ -374,12 +374,26 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
var removedSsrc: [UInt32] = []
|
var removedSsrc: [UInt32] = []
|
||||||
for (callId, update) in updates {
|
for (callId, update) in updates {
|
||||||
if callId == callInfo.id {
|
if callId == callInfo.id {
|
||||||
for participantUpdate in update.participantUpdates {
|
switch update {
|
||||||
if participantUpdate.isRemoved {
|
case let .state(update):
|
||||||
removedSsrc.append(participantUpdate.ssrc)
|
for participantUpdate in update.participantUpdates {
|
||||||
|
if participantUpdate.isRemoved {
|
||||||
|
removedSsrc.append(participantUpdate.ssrc)
|
||||||
|
|
||||||
|
if participantUpdate.peerId == strongSelf.accountContext.account.peerId {
|
||||||
|
strongSelf._canBeRemoved.set(.single(true))
|
||||||
|
}
|
||||||
|
} else if participantUpdate.peerId == strongSelf.accountContext.account.peerId {
|
||||||
|
if case let .estabilished(_, _, ssrc, _) = strongSelf.internalState, ssrc != participantUpdate.ssrc {
|
||||||
|
strongSelf._canBeRemoved.set(.single(true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case let .call(isTerminated):
|
||||||
|
if isTerminated {
|
||||||
|
strongSelf._canBeRemoved.set(.single(true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
strongSelf.participantsContext?.addUpdates(updates: [update])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !removedSsrc.isEmpty {
|
if !removedSsrc.isEmpty {
|
||||||
@ -553,7 +567,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
if case let .estabilished(callInfo, clientParams, _, initialState) = internalState {
|
if case let .estabilished(callInfo, clientParams, _, initialState) = internalState {
|
||||||
self.summaryInfoState.set(.single(SummaryInfoState(info: callInfo)))
|
self.summaryInfoState.set(.single(SummaryInfoState(info: callInfo)))
|
||||||
|
|
||||||
self.stateValue.canManageCall = initialState.isCreator
|
self.stateValue.canManageCall = initialState.isCreator || initialState.adminIds.contains(self.accountContext.account.peerId)
|
||||||
|
|
||||||
self.ssrcMapping.removeAll()
|
self.ssrcMapping.removeAll()
|
||||||
var ssrcs: [UInt32] = []
|
var ssrcs: [UInt32] = []
|
||||||
@ -780,7 +794,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let account = self.account
|
let account = self.account
|
||||||
let peerId = self.peerId
|
|
||||||
|
|
||||||
let currentCall: Signal<GroupCallInfo?, CallError>
|
let currentCall: Signal<GroupCallInfo?, CallError>
|
||||||
if let initialCall = self.initialCall {
|
if let initialCall = self.initialCall {
|
||||||
@ -796,36 +809,27 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let currentOrRequestedCall = currentCall
|
let currentOrRequestedCall = currentCall
|
||||||
|> mapToSignal { callInfo -> Signal<GroupCallInfo, CallError> in
|
|> mapToSignal { callInfo -> Signal<GroupCallInfo?, CallError> in
|
||||||
if let callInfo = callInfo {
|
if let callInfo = callInfo {
|
||||||
return .single(callInfo)
|
return .single(callInfo)
|
||||||
} else {
|
} else {
|
||||||
return createGroupCall(account: account, peerId: peerId)
|
return .single(nil)
|
||||||
|> mapError { _ -> CallError in
|
|
||||||
return .generic
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*let restartedCall = currentOrRequestedCall
|
|
||||||
|> mapToSignal { value -> Signal<GroupCallInfo, CallError> in
|
|
||||||
let stopped: Signal<GroupCallInfo, CallError> = stopGroupCall(account: account, callId: value.id, accessHash: value.accessHash)
|
|
||||||
|> mapError { _ -> CallError in
|
|
||||||
return .generic
|
|
||||||
}
|
|
||||||
|> map { _ -> GroupCallInfo in
|
|
||||||
}
|
|
||||||
|
|
||||||
return stopped
|
|
||||||
|> then(currentOrRequestedCall)
|
|
||||||
}*/
|
|
||||||
|
|
||||||
self.requestDisposable.set((currentOrRequestedCall
|
self.requestDisposable.set((currentOrRequestedCall
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.updateSessionState(internalState: .active(value), audioSessionControl: strongSelf.audioSessionControl)
|
|
||||||
|
if let value = value {
|
||||||
|
strongSelf.initialCall = CachedChannelData.ActiveCall(id: value.id, accessHash: value.accessHash)
|
||||||
|
|
||||||
|
strongSelf.updateSessionState(internalState: .active(value), audioSessionControl: strongSelf.audioSessionControl)
|
||||||
|
} else {
|
||||||
|
strongSelf._canBeRemoved.set(.single(true))
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -868,7 +872,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
if shouldBeSendingTyping != strongSelf.isSendingTyping {
|
if shouldBeSendingTyping != strongSelf.isSendingTyping {
|
||||||
strongSelf.isSendingTyping = shouldBeSendingTyping
|
strongSelf.isSendingTyping = shouldBeSendingTyping
|
||||||
if shouldBeSendingTyping {
|
if shouldBeSendingTyping {
|
||||||
strongSelf.typingDisposable.set(strongSelf.accountContext.account.acquireLocalInputActivity(peerId: PeerActivitySpace(peerId: strongSelf.peerId, category: .voiceChat), activity: .speakingInGroupCall))
|
strongSelf.typingDisposable.set(strongSelf.accountContext.account.acquireLocalInputActivity(peerId: PeerActivitySpace(peerId: strongSelf.peerId, category: .voiceChat), activity: .speakingInGroupCall(timestamp: 0)))
|
||||||
strongSelf.restartMyAudioLevelTimer()
|
strongSelf.restartMyAudioLevelTimer()
|
||||||
} else {
|
} else {
|
||||||
strongSelf.typingDisposable.set(nil)
|
strongSelf.typingDisposable.set(nil)
|
||||||
|
@ -223,7 +223,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
} else {
|
} else {
|
||||||
microphoneColor = UIColor(rgb: 0x979797)
|
microphoneColor = UIColor(rgb: 0x979797)
|
||||||
}
|
}
|
||||||
icon = .microphone(true, microphoneColor)
|
icon = .microphone(self.muteState != nil, microphoneColor)
|
||||||
case .speaking:
|
case .speaking:
|
||||||
text = .text(presentationData.strings.VoiceChat_StatusSpeaking, .constructive)
|
text = .text(presentationData.strings.VoiceChat_StatusSpeaking, .constructive)
|
||||||
icon = .microphone(false, UIColor(rgb: 0x34c759))
|
icon = .microphone(false, UIColor(rgb: 0x34c759))
|
||||||
@ -411,37 +411,38 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let callState = strongSelf.callState, (callState.canManageCall && !callState.adminIds.contains(strongSelf.context.account.peerId)) {
|
||||||
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_RemovePeer, textColor: .destructive, icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_RemovePeer, textColor: .destructive, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.destructiveActionTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.destructiveActionTextColor)
|
||||||
}, action: { [weak self] _, f in
|
}, action: { [weak self] _, f in
|
||||||
f(.dismissWithoutContent)
|
f(.dismissWithoutContent)
|
||||||
|
|
||||||
guard let strongSelf = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme))
|
|
||||||
var items: [ActionSheetItem] = []
|
|
||||||
|
|
||||||
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: peer, chatPeer: peer, action: .removeFromGroup, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder))
|
|
||||||
|
|
||||||
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.VoiceChat_RemovePeerRemove, color: .destructive, action: { [weak actionSheet] in
|
|
||||||
actionSheet?.dismissAnimated()
|
|
||||||
|
|
||||||
|
guard let strongSelf = self else {
|
||||||
}))
|
return
|
||||||
|
}
|
||||||
|
|
||||||
actionSheet.setItemGroups([
|
let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme))
|
||||||
ActionSheetItemGroup(items: items),
|
var items: [ActionSheetItem] = []
|
||||||
ActionSheetItemGroup(items: [
|
|
||||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: peer, chatPeer: peer, action: .removeFromGroup, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder))
|
||||||
actionSheet?.dismissAnimated()
|
|
||||||
})
|
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.VoiceChat_RemovePeerRemove, color: .destructive, action: { [weak actionSheet] in
|
||||||
|
actionSheet?.dismissAnimated()
|
||||||
|
|
||||||
|
|
||||||
|
}))
|
||||||
|
|
||||||
|
actionSheet.setItemGroups([
|
||||||
|
ActionSheetItemGroup(items: items),
|
||||||
|
ActionSheetItemGroup(items: [
|
||||||
|
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||||
|
actionSheet?.dismissAnimated()
|
||||||
|
})
|
||||||
|
])
|
||||||
])
|
])
|
||||||
])
|
strongSelf.controller?.present(actionSheet, in: .window(.root))
|
||||||
strongSelf.controller?.present(actionSheet, in: .window(.root))
|
})))
|
||||||
})))
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1139,11 +1140,13 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
var callMembers = callMembers
|
var callMembers = callMembers
|
||||||
|
|
||||||
|
callMembers.sort()
|
||||||
|
|
||||||
for i in 0 ..< callMembers.count {
|
for i in 0 ..< callMembers.count {
|
||||||
if callMembers[i].peer.id == self.context.account.peerId {
|
if callMembers[i].peer.id == self.context.account.peerId {
|
||||||
let member = callMembers[i]
|
let member = callMembers[i]
|
||||||
callMembers.remove(at: i)
|
callMembers.remove(at: i)
|
||||||
callMembers.insert(member, at: i)
|
callMembers.insert(member, at: 0)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,7 @@ enum AccountStateMutationOperation {
|
|||||||
case UpdateChatListFilter(id: Int32, filter: Api.DialogFilter?)
|
case UpdateChatListFilter(id: Int32, filter: Api.DialogFilter?)
|
||||||
case UpdateReadThread(threadMessageId: MessageId, readMaxId: Int32, isIncoming: Bool, mainChannelMessage: MessageId?)
|
case UpdateReadThread(threadMessageId: MessageId, readMaxId: Int32, isIncoming: Bool, mainChannelMessage: MessageId?)
|
||||||
case UpdateGroupCallParticipants(id: Int64, accessHash: Int64, participants: [Api.GroupCallParticipant], version: Int32)
|
case UpdateGroupCallParticipants(id: Int64, accessHash: Int64, participants: [Api.GroupCallParticipant], version: Int32)
|
||||||
|
case UpdateGroupCall(call: Api.GroupCall)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HoleFromPreviousState {
|
struct HoleFromPreviousState {
|
||||||
@ -281,6 +282,10 @@ struct AccountMutableState {
|
|||||||
self.addOperation(.UpdateGroupCallParticipants(id: id, accessHash: accessHash, participants: participants, version: version))
|
self.addOperation(.UpdateGroupCallParticipants(id: id, accessHash: accessHash, participants: participants, version: version))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutating func updateGroupCall(call: Api.GroupCall) {
|
||||||
|
self.addOperation(.UpdateGroupCall(call: call))
|
||||||
|
}
|
||||||
|
|
||||||
mutating func readGroupFeedInbox(groupId: PeerGroupId, index: MessageIndex) {
|
mutating func readGroupFeedInbox(groupId: PeerGroupId, index: MessageIndex) {
|
||||||
self.addOperation(.ReadGroupFeedInbox(groupId, index))
|
self.addOperation(.ReadGroupFeedInbox(groupId, index))
|
||||||
}
|
}
|
||||||
@ -489,7 +494,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, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateMessagesPinned:
|
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned:
|
||||||
break
|
break
|
||||||
case let .AddMessages(messages, location):
|
case let .AddMessages(messages, location):
|
||||||
for message in messages {
|
for message in messages {
|
||||||
@ -607,7 +612,7 @@ struct AccountReplayedFinalState {
|
|||||||
let updatedWebpages: [MediaId: TelegramMediaWebpage]
|
let updatedWebpages: [MediaId: TelegramMediaWebpage]
|
||||||
let updatedCalls: [Api.PhoneCall]
|
let updatedCalls: [Api.PhoneCall]
|
||||||
let addedCallSignalingData: [(Int64, Data)]
|
let addedCallSignalingData: [(Int64, Data)]
|
||||||
let updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.StateUpdate)]
|
let updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)]
|
||||||
let updatedPeersNearby: [PeerNearby]?
|
let updatedPeersNearby: [PeerNearby]?
|
||||||
let isContactUpdates: [(PeerId, Bool)]
|
let isContactUpdates: [(PeerId, Bool)]
|
||||||
let delayNotificatonsUntil: Int32?
|
let delayNotificatonsUntil: Int32?
|
||||||
@ -623,7 +628,7 @@ struct AccountFinalStateEvents {
|
|||||||
let updatedWebpages: [MediaId: TelegramMediaWebpage]
|
let updatedWebpages: [MediaId: TelegramMediaWebpage]
|
||||||
let updatedCalls: [Api.PhoneCall]
|
let updatedCalls: [Api.PhoneCall]
|
||||||
let addedCallSignalingData: [(Int64, Data)]
|
let addedCallSignalingData: [(Int64, Data)]
|
||||||
let updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.StateUpdate)]
|
let updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)]
|
||||||
let updatedPeersNearby: [PeerNearby]?
|
let updatedPeersNearby: [PeerNearby]?
|
||||||
let isContactUpdates: [(PeerId, Bool)]
|
let isContactUpdates: [(PeerId, Bool)]
|
||||||
let displayAlerts: [(text: String, isDropAuth: Bool)]
|
let displayAlerts: [(text: String, isDropAuth: Bool)]
|
||||||
@ -639,7 +644,7 @@ struct AccountFinalStateEvents {
|
|||||||
return self.addedIncomingMessageIds.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty
|
return self.addedIncomingMessageIds.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
init(addedIncomingMessageIds: [MessageId] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.StateUpdate)] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set<PeerId> = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:]) {
|
init(addedIncomingMessageIds: [MessageId] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set<PeerId> = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:]) {
|
||||||
self.addedIncomingMessageIds = addedIncomingMessageIds
|
self.addedIncomingMessageIds = addedIncomingMessageIds
|
||||||
self.wasScheduledMessageIds = wasScheduledMessageIds
|
self.wasScheduledMessageIds = wasScheduledMessageIds
|
||||||
self.deletedMessageIds = deletedMessageIds
|
self.deletedMessageIds = deletedMessageIds
|
||||||
|
@ -1221,7 +1221,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
|
|||||||
updatedState.readSecretOutbox(peerId: PeerId(namespace: Namespaces.Peer.SecretChat, id: chatId), timestamp: maxDate, actionTimestamp: date)
|
updatedState.readSecretOutbox(peerId: PeerId(namespace: Namespaces.Peer.SecretChat, id: chatId), timestamp: maxDate, actionTimestamp: date)
|
||||||
case let .updateUserTyping(userId, type):
|
case let .updateUserTyping(userId, type):
|
||||||
if let date = updatesDate, date + 60 > serverTime {
|
if let date = updatesDate, date + 60 > serverTime {
|
||||||
let activity = PeerInputActivity(apiType: type)
|
let activity = PeerInputActivity(apiType: type, timestamp: date)
|
||||||
var category: PeerActivitySpace.Category = .global
|
var category: PeerActivitySpace.Category = .global
|
||||||
if case .speakingInGroupCall = activity {
|
if case .speakingInGroupCall = activity {
|
||||||
category = .voiceChat
|
category = .voiceChat
|
||||||
@ -1231,7 +1231,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
|
|||||||
}
|
}
|
||||||
case let .updateChatUserTyping(chatId, userId, type):
|
case let .updateChatUserTyping(chatId, userId, type):
|
||||||
if let date = updatesDate, date + 60 > serverTime {
|
if let date = updatesDate, date + 60 > serverTime {
|
||||||
let activity = PeerInputActivity(apiType: type)
|
let activity = PeerInputActivity(apiType: type, timestamp: date)
|
||||||
var category: PeerActivitySpace.Category = .global
|
var category: PeerActivitySpace.Category = .global
|
||||||
if case .speakingInGroupCall = activity {
|
if case .speakingInGroupCall = activity {
|
||||||
category = .voiceChat
|
category = .voiceChat
|
||||||
@ -1244,7 +1244,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
|
|||||||
let channelPeerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
let channelPeerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
||||||
let threadId = topMsgId.flatMap { makeMessageThreadId(MessageId(peerId: channelPeerId, namespace: Namespaces.Message.Cloud, id: $0)) }
|
let threadId = topMsgId.flatMap { makeMessageThreadId(MessageId(peerId: channelPeerId, namespace: Namespaces.Message.Cloud, id: $0)) }
|
||||||
|
|
||||||
let activity = PeerInputActivity(apiType: type)
|
let activity = PeerInputActivity(apiType: type, timestamp: date)
|
||||||
var category: PeerActivitySpace.Category = .global
|
var category: PeerActivitySpace.Category = .global
|
||||||
if case .speakingInGroupCall = activity {
|
if case .speakingInGroupCall = activity {
|
||||||
category = .voiceChat
|
category = .voiceChat
|
||||||
@ -1332,6 +1332,8 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
|
|||||||
case let .inputGroupCall(id, accessHash):
|
case let .inputGroupCall(id, accessHash):
|
||||||
updatedState.updateGroupCallParticipants(id: id, accessHash: accessHash, participants: participants, version: version)
|
updatedState.updateGroupCallParticipants(id: id, accessHash: accessHash, participants: participants, version: version)
|
||||||
}
|
}
|
||||||
|
case let .updateGroupCall(call):
|
||||||
|
updatedState.updateGroupCall(call: call)
|
||||||
case let .updateLangPackTooLong(langCode):
|
case let .updateLangPackTooLong(langCode):
|
||||||
updatedState.updateLangPack(langCode: langCode, difference: nil)
|
updatedState.updateLangPack(langCode: langCode, difference: nil)
|
||||||
case let .updateLangPack(difference):
|
case let .updateLangPack(difference):
|
||||||
@ -2135,7 +2137,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, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants:
|
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, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall:
|
||||||
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))
|
||||||
}
|
}
|
||||||
@ -2222,7 +2224,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
|||||||
var updatedWebpages: [MediaId: TelegramMediaWebpage] = [:]
|
var updatedWebpages: [MediaId: TelegramMediaWebpage] = [:]
|
||||||
var updatedCalls: [Api.PhoneCall] = []
|
var updatedCalls: [Api.PhoneCall] = []
|
||||||
var addedCallSignalingData: [(Int64, Data)] = []
|
var addedCallSignalingData: [(Int64, Data)] = []
|
||||||
var updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.StateUpdate)] = []
|
var updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = []
|
||||||
var updatedPeersNearby: [PeerNearby]?
|
var updatedPeersNearby: [PeerNearby]?
|
||||||
var isContactUpdates: [(PeerId, Bool)] = []
|
var isContactUpdates: [(PeerId, Bool)] = []
|
||||||
var stickerPackOperations: [AccountStateUpdateStickerPacksOperation] = []
|
var stickerPackOperations: [AccountStateUpdateStickerPacksOperation] = []
|
||||||
@ -2955,8 +2957,18 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
|||||||
case let .UpdateGroupCallParticipants(callId, _, participants, version):
|
case let .UpdateGroupCallParticipants(callId, _, participants, version):
|
||||||
updatedGroupCallParticipants.append((
|
updatedGroupCallParticipants.append((
|
||||||
callId,
|
callId,
|
||||||
GroupCallParticipantsContext.StateUpdate(participants: participants, version: version)
|
.state(update: GroupCallParticipantsContext.Update.StateUpdate(participants: participants, version: version))
|
||||||
))
|
))
|
||||||
|
case let .UpdateGroupCall(call):
|
||||||
|
switch call {
|
||||||
|
case .groupCall:
|
||||||
|
break
|
||||||
|
case let .groupCallDiscarded(callId, _, _):
|
||||||
|
updatedGroupCallParticipants.append((
|
||||||
|
callId,
|
||||||
|
.call(isTerminated: true)
|
||||||
|
))
|
||||||
|
}
|
||||||
case let .UpdateLangPack(langCode, difference):
|
case let .UpdateLangPack(langCode, difference):
|
||||||
if let difference = difference {
|
if let difference = difference {
|
||||||
if langPackDifferences[langCode] == nil {
|
if langPackDifferences[langCode] == nil {
|
||||||
|
@ -148,8 +148,8 @@ public final class AccountStateManager {
|
|||||||
return self.threadReadStateUpdatesPipe.signal()
|
return self.threadReadStateUpdatesPipe.signal()
|
||||||
}
|
}
|
||||||
|
|
||||||
private let groupCallParticipantUpdatesPipe = ValuePipe<[(Int64, GroupCallParticipantsContext.StateUpdate)]>()
|
private let groupCallParticipantUpdatesPipe = ValuePipe<[(Int64, GroupCallParticipantsContext.Update)]>()
|
||||||
public var groupCallParticipantUpdates: Signal<[(Int64, GroupCallParticipantsContext.StateUpdate)], NoError> {
|
public var groupCallParticipantUpdates: Signal<[(Int64, GroupCallParticipantsContext.Update)], NoError> {
|
||||||
return self.groupCallParticipantUpdatesPipe.signal()
|
return self.groupCallParticipantUpdatesPipe.signal()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,7 +467,7 @@ private func binaryInsertionIndex(_ inputArr: [GroupCallParticipantsContext.Part
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class GroupCallParticipantsContext {
|
public final class GroupCallParticipantsContext {
|
||||||
public struct Participant: Equatable {
|
public struct Participant: Equatable, Comparable {
|
||||||
public struct MuteState: Equatable {
|
public struct MuteState: Equatable {
|
||||||
public var canUnmute: Bool
|
public var canUnmute: Bool
|
||||||
|
|
||||||
@ -500,6 +500,24 @@ public final class GroupCallParticipantsContext {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func <(lhs: Participant, rhs: Participant) -> Bool {
|
||||||
|
if let lhsActivityTimestamp = lhs.activityTimestamp, let rhsActivityTimestamp = rhs.activityTimestamp {
|
||||||
|
if lhsActivityTimestamp != rhsActivityTimestamp {
|
||||||
|
return lhsActivityTimestamp > rhsActivityTimestamp
|
||||||
|
}
|
||||||
|
} else if lhs.activityTimestamp != nil {
|
||||||
|
return true
|
||||||
|
} else if rhs.activityTimestamp != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if lhs.joinTimestamp != rhs.joinTimestamp {
|
||||||
|
return lhs.joinTimestamp > rhs.joinTimestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs.peer.id < rhs.peer.id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct State: Equatable {
|
public struct State: Equatable {
|
||||||
@ -542,20 +560,25 @@ public final class GroupCallParticipantsContext {
|
|||||||
var overlayState: OverlayState
|
var overlayState: OverlayState
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct StateUpdate {
|
public enum Update {
|
||||||
public struct ParticipantUpdate {
|
public struct StateUpdate {
|
||||||
public var peerId: PeerId
|
public struct ParticipantUpdate {
|
||||||
public var ssrc: UInt32
|
public var peerId: PeerId
|
||||||
public var joinTimestamp: Int32
|
public var ssrc: UInt32
|
||||||
public var activityTimestamp: Int32?
|
public var joinTimestamp: Int32
|
||||||
public var muteState: Participant.MuteState?
|
public var activityTimestamp: Int32?
|
||||||
public var isRemoved: Bool
|
public var muteState: Participant.MuteState?
|
||||||
|
public var isRemoved: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
public var participantUpdates: [ParticipantUpdate]
|
||||||
|
public var version: Int32
|
||||||
|
|
||||||
|
public var removePendingMuteStates: Set<PeerId>
|
||||||
}
|
}
|
||||||
|
|
||||||
public var participantUpdates: [ParticipantUpdate]
|
case state(update: StateUpdate)
|
||||||
public var version: Int32
|
case call(isTerminated: Bool)
|
||||||
|
|
||||||
public var removePendingMuteStates: Set<PeerId>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private let account: Account
|
private let account: Account
|
||||||
@ -597,7 +620,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
return self.numberOfActiveSpeakersPromise.get()
|
return self.numberOfActiveSpeakersPromise.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var updateQueue: [StateUpdate] = []
|
private var updateQueue: [Update.StateUpdate] = []
|
||||||
private var isProcessingUpdate: Bool = false
|
private var isProcessingUpdate: Bool = false
|
||||||
private let disposable = MetaDisposable()
|
private let disposable = MetaDisposable()
|
||||||
|
|
||||||
@ -616,7 +639,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var filteredUpdates: [StateUpdate] = []
|
var filteredUpdates: [Update] = []
|
||||||
for (callId, update) in updates {
|
for (callId, update) in updates {
|
||||||
if callId == id {
|
if callId == id {
|
||||||
filteredUpdates.append(update)
|
filteredUpdates.append(update)
|
||||||
@ -635,6 +658,53 @@ public final class GroupCallParticipantsContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.numberOfActiveSpeakersValue = activities.count
|
strongSelf.numberOfActiveSpeakersValue = activities.count
|
||||||
|
|
||||||
|
var updatedParticipants = strongSelf.stateValue.state.participants
|
||||||
|
var indexMap: [PeerId: Int] = [:]
|
||||||
|
for i in 0 ..< updatedParticipants.count {
|
||||||
|
indexMap[updatedParticipants[i].peer.id] = i
|
||||||
|
}
|
||||||
|
var updated = false
|
||||||
|
|
||||||
|
for (activityPeerId, activity) in activities {
|
||||||
|
if case let .speakingInGroupCall(timestamp) = activity {
|
||||||
|
if let index = indexMap[activityPeerId] {
|
||||||
|
if let activityTimestamp = updatedParticipants[index].activityTimestamp {
|
||||||
|
if activityTimestamp < timestamp {
|
||||||
|
updatedParticipants[index].activityTimestamp = timestamp
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updatedParticipants[index].activityTimestamp = timestamp
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if updated {
|
||||||
|
updatedParticipants.sort()
|
||||||
|
for i in 0 ..< updatedParticipants.count {
|
||||||
|
if updatedParticipants[i].peer.id == strongSelf.account.peerId {
|
||||||
|
let member = updatedParticipants[i]
|
||||||
|
updatedParticipants.remove(at: i)
|
||||||
|
updatedParticipants.insert(member, at: 0)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.stateValue = InternalState(
|
||||||
|
state: State(
|
||||||
|
participants: updatedParticipants,
|
||||||
|
nextParticipantsFetchOffset: strongSelf.stateValue.state.nextParticipantsFetchOffset,
|
||||||
|
adminIds: strongSelf.stateValue.state.adminIds,
|
||||||
|
isCreator: strongSelf.stateValue.state.isCreator,
|
||||||
|
totalCount: strongSelf.stateValue.state.totalCount,
|
||||||
|
version: strongSelf.stateValue.state.version
|
||||||
|
),
|
||||||
|
overlayState: strongSelf.stateValue.overlayState
|
||||||
|
)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -644,9 +714,18 @@ public final class GroupCallParticipantsContext {
|
|||||||
self.activitiesDisposable?.dispose()
|
self.activitiesDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func addUpdates(updates: [StateUpdate]) {
|
public func addUpdates(updates: [Update]) {
|
||||||
self.updateQueue.append(contentsOf: updates)
|
var stateUpdates: [Update.StateUpdate] = []
|
||||||
self.beginProcessingUpdatesIfNeeded()
|
for update in updates {
|
||||||
|
if case let .state(update) = update {
|
||||||
|
stateUpdates.append(update)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !stateUpdates.isEmpty {
|
||||||
|
self.updateQueue.append(contentsOf: stateUpdates)
|
||||||
|
self.beginProcessingUpdatesIfNeeded()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func beginProcessingUpdatesIfNeeded() {
|
private func beginProcessingUpdatesIfNeeded() {
|
||||||
@ -667,7 +746,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
self.beginProcessingUpdatesIfNeeded()
|
self.beginProcessingUpdatesIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func processUpdate(update: StateUpdate) {
|
private func processUpdate(update: Update.StateUpdate) {
|
||||||
if update.version < self.stateValue.state.version {
|
if update.version < self.stateValue.state.version {
|
||||||
for peerId in update.removePendingMuteStates {
|
for peerId in update.removePendingMuteStates {
|
||||||
self.stateValue.overlayState.pendingMuteStateChanges.removeValue(forKey: peerId)
|
self.stateValue.overlayState.pendingMuteStateChanges.removeValue(forKey: peerId)
|
||||||
@ -702,7 +781,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var updatedParticipants = Array(strongSelf.stateValue.state.participants.reversed())
|
var updatedParticipants = strongSelf.stateValue.state.participants
|
||||||
var updatedTotalCount = strongSelf.stateValue.state.totalCount
|
var updatedTotalCount = strongSelf.stateValue.state.totalCount
|
||||||
|
|
||||||
for participantUpdate in update.participantUpdates {
|
for participantUpdate in update.participantUpdates {
|
||||||
@ -718,20 +797,29 @@ public final class GroupCallParticipantsContext {
|
|||||||
assertionFailure()
|
assertionFailure()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
var previousActivityTimestamp: Int32?
|
||||||
if let index = updatedParticipants.firstIndex(where: { $0.peer.id == participantUpdate.peerId }) {
|
if let index = updatedParticipants.firstIndex(where: { $0.peer.id == participantUpdate.peerId }) {
|
||||||
|
previousActivityTimestamp = updatedParticipants[index].activityTimestamp
|
||||||
updatedParticipants.remove(at: index)
|
updatedParticipants.remove(at: index)
|
||||||
} else {
|
} else {
|
||||||
updatedTotalCount += 1
|
updatedTotalCount += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var activityTimestamp: Int32?
|
||||||
|
if let previousActivityTimestamp = previousActivityTimestamp, let updatedActivityTimestamp = participantUpdate.activityTimestamp {
|
||||||
|
activityTimestamp = max(updatedActivityTimestamp, previousActivityTimestamp)
|
||||||
|
} else {
|
||||||
|
activityTimestamp = participantUpdate.activityTimestamp ?? previousActivityTimestamp
|
||||||
|
}
|
||||||
|
|
||||||
let participant = Participant(
|
let participant = Participant(
|
||||||
peer: peer,
|
peer: peer,
|
||||||
ssrc: participantUpdate.ssrc,
|
ssrc: participantUpdate.ssrc,
|
||||||
joinTimestamp: participantUpdate.joinTimestamp,
|
joinTimestamp: participantUpdate.joinTimestamp,
|
||||||
|
activityTimestamp: activityTimestamp,
|
||||||
muteState: participantUpdate.muteState
|
muteState: participantUpdate.muteState
|
||||||
)
|
)
|
||||||
let index = binaryInsertionIndex(updatedParticipants, searchItem: participant.joinTimestamp)
|
updatedParticipants.append(participant)
|
||||||
updatedParticipants.insert(participant, at: index)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -744,9 +832,19 @@ public final class GroupCallParticipantsContext {
|
|||||||
let adminIds = strongSelf.stateValue.state.adminIds
|
let adminIds = strongSelf.stateValue.state.adminIds
|
||||||
let isCreator = strongSelf.stateValue.state.isCreator
|
let isCreator = strongSelf.stateValue.state.isCreator
|
||||||
|
|
||||||
|
updatedParticipants.sort()
|
||||||
|
for i in 0 ..< updatedParticipants.count {
|
||||||
|
if updatedParticipants[i].peer.id == strongSelf.account.peerId {
|
||||||
|
let member = updatedParticipants[i]
|
||||||
|
updatedParticipants.remove(at: i)
|
||||||
|
updatedParticipants.insert(member, at: 0)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.stateValue = InternalState(
|
strongSelf.stateValue = InternalState(
|
||||||
state: State(
|
state: State(
|
||||||
participants: Array(updatedParticipants.reversed()),
|
participants: updatedParticipants,
|
||||||
nextParticipantsFetchOffset: nextParticipantsFetchOffset,
|
nextParticipantsFetchOffset: nextParticipantsFetchOffset,
|
||||||
adminIds: adminIds,
|
adminIds: adminIds,
|
||||||
isCreator: isCreator,
|
isCreator: isCreator,
|
||||||
@ -782,6 +880,14 @@ public final class GroupCallParticipantsContext {
|
|||||||
self.stateValue.overlayState.pendingMuteStateChanges.removeValue(forKey: peerId)
|
self.stateValue.overlayState.pendingMuteStateChanges.removeValue(forKey: peerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for participant in self.stateValue.state.participants {
|
||||||
|
if participant.peer.id == peerId {
|
||||||
|
if participant.muteState == muteState {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
self.stateValue.overlayState.pendingMuteStateChanges[peerId] = OverlayState.MuteStateChange(
|
self.stateValue.overlayState.pendingMuteStateChanges[peerId] = OverlayState.MuteStateChange(
|
||||||
state: muteState,
|
state: muteState,
|
||||||
@ -800,7 +906,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
if let muteState = muteState, !muteState.canUnmute {
|
if let muteState = muteState, (!muteState.canUnmute || peerId == account.peerId) {
|
||||||
flags |= 1 << 0
|
flags |= 1 << 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -818,7 +924,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let updates = updates {
|
if let updates = updates {
|
||||||
var stateUpdates: [GroupCallParticipantsContext.StateUpdate] = []
|
var stateUpdates: [GroupCallParticipantsContext.Update] = []
|
||||||
|
|
||||||
loop: for update in updates.allUpdates {
|
loop: for update in updates.allUpdates {
|
||||||
switch update {
|
switch update {
|
||||||
@ -829,7 +935,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
continue loop
|
continue loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stateUpdates.append(GroupCallParticipantsContext.StateUpdate(participants: participants, version: version, removePendingMuteStates: [peerId]))
|
stateUpdates.append(.state(update: GroupCallParticipantsContext.Update.StateUpdate(participants: participants, version: version, removePendingMuteStates: [peerId])))
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -845,9 +951,9 @@ public final class GroupCallParticipantsContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension GroupCallParticipantsContext.StateUpdate {
|
extension GroupCallParticipantsContext.Update.StateUpdate {
|
||||||
init(participants: [Api.GroupCallParticipant], version: Int32, removePendingMuteStates: Set<PeerId> = Set()) {
|
init(participants: [Api.GroupCallParticipant], version: Int32, removePendingMuteStates: Set<PeerId> = Set()) {
|
||||||
var participantUpdates: [GroupCallParticipantsContext.StateUpdate.ParticipantUpdate] = []
|
var participantUpdates: [GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate] = []
|
||||||
for participant in participants {
|
for participant in participants {
|
||||||
switch participant {
|
switch participant {
|
||||||
case let .groupCallParticipant(flags, userId, date, activeDate, source):
|
case let .groupCallParticipant(flags, userId, date, activeDate, source):
|
||||||
@ -859,7 +965,7 @@ extension GroupCallParticipantsContext.StateUpdate {
|
|||||||
muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: canUnmute)
|
muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: canUnmute)
|
||||||
}
|
}
|
||||||
let isRemoved = (flags & (1 << 1)) != 0
|
let isRemoved = (flags & (1 << 1)) != 0
|
||||||
participantUpdates.append(GroupCallParticipantsContext.StateUpdate.ParticipantUpdate(
|
participantUpdates.append(GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate(
|
||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
ssrc: ssrc,
|
ssrc: ssrc,
|
||||||
joinTimestamp: date,
|
joinTimestamp: date,
|
||||||
|
@ -10,7 +10,7 @@ public enum PeerInputActivity: Comparable {
|
|||||||
case playingGame
|
case playingGame
|
||||||
case recordingInstantVideo
|
case recordingInstantVideo
|
||||||
case uploadingInstantVideo(progress: Int32)
|
case uploadingInstantVideo(progress: Int32)
|
||||||
case speakingInGroupCall
|
case speakingInGroupCall(timestamp: Int32)
|
||||||
|
|
||||||
public var key: Int32 {
|
public var key: Int32 {
|
||||||
switch self {
|
switch self {
|
||||||
@ -41,7 +41,7 @@ public enum PeerInputActivity: Comparable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension PeerInputActivity {
|
extension PeerInputActivity {
|
||||||
init?(apiType: Api.SendMessageAction) {
|
init?(apiType: Api.SendMessageAction, timestamp: Int32) {
|
||||||
switch apiType {
|
switch apiType {
|
||||||
case .sendMessageCancelAction, .sendMessageChooseContactAction, .sendMessageGeoLocationAction, .sendMessageRecordVideoAction:
|
case .sendMessageCancelAction, .sendMessageChooseContactAction, .sendMessageGeoLocationAction, .sendMessageRecordVideoAction:
|
||||||
return nil
|
return nil
|
||||||
@ -62,7 +62,7 @@ extension PeerInputActivity {
|
|||||||
case let .sendMessageUploadRoundAction(progress):
|
case let .sendMessageUploadRoundAction(progress):
|
||||||
self = .uploadingInstantVideo(progress: progress)
|
self = .uploadingInstantVideo(progress: progress)
|
||||||
case .speakingInGroupCallAction:
|
case .speakingInGroupCallAction:
|
||||||
self = .speakingInGroupCall
|
self = .speakingInGroupCall(timestamp: timestamp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -408,9 +408,9 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||||||
//TODO:localize
|
//TODO:localize
|
||||||
let titleString: String
|
let titleString: String
|
||||||
if let duration = duration {
|
if let duration = duration {
|
||||||
titleString = "Group Call \(duration)s"
|
titleString = "Voice chat ended (\(duration)s)"
|
||||||
} else {
|
} else {
|
||||||
titleString = "Group Call"
|
titleString = "Voice chat started"
|
||||||
}
|
}
|
||||||
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
|
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
|
||||||
case let .customText(text, entities):
|
case let .customText(text, entities):
|
||||||
|
@ -5986,7 +5986,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let callResult = strongSelf.context.sharedContext.callManager?.requestOrJoinGroupCall(context: strongSelf.context, peerId: peer.id, initialCall: activeCall, endCurrentIfAny: false, sourcePanel: nil)
|
let callResult = strongSelf.context.sharedContext.callManager?.joinGroupCall(context: strongSelf.context, peerId: peer.id, initialCall: activeCall, endCurrentIfAny: false, sourcePanel: nil)
|
||||||
if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult {
|
if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult {
|
||||||
if currentPeerId == peer.id {
|
if currentPeerId == peer.id {
|
||||||
strongSelf.context.sharedContext.navigateToCurrentCall(sourcePanel: nil)
|
strongSelf.context.sharedContext.navigateToCurrentCall(sourcePanel: nil)
|
||||||
@ -5999,7 +5999,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
if let strongSelf = self, let current = current {
|
if let strongSelf = self, let current = current {
|
||||||
strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
|
strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let _ = strongSelf.context.sharedContext.callManager?.requestOrJoinGroupCall(context: strongSelf.context, peerId: peer.id, initialCall: activeCall, endCurrentIfAny: true, sourcePanel: nil)
|
let _ = strongSelf.context.sharedContext.callManager?.joinGroupCall(context: strongSelf.context, peerId: peer.id, initialCall: activeCall, endCurrentIfAny: true, sourcePanel: nil)
|
||||||
}
|
}
|
||||||
})]), in: .window(.root))
|
})]), in: .window(.root))
|
||||||
} else {
|
} else {
|
||||||
|
@ -949,7 +949,6 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro
|
|||||||
displayLeave = false
|
displayLeave = false
|
||||||
if channel.flags.contains(.isCreator) || channel.hasPermission(.inviteMembers) {
|
if channel.flags.contains(.isCreator) || channel.hasPermission(.inviteMembers) {
|
||||||
result.append(.addMember)
|
result.append(.addMember)
|
||||||
result.append(.call)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch channel.participationStatus {
|
switch channel.participationStatus {
|
||||||
|
@ -2966,6 +2966,16 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let channel = peer as? TelegramChannel {
|
} else if let channel = peer as? TelegramChannel {
|
||||||
|
if case .group = channel.info, !channel.flags.contains(.hasVoiceChat) {
|
||||||
|
if channel.flags.contains(.isCreator) || channel.hasPermission(.manageCalls) {
|
||||||
|
//TODO:localize
|
||||||
|
items.append(ActionSheetButtonItem(title: "Create Voice Chat", color: .accent, action: { [weak self] in
|
||||||
|
dismissAction()
|
||||||
|
self?.requestCall(isVideo: false)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let cachedData = self.data?.cachedData as? CachedChannelData, cachedData.flags.contains(.canViewStats) {
|
if let cachedData = self.data?.cachedData as? CachedChannelData, cachedData.flags.contains(.canViewStats) {
|
||||||
items.append(ActionSheetButtonItem(title: presentationData.strings.ChannelInfo_Stats, color: .accent, action: { [weak self] in
|
items.append(ActionSheetButtonItem(title: presentationData.strings.ChannelInfo_Stats, color: .accent, action: { [weak self] in
|
||||||
dismissAction()
|
dismissAction()
|
||||||
@ -3169,7 +3179,41 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
guard let cachedChannelData = self.data?.cachedData as? CachedChannelData else {
|
guard let cachedChannelData = self.data?.cachedData as? CachedChannelData else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = self.context.sharedContext.callManager?.requestOrJoinGroupCall(context: self.context, peerId: peer.id, initialCall: cachedChannelData.activeCall, endCurrentIfAny: false, sourcePanel: nil)
|
|
||||||
|
if let activeCall = cachedChannelData.activeCall {
|
||||||
|
let _ = self.context.sharedContext.callManager?.joinGroupCall(context: self.context, peerId: peer.id, initialCall: activeCall, endCurrentIfAny: false, sourcePanel: nil)
|
||||||
|
} else {
|
||||||
|
var dismissStatus: (() -> Void)?
|
||||||
|
let statusController = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: {
|
||||||
|
dismissStatus?()
|
||||||
|
}))
|
||||||
|
dismissStatus = { [weak self, weak statusController] in
|
||||||
|
self?.activeActionDisposable.set(nil)
|
||||||
|
statusController?.dismiss()
|
||||||
|
}
|
||||||
|
self.controller?.present(statusController, in: .window(.root))
|
||||||
|
self.activeActionDisposable.set((createGroupCall(account: self.context.account, peerId: peer.id)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] info in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let _ = strongSelf.context.sharedContext.callManager?.joinGroupCall(context: strongSelf.context, peerId: peer.id, initialCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash), endCurrentIfAny: false, sourcePanel: nil)
|
||||||
|
}, error: { [weak self] _ in
|
||||||
|
dismissStatus?()
|
||||||
|
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel)
|
||||||
|
}, completed: { [weak self] in
|
||||||
|
dismissStatus?()
|
||||||
|
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 353184e3e5c5e560a59cb836ca8de496c0cc95bb
|
Subproject commit 7f2022780754c0b3a23f8ce8a940042101120077
|
Loading…
x
Reference in New Issue
Block a user