mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
f0fd82f3c2
@ -1515,7 +1515,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
interaction.dismissInput()
|
interaction.dismissInput()
|
||||||
}, present: { c, a in
|
}, present: { c, a in
|
||||||
interaction.present(c, a)
|
interaction.present(c, a)
|
||||||
}, transitionNode: { [weak self] messageId, media in
|
}, transitionNode: { messageId, media in
|
||||||
var transitionNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
var transitionNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.listNode.forEachItemNode { itemNode in
|
strongSelf.listNode.forEachItemNode { itemNode in
|
||||||
@ -1527,7 +1527,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return transitionNode
|
return transitionNode
|
||||||
}, addToTransitionSurface: { [weak self] view in
|
}, addToTransitionSurface: { view in
|
||||||
self?.addToTransitionSurface(view: view)
|
self?.addToTransitionSurface(view: view)
|
||||||
}, openUrl: { url in
|
}, openUrl: { url in
|
||||||
interaction.openUrl(url)
|
interaction.openUrl(url)
|
||||||
@ -1626,7 +1626,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
let firstTime = previousEntries == nil
|
let firstTime = previousEntries == nil
|
||||||
var transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: newEntries, displayingResults: entriesAndFlags?.0 != nil, isEmpty: !isSearching && (entriesAndFlags?.0.isEmpty ?? false), isLoading: isSearching, animated: animated, context: context, presentationData: strongSelf.presentationData, enableHeaders: true, filter: peersFilter, key: strongSelf.key, tagMask: tagMask, interaction: chatListInteraction, listInteraction: listInteraction, peerContextAction: { message, node, rect, gesture in
|
var transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: newEntries, displayingResults: entriesAndFlags?.0 != nil, isEmpty: !isSearching && (entriesAndFlags?.0.isEmpty ?? false), isLoading: isSearching, animated: animated, context: context, presentationData: strongSelf.presentationData, enableHeaders: true, filter: peersFilter, key: strongSelf.key, tagMask: tagMask, interaction: chatListInteraction, listInteraction: listInteraction, peerContextAction: { message, node, rect, gesture in
|
||||||
interaction.peerContextAction?(message, node, rect, gesture)
|
interaction.peerContextAction?(message, node, rect, gesture)
|
||||||
}, toggleExpandLocalResults: { [weak self] in
|
}, toggleExpandLocalResults: {
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1635,7 +1635,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
state.expandLocalSearch = !state.expandLocalSearch
|
state.expandLocalSearch = !state.expandLocalSearch
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}, toggleExpandGlobalResults: { [weak self] in
|
}, toggleExpandGlobalResults: {
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1959,6 +1959,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
self.recentDisposable.dispose()
|
self.recentDisposable.dispose()
|
||||||
self.updatedRecentPeersDisposable.dispose()
|
self.updatedRecentPeersDisposable.dispose()
|
||||||
self.deletedMessagesDisposable?.dispose()
|
self.deletedMessagesDisposable?.dispose()
|
||||||
|
if self.key == .downloads {
|
||||||
|
print("downloads")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func didLoad() {
|
override func didLoad() {
|
||||||
|
18
submodules/Components/ActivityIndicatorComponent/BUILD
Normal file
18
submodules/Components/ActivityIndicatorComponent/BUILD
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||||
|
|
||||||
|
swift_library(
|
||||||
|
name = "ActivityIndicatorComponent",
|
||||||
|
module_name = "ActivityIndicatorComponent",
|
||||||
|
srcs = glob([
|
||||||
|
"Sources/**/*.swift",
|
||||||
|
]),
|
||||||
|
copts = [
|
||||||
|
"-warnings-as-errors",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//submodules/ComponentFlow:ComponentFlow",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
@ -0,0 +1,39 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import ComponentFlow
|
||||||
|
|
||||||
|
public final class ActivityIndicatorComponent: Component {
|
||||||
|
public init(
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: ActivityIndicatorComponent, rhs: ActivityIndicatorComponent) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class View: UIActivityIndicatorView {
|
||||||
|
public init() {
|
||||||
|
super.init(style: .whiteLarge)
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(component: ActivityIndicatorComponent, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||||
|
if !self.isAnimating {
|
||||||
|
self.startAnimating()
|
||||||
|
}
|
||||||
|
|
||||||
|
return CGSize(width: 22.0, height: 22.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeView() -> View {
|
||||||
|
return View()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||||
|
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||||
|
}
|
||||||
|
}
|
@ -97,6 +97,7 @@ swift_library(
|
|||||||
"//third-party/libyuv:LibYuvBinding",
|
"//third-party/libyuv:LibYuvBinding",
|
||||||
"//submodules/ComponentFlow:ComponentFlow",
|
"//submodules/ComponentFlow:ComponentFlow",
|
||||||
"//submodules/Components/LottieAnimationComponent:LottieAnimationComponent",
|
"//submodules/Components/LottieAnimationComponent:LottieAnimationComponent",
|
||||||
|
"//submodules/Components/ActivityIndicatorComponent:ActivityIndicatorComponent",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -489,8 +489,12 @@ public final class MediaStreamComponent: CombinedComponent {
|
|||||||
private(set) var displayUI: Bool = true
|
private(set) var displayUI: Bool = true
|
||||||
var dismissOffset: CGFloat = 0.0
|
var dismissOffset: CGFloat = 0.0
|
||||||
|
|
||||||
|
var storedIsLandscape: Bool?
|
||||||
|
|
||||||
let isPictureInPictureSupported: Bool
|
let isPictureInPictureSupported: Bool
|
||||||
|
|
||||||
|
private var scheduledDismissUITimer: SwiftSignalKit.Timer?
|
||||||
|
|
||||||
init(call: PresentationGroupCallImpl) {
|
init(call: PresentationGroupCallImpl) {
|
||||||
self.call = call
|
self.call = call
|
||||||
|
|
||||||
@ -551,6 +555,26 @@ public final class MediaStreamComponent: CombinedComponent {
|
|||||||
self.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .easeInOut)))
|
self.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .easeInOut)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cancelScheduledDismissUI() {
|
||||||
|
self.scheduledDismissUITimer?.invalidate()
|
||||||
|
self.scheduledDismissUITimer = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func scheduleDismissUI() {
|
||||||
|
if self.scheduledDismissUITimer == nil {
|
||||||
|
self.scheduledDismissUITimer = SwiftSignalKit.Timer(timeout: 3.0, repeat: false, completion: { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.scheduledDismissUITimer = nil
|
||||||
|
if strongSelf.displayUI {
|
||||||
|
strongSelf.toggleDisplayUI()
|
||||||
|
}
|
||||||
|
}, queue: .mainQueue())
|
||||||
|
self.scheduledDismissUITimer?.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func updateDismissOffset(value: CGFloat, interactive: Bool) {
|
func updateDismissOffset(value: CGFloat, interactive: Bool) {
|
||||||
self.dismissOffset = value
|
self.dismissOffset = value
|
||||||
if interactive {
|
if interactive {
|
||||||
@ -575,7 +599,8 @@ public final class MediaStreamComponent: CombinedComponent {
|
|||||||
|
|
||||||
return { context in
|
return { context in
|
||||||
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
|
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
|
||||||
if !environment.isVisible {
|
if environment.isVisible {
|
||||||
|
} else {
|
||||||
context.state.dismissOffset = 0.0
|
context.state.dismissOffset = 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,29 +613,28 @@ public final class MediaStreamComponent: CombinedComponent {
|
|||||||
let call = context.component.call
|
let call = context.component.call
|
||||||
let controller = environment.controller
|
let controller = environment.controller
|
||||||
|
|
||||||
let video = Condition(context.state.hasVideo) {
|
let video = video.update(
|
||||||
return video.update(
|
component: MediaStreamVideoComponent(
|
||||||
component: MediaStreamVideoComponent(
|
call: context.component.call,
|
||||||
call: context.component.call,
|
hasVideo: context.state.hasVideo,
|
||||||
activatePictureInPicture: activatePictureInPicture,
|
activatePictureInPicture: activatePictureInPicture,
|
||||||
bringBackControllerForPictureInPictureDeactivation: { [weak call] completed in
|
bringBackControllerForPictureInPictureDeactivation: { [weak call] completed in
|
||||||
guard let call = call else {
|
guard let call = call else {
|
||||||
completed()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
call.accountContext.sharedContext.mainWindow?.inCallNavigate?()
|
|
||||||
|
|
||||||
completed()
|
completed()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
),
|
|
||||||
availableSize: context.availableSize,
|
call.accountContext.sharedContext.mainWindow?.inCallNavigate?()
|
||||||
transition: context.transition
|
|
||||||
)
|
completed()
|
||||||
}
|
}
|
||||||
|
),
|
||||||
|
availableSize: context.availableSize,
|
||||||
|
transition: context.transition
|
||||||
|
)
|
||||||
|
|
||||||
var navigationRightItems: [AnyComponentWithIdentity<Empty>] = []
|
var navigationRightItems: [AnyComponentWithIdentity<Empty>] = []
|
||||||
if context.state.isPictureInPictureSupported, let _ = video {
|
if context.state.isPictureInPictureSupported, context.state.hasVideo {
|
||||||
navigationRightItems.append(AnyComponentWithIdentity(id: "pip", component: AnyComponent(Button(
|
navigationRightItems.append(AnyComponentWithIdentity(id: "pip", component: AnyComponent(Button(
|
||||||
content: AnyComponent(BundleIconComponent(
|
content: AnyComponent(BundleIconComponent(
|
||||||
name: "Media Gallery/PictureInPictureButton",
|
name: "Media Gallery/PictureInPictureButton",
|
||||||
@ -663,7 +687,7 @@ public final class MediaStreamComponent: CombinedComponent {
|
|||||||
topInset: environment.statusBarHeight,
|
topInset: environment.statusBarHeight,
|
||||||
sideInset: environment.safeInsets.left,
|
sideInset: environment.safeInsets.left,
|
||||||
leftItem: AnyComponent(Button(
|
leftItem: AnyComponent(Button(
|
||||||
content: AnyComponent(NavigationBackButtonComponent(text: environment.strings.Common_Back, color: .white)),
|
content: AnyComponent(NavigationBackButtonComponent(text: environment.strings.Common_Close, color: .white)),
|
||||||
action: { [weak call] in
|
action: { [weak call] in
|
||||||
let _ = call?.leave(terminateIfPossible: false)
|
let _ = call?.leave(terminateIfPossible: false)
|
||||||
})
|
})
|
||||||
@ -676,6 +700,14 @@ public final class MediaStreamComponent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
|
|
||||||
let isLandscape = context.availableSize.width > context.availableSize.height
|
let isLandscape = context.availableSize.width > context.availableSize.height
|
||||||
|
if context.state.storedIsLandscape != isLandscape {
|
||||||
|
context.state.storedIsLandscape = isLandscape
|
||||||
|
if isLandscape {
|
||||||
|
context.state.scheduleDismissUI()
|
||||||
|
} else {
|
||||||
|
context.state.cancelScheduledDismissUI()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var infoItem: AnyComponent<Empty>?
|
var infoItem: AnyComponent<Empty>?
|
||||||
if let originInfo = context.state.originInfo {
|
if let originInfo = context.state.originInfo {
|
||||||
@ -754,11 +786,9 @@ public final class MediaStreamComponent: CombinedComponent {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
if let video = video {
|
context.add(video
|
||||||
context.add(video
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0 + context.state.dismissOffset))
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0 + context.state.dismissOffset))
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
context.add(navigationBar
|
context.add(navigationBar
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: navigationBar.size.height / 2.0))
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: navigationBar.size.height / 2.0))
|
||||||
@ -776,6 +806,7 @@ public final class MediaStreamComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class MediaStreamComponentController: ViewControllerComponentContainer, VoiceChatController {
|
public final class MediaStreamComponentController: ViewControllerComponentContainer, VoiceChatController {
|
||||||
|
private let context: AccountContext
|
||||||
public let call: PresentationGroupCall
|
public let call: PresentationGroupCall
|
||||||
public private(set) var currentOverlayController: VoiceChatOverlayController? = nil
|
public private(set) var currentOverlayController: VoiceChatOverlayController? = nil
|
||||||
public var parentNavigationController: NavigationController?
|
public var parentNavigationController: NavigationController?
|
||||||
@ -785,13 +816,19 @@ public final class MediaStreamComponentController: ViewControllerComponentContai
|
|||||||
|
|
||||||
private var initialOrientation: UIInterfaceOrientation?
|
private var initialOrientation: UIInterfaceOrientation?
|
||||||
|
|
||||||
|
private let inviteLinksPromise = Promise<GroupCallInviteLinks?>(nil)
|
||||||
|
|
||||||
public init(call: PresentationGroupCall) {
|
public init(call: PresentationGroupCall) {
|
||||||
|
self.context = call.accountContext
|
||||||
self.call = call
|
self.call = call
|
||||||
|
|
||||||
super.init(context: call.accountContext, component: MediaStreamComponent(call: call as! PresentationGroupCallImpl))
|
super.init(context: call.accountContext, component: MediaStreamComponent(call: call as! PresentationGroupCallImpl))
|
||||||
|
|
||||||
self.statusBar.statusBarStyle = .White
|
self.statusBar.statusBarStyle = .White
|
||||||
self.view.disablesInteractiveModalDismiss = true
|
self.view.disablesInteractiveModalDismiss = true
|
||||||
|
|
||||||
|
self.inviteLinksPromise.set(.single(nil)
|
||||||
|
|> then(call.inviteLinks))
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init(coder aDecoder: NSCoder) {
|
required public init(coder aDecoder: NSCoder) {
|
||||||
@ -811,6 +848,10 @@ public final class MediaStreamComponentController: ViewControllerComponentContai
|
|||||||
self.onViewDidAppear?()
|
self.onViewDidAppear?()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let view = self.node.hostView.findTaggedView(tag: MediaStreamVideoComponent.View.Tag()) as? MediaStreamVideoComponent.View {
|
||||||
|
view.expandFromPictureInPicture()
|
||||||
|
}
|
||||||
|
|
||||||
self.view.layer.allowsGroupOpacity = true
|
self.view.layer.allowsGroupOpacity = true
|
||||||
self.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, completion: { [weak self] _ in
|
self.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, completion: { [weak self] _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -858,6 +899,41 @@ public final class MediaStreamComponentController: ViewControllerComponentContai
|
|||||||
}
|
}
|
||||||
|
|
||||||
func presentShare() {
|
func presentShare() {
|
||||||
|
let _ = (self.inviteLinksPromise.get()
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] inviteLinks in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let callPeerId = strongSelf.call.peerId
|
||||||
|
let _ = (strongSelf.call.accountContext.account.postbox.transaction { transaction -> GroupCallInviteLinks? in
|
||||||
|
if let inviteLinks = inviteLinks {
|
||||||
|
return inviteLinks
|
||||||
|
} else if let peer = transaction.getPeer(callPeerId), let addressName = peer.addressName, !addressName.isEmpty {
|
||||||
|
return GroupCallInviteLinks(listenerLink: "https://t.me/\(addressName)?voicechat", speakerLink: nil)
|
||||||
|
} else if let cachedData = transaction.getPeerCachedData(peerId: callPeerId) {
|
||||||
|
if let cachedData = cachedData as? CachedChannelData, let link = cachedData.exportedInvitation?.link {
|
||||||
|
return GroupCallInviteLinks(listenerLink: link, speakerLink: nil)
|
||||||
|
} else if let cachedData = cachedData as? CachedGroupData, let link = cachedData.exportedInvitation?.link {
|
||||||
|
return GroupCallInviteLinks(listenerLink: link, speakerLink: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|> deliverOnMainQueue).start(next: { links in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let links = links {
|
||||||
|
strongSelf.presentShare(links: links)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func presentShare(links inviteLinks: GroupCallInviteLinks) {
|
||||||
let formatSendTitle: (String) -> String = { string in
|
let formatSendTitle: (String) -> String = { string in
|
||||||
var string = string
|
var string = string
|
||||||
if string.contains("[") && string.contains("]") {
|
if string.contains("[") && string.contains("]") {
|
||||||
@ -869,34 +945,29 @@ public final class MediaStreamComponentController: ViewControllerComponentContai
|
|||||||
}
|
}
|
||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
|
let _ = formatSendTitle
|
||||||
|
|
||||||
let _ = (combineLatest(self.call.accountContext.account.postbox.loadedPeerWithId(self.call.peerId), self.call.state |> take(1))
|
let _ = (combineLatest(queue: .mainQueue(), self.context.account.postbox.loadedPeerWithId(self.call.peerId), self.call.state |> take(1))
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] peer, callState in
|
|> deliverOnMainQueue).start(next: { [weak self] peer, callState in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
var maybeInviteLinks: GroupCallInviteLinks? = nil
|
var inviteLinks = inviteLinks
|
||||||
|
|
||||||
if let peer = peer as? TelegramChannel, let addressName = peer.addressName {
|
if let peer = peer as? TelegramChannel, case .group = peer.info, !peer.flags.contains(.isGigagroup), !(peer.addressName ?? "").isEmpty, let defaultParticipantMuteState = callState.defaultParticipantMuteState {
|
||||||
maybeInviteLinks = GroupCallInviteLinks(listenerLink: "https://t.me/\(addressName)", speakerLink: nil)
|
let isMuted = defaultParticipantMuteState == .muted
|
||||||
|
|
||||||
|
if !isMuted {
|
||||||
|
inviteLinks = GroupCallInviteLinks(listenerLink: inviteLinks.listenerLink, speakerLink: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let inviteLinks = maybeInviteLinks else {
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let presentationData = strongSelf.call.accountContext.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
|
|
||||||
var segmentedValues: [ShareControllerSegmentedValue]?
|
var segmentedValues: [ShareControllerSegmentedValue]?
|
||||||
if let speakerLink = inviteLinks.speakerLink {
|
segmentedValues = nil
|
||||||
segmentedValues = [ShareControllerSegmentedValue(title: presentationData.strings.VoiceChat_InviteLink_Speaker, subject: .url(speakerLink), actionTitle: presentationData.strings.VoiceChat_InviteLink_CopySpeakerLink, formatSendTitle: { count in
|
let shareController = ShareController(context: strongSelf.context, subject: .url(inviteLinks.listenerLink), segmentedValues: segmentedValues, forceTheme: defaultDarkPresentationTheme, forcedActionTitle: presentationData.strings.VoiceChat_CopyInviteLink)
|
||||||
return formatSendTitle(presentationData.strings.VoiceChat_InviteLink_InviteSpeakers(Int32(count)))
|
|
||||||
}), ShareControllerSegmentedValue(title: presentationData.strings.VoiceChat_InviteLink_Listener, subject: .url(inviteLinks.listenerLink), actionTitle: presentationData.strings.VoiceChat_InviteLink_CopyListenerLink, formatSendTitle: { count in
|
|
||||||
return formatSendTitle(presentationData.strings.VoiceChat_InviteLink_InviteListeners(Int32(count)))
|
|
||||||
})]
|
|
||||||
}
|
|
||||||
let shareController = ShareController(context: strongSelf.call.accountContext, subject: .url(inviteLinks.listenerLink), segmentedValues: segmentedValues, forceTheme: defaultDarkColorPresentationTheme, forcedActionTitle: presentationData.strings.VoiceChat_CopyInviteLink)
|
|
||||||
shareController.completed = { [weak self] peerIds in
|
shareController.completed = { [weak self] peerIds in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let _ = (strongSelf.call.accountContext.account.postbox.transaction { transaction -> [Peer] in
|
let _ = (strongSelf.context.account.postbox.transaction { transaction -> [Peer] in
|
||||||
var peers: [Peer] = []
|
var peers: [Peer] = []
|
||||||
for peerId in peerIds {
|
for peerId in peerIds {
|
||||||
if let peer = transaction.getPeer(peerId) {
|
if let peer = transaction.getPeer(peerId) {
|
||||||
@ -904,19 +975,19 @@ public final class MediaStreamComponentController: ViewControllerComponentContai
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return peers
|
return peers
|
||||||
} |> deliverOnMainQueue).start(next: { peers in
|
} |> deliverOnMainQueue).start(next: { [weak self] peers in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let presentationData = strongSelf.call.accountContext.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
let text: String
|
let text: String
|
||||||
var isSavedMessages = false
|
var isSavedMessages = false
|
||||||
if peers.count == 1, let peer = peers.first {
|
if peers.count == 1, let peer = peers.first {
|
||||||
isSavedMessages = peer.id == strongSelf.call.accountContext.account.peerId
|
isSavedMessages = peer.id == strongSelf.context.account.peerId
|
||||||
let peerName = peer.id == strongSelf.call.accountContext.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||||
text = presentationData.strings.VoiceChat_ForwardTooltip_Chat(peerName).string
|
text = presentationData.strings.VoiceChat_ForwardTooltip_Chat(peerName).string
|
||||||
} else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last {
|
} else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last {
|
||||||
let firstPeerName = firstPeer.id == strongSelf.call.accountContext.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(firstPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(firstPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||||
let secondPeerName = secondPeer.id == strongSelf.call.accountContext.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(secondPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : EnginePeer(secondPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||||
text = presentationData.strings.VoiceChat_ForwardTooltip_TwoChats(firstPeerName, secondPeerName).string
|
text = presentationData.strings.VoiceChat_ForwardTooltip_TwoChats(firstPeerName, secondPeerName).string
|
||||||
} else if let peer = peers.first {
|
} else if let peer = peers.first {
|
||||||
let peerName = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
let peerName = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||||
@ -932,7 +1003,7 @@ public final class MediaStreamComponentController: ViewControllerComponentContai
|
|||||||
}
|
}
|
||||||
shareController.actionCompleted = {
|
shareController.actionCompleted = {
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let presentationData = strongSelf.call.accountContext.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.VoiceChat_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
|
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.VoiceChat_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
import ComponentFlow
|
import ComponentFlow
|
||||||
|
import ActivityIndicatorComponent
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import AVKit
|
import AVKit
|
||||||
|
|
||||||
final class MediaStreamVideoComponent: Component {
|
final class MediaStreamVideoComponent: Component {
|
||||||
let call: PresentationGroupCallImpl
|
let call: PresentationGroupCallImpl
|
||||||
|
let hasVideo: Bool
|
||||||
let activatePictureInPicture: ActionSlot<Action<Void>>
|
let activatePictureInPicture: ActionSlot<Action<Void>>
|
||||||
let bringBackControllerForPictureInPictureDeactivation: (@escaping () -> Void) -> Void
|
let bringBackControllerForPictureInPictureDeactivation: (@escaping () -> Void) -> Void
|
||||||
|
|
||||||
init(call: PresentationGroupCallImpl, activatePictureInPicture: ActionSlot<Action<Void>>, bringBackControllerForPictureInPictureDeactivation: @escaping (@escaping () -> Void) -> Void) {
|
init(call: PresentationGroupCallImpl, hasVideo: Bool, activatePictureInPicture: ActionSlot<Action<Void>>, bringBackControllerForPictureInPictureDeactivation: @escaping (@escaping () -> Void) -> Void) {
|
||||||
self.call = call
|
self.call = call
|
||||||
|
self.hasVideo = hasVideo
|
||||||
self.activatePictureInPicture = activatePictureInPicture
|
self.activatePictureInPicture = activatePictureInPicture
|
||||||
self.bringBackControllerForPictureInPictureDeactivation = bringBackControllerForPictureInPictureDeactivation
|
self.bringBackControllerForPictureInPictureDeactivation = bringBackControllerForPictureInPictureDeactivation
|
||||||
}
|
}
|
||||||
@ -19,6 +22,9 @@ final class MediaStreamVideoComponent: Component {
|
|||||||
if lhs.call !== rhs.call {
|
if lhs.call !== rhs.call {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.hasVideo != rhs.hasVideo {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -33,19 +39,24 @@ final class MediaStreamVideoComponent: Component {
|
|||||||
return State()
|
return State()
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class View: UIView, AVPictureInPictureControllerDelegate, AVPictureInPictureSampleBufferPlaybackDelegate {
|
public final class View: UIView, AVPictureInPictureControllerDelegate, AVPictureInPictureSampleBufferPlaybackDelegate, ComponentTaggedView {
|
||||||
|
public final class Tag {
|
||||||
|
}
|
||||||
|
|
||||||
private let videoRenderingContext = VideoRenderingContext()
|
private let videoRenderingContext = VideoRenderingContext()
|
||||||
private var videoView: VideoRenderingView?
|
private var videoView: VideoRenderingView?
|
||||||
private let blurTintView: UIView
|
private let blurTintView: UIView
|
||||||
private var videoBlurView: VideoRenderingView?
|
private var videoBlurView: VideoRenderingView?
|
||||||
|
private var activityIndicatorView: ComponentHostView<Empty>?
|
||||||
|
|
||||||
private var pictureInPictureController: AVPictureInPictureController?
|
private var pictureInPictureController: AVPictureInPictureController?
|
||||||
|
|
||||||
private var component: MediaStreamVideoComponent?
|
private var component: MediaStreamVideoComponent?
|
||||||
|
private var hadVideo: Bool = false
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
self.blurTintView = UIView()
|
self.blurTintView = UIView()
|
||||||
self.blurTintView.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
|
self.blurTintView.backgroundColor = UIColor(white: 0.0, alpha: 0.55)
|
||||||
|
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
@ -59,8 +70,19 @@ final class MediaStreamVideoComponent: Component {
|
|||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func matches(tag: Any) -> Bool {
|
||||||
|
if let _ = tag as? Tag {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandFromPictureInPicture() {
|
||||||
|
self.pictureInPictureController?.stopPictureInPicture()
|
||||||
|
}
|
||||||
|
|
||||||
func update(component: MediaStreamVideoComponent, availableSize: CGSize, state: State, transition: Transition) -> CGSize {
|
func update(component: MediaStreamVideoComponent, availableSize: CGSize, state: State, transition: Transition) -> CGSize {
|
||||||
if self.videoView == nil {
|
if component.hasVideo, self.videoView == nil {
|
||||||
if let input = component.call.video(endpointId: "unified") {
|
if let input = component.call.video(endpointId: "unified") {
|
||||||
if let videoBlurView = self.videoRenderingContext.makeView(input: input, blur: true) {
|
if let videoBlurView = self.videoRenderingContext.makeView(input: input, blur: true) {
|
||||||
self.videoBlurView = videoBlurView
|
self.videoBlurView = videoBlurView
|
||||||
@ -73,17 +95,26 @@ final class MediaStreamVideoComponent: Component {
|
|||||||
|
|
||||||
if #available(iOSApplicationExtension 15.0, iOS 15.0, *), AVPictureInPictureController.isPictureInPictureSupported(), let sampleBufferVideoView = videoView as? SampleBufferVideoRenderingView {
|
if #available(iOSApplicationExtension 15.0, iOS 15.0, *), AVPictureInPictureController.isPictureInPictureSupported(), let sampleBufferVideoView = videoView as? SampleBufferVideoRenderingView {
|
||||||
let pictureInPictureController = AVPictureInPictureController(contentSource: AVPictureInPictureController.ContentSource(sampleBufferDisplayLayer: sampleBufferVideoView.sampleBufferLayer, playbackDelegate: self))
|
let pictureInPictureController = AVPictureInPictureController(contentSource: AVPictureInPictureController.ContentSource(sampleBufferDisplayLayer: sampleBufferVideoView.sampleBufferLayer, playbackDelegate: self))
|
||||||
self.pictureInPictureController = pictureInPictureController
|
|
||||||
|
|
||||||
|
pictureInPictureController.delegate = self
|
||||||
pictureInPictureController.canStartPictureInPictureAutomaticallyFromInline = true
|
pictureInPictureController.canStartPictureInPictureAutomaticallyFromInline = true
|
||||||
pictureInPictureController.requiresLinearPlayback = true
|
pictureInPictureController.requiresLinearPlayback = true
|
||||||
pictureInPictureController.delegate = self
|
|
||||||
|
self.pictureInPictureController = pictureInPictureController
|
||||||
}
|
}
|
||||||
|
|
||||||
videoView.setOnOrientationUpdated { [weak state] _, _ in
|
videoView.setOnOrientationUpdated { [weak state] _, _ in
|
||||||
state?.updated(transition: .immediate)
|
state?.updated(transition: .immediate)
|
||||||
}
|
}
|
||||||
videoView.setOnFirstFrameReceived { [weak state] _ in
|
videoView.setOnFirstFrameReceived { [weak self, weak state] _ in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.hadVideo = true
|
||||||
|
strongSelf.activityIndicatorView?.removeFromSuperview()
|
||||||
|
strongSelf.activityIndicatorView = nil
|
||||||
|
|
||||||
state?.updated(transition: .immediate)
|
state?.updated(transition: .immediate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,6 +139,27 @@ final class MediaStreamVideoComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !self.hadVideo {
|
||||||
|
var activityIndicatorTransition = transition
|
||||||
|
let activityIndicatorView: ComponentHostView<Empty>
|
||||||
|
if let current = self.activityIndicatorView {
|
||||||
|
activityIndicatorView = current
|
||||||
|
} else {
|
||||||
|
activityIndicatorTransition = transition.withAnimation(.none)
|
||||||
|
activityIndicatorView = ComponentHostView<Empty>()
|
||||||
|
self.activityIndicatorView = activityIndicatorView
|
||||||
|
self.addSubview(activityIndicatorView)
|
||||||
|
}
|
||||||
|
|
||||||
|
let activityIndicatorSize = activityIndicatorView.update(
|
||||||
|
transition: transition,
|
||||||
|
component: AnyComponent(ActivityIndicatorComponent()),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||||
|
)
|
||||||
|
activityIndicatorTransition.setFrame(view: activityIndicatorView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - activityIndicatorSize.width) / 2.0), y: floor((availableSize.height - activityIndicatorSize.height) / 2.0)), size: activityIndicatorSize), completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
self.component = component
|
self.component = component
|
||||||
|
|
||||||
component.activatePictureInPicture.connect { [weak self] completion in
|
component.activatePictureInPicture.connect { [weak self] completion in
|
||||||
|
@ -78,12 +78,12 @@ open class ViewControllerComponentContainer: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class Node: ViewControllerTracingNode {
|
final class Node: ViewControllerTracingNode {
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private weak var controller: ViewControllerComponentContainer?
|
private weak var controller: ViewControllerComponentContainer?
|
||||||
|
|
||||||
private let component: AnyComponent<ViewControllerComponentContainer.Environment>
|
private let component: AnyComponent<ViewControllerComponentContainer.Environment>
|
||||||
private let hostView: ComponentHostView<ViewControllerComponentContainer.Environment>
|
let hostView: ComponentHostView<ViewControllerComponentContainer.Environment>
|
||||||
|
|
||||||
private var currentIsVisible: Bool = false
|
private var currentIsVisible: Bool = false
|
||||||
private var currentLayout: ContainerViewLayout?
|
private var currentLayout: ContainerViewLayout?
|
||||||
@ -137,7 +137,7 @@ open class ViewControllerComponentContainer: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var node: Node {
|
var node: Node {
|
||||||
return self.displayNode as! Node
|
return self.displayNode as! Node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,6 +379,8 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
|
|||||||
let membersText: String
|
let membersText: String
|
||||||
if summaryState.participantCount == 0 {
|
if summaryState.participantCount == 0 {
|
||||||
membersText = strongSelf.strings.VoiceChat_Panel_TapToJoin
|
membersText = strongSelf.strings.VoiceChat_Panel_TapToJoin
|
||||||
|
} else if let info = summaryState.info, info.isStream {
|
||||||
|
membersText = strongSelf.strings.LiveStream_ViewerCount(Int32(summaryState.participantCount))
|
||||||
} else {
|
} else {
|
||||||
membersText = strongSelf.strings.VoiceChat_Panel_Members(Int32(summaryState.participantCount))
|
membersText = strongSelf.strings.VoiceChat_Panel_Members(Int32(summaryState.participantCount))
|
||||||
}
|
}
|
||||||
|
@ -687,7 +687,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
isChannel: isChannel,
|
isChannel: isChannel,
|
||||||
invite: nil,
|
invite: nil,
|
||||||
joinAsPeerId: nil
|
joinAsPeerId: nil,
|
||||||
|
isStream: false
|
||||||
)
|
)
|
||||||
strongSelf.updateCurrentGroupCall(call)
|
strongSelf.updateCurrentGroupCall(call)
|
||||||
strongSelf.currentGroupCallPromise.set(.single(call))
|
strongSelf.currentGroupCallPromise.set(.single(call))
|
||||||
@ -849,7 +850,8 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
isChannel: isChannel,
|
isChannel: isChannel,
|
||||||
invite: invite,
|
invite: invite,
|
||||||
joinAsPeerId: joinAsPeerId
|
joinAsPeerId: joinAsPeerId,
|
||||||
|
isStream: initialCall.isStream ?? false
|
||||||
)
|
)
|
||||||
strongSelf.updateCurrentGroupCall(call)
|
strongSelf.updateCurrentGroupCall(call)
|
||||||
strongSelf.currentGroupCallPromise.set(.single(call))
|
strongSelf.currentGroupCallPromise.set(.single(call))
|
||||||
|
@ -86,13 +86,14 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
|||||||
var disposable: Disposable?
|
var disposable: Disposable?
|
||||||
public var participantsContext: GroupCallParticipantsContext?
|
public var participantsContext: GroupCallParticipantsContext?
|
||||||
|
|
||||||
private let panelDataPromise = Promise<GroupCallPanelData>()
|
private let panelDataPromise = Promise<GroupCallPanelData?>()
|
||||||
public var panelData: Signal<GroupCallPanelData, NoError> {
|
public var panelData: Signal<GroupCallPanelData?, NoError> {
|
||||||
return self.panelDataPromise.get()
|
return self.panelDataPromise.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(account: Account, engine: TelegramEngine, peerId: PeerId, isChannel: Bool, call: EngineGroupCallDescription) {
|
public init(account: Account, engine: TelegramEngine, peerId: PeerId, isChannel: Bool, call: EngineGroupCallDescription) {
|
||||||
self.panelDataPromise.set(.single(GroupCallPanelData(
|
self.panelDataPromise.set(.single(nil))
|
||||||
|
/*self.panelDataPromise.set(.single(GroupCallPanelData(
|
||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
isChannel: isChannel,
|
isChannel: isChannel,
|
||||||
info: GroupCallInfo(
|
info: GroupCallInfo(
|
||||||
@ -114,7 +115,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
|||||||
participantCount: 0,
|
participantCount: 0,
|
||||||
activeSpeakers: Set(),
|
activeSpeakers: Set(),
|
||||||
groupCall: nil
|
groupCall: nil
|
||||||
)))
|
)))*/
|
||||||
|
|
||||||
let state = engine.calls.getGroupCallParticipants(callId: call.id, accessHash: call.accessHash, offset: "", ssrcs: [], limit: 100, sortAscending: nil)
|
let state = engine.calls.getGroupCallParticipants(callId: call.id, accessHash: call.accessHash, offset: "", ssrcs: [], limit: 100, sortAscending: nil)
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
@ -161,7 +162,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
|||||||
return GroupCallPanelData(
|
return GroupCallPanelData(
|
||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
isChannel: isChannel,
|
isChannel: isChannel,
|
||||||
info: GroupCallInfo(id: call.id, accessHash: call.accessHash, participantCount: state.totalCount, streamDcId: nil, title: state.title, scheduleTimestamp: state.scheduleTimestamp, subscribedToScheduled: state.subscribedToScheduled, recordingStartTimestamp: nil, sortAscending: state.sortAscending, defaultParticipantsAreMuted: state.defaultParticipantsAreMuted, isVideoEnabled: state.isVideoEnabled, unmutedVideoLimit: state.unmutedVideoLimit, isStream: call.isStream),
|
info: GroupCallInfo(id: call.id, accessHash: call.accessHash, participantCount: state.totalCount, streamDcId: nil, title: state.title, scheduleTimestamp: state.scheduleTimestamp, subscribedToScheduled: state.subscribedToScheduled, recordingStartTimestamp: nil, sortAscending: state.sortAscending, defaultParticipantsAreMuted: state.defaultParticipantsAreMuted, isVideoEnabled: state.isVideoEnabled, unmutedVideoLimit: state.unmutedVideoLimit, isStream: state.isStream),
|
||||||
topParticipants: topParticipants,
|
topParticipants: topParticipants,
|
||||||
participantCount: state.totalCount,
|
participantCount: state.totalCount,
|
||||||
activeSpeakers: activeSpeakers,
|
activeSpeakers: activeSpeakers,
|
||||||
@ -229,7 +230,14 @@ public final class AccountGroupCallContextCacheImpl: AccountGroupCallContextCach
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func leaveInBackground(engine: TelegramEngine, id: Int64, accessHash: Int64, source: UInt32) {
|
public func leaveInBackground(engine: TelegramEngine, id: Int64, accessHash: Int64, source: UInt32) {
|
||||||
let disposable = engine.calls.leaveGroupCall(callId: id, accessHash: accessHash, source: source).start()
|
let disposable = engine.calls.leaveGroupCall(callId: id, accessHash: accessHash, source: source).start(completed: { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let context = strongSelf.contexts[id] {
|
||||||
|
context.context.participantsContext?.removeLocalPeerId()
|
||||||
|
}
|
||||||
|
})
|
||||||
self.leaveDisposables.add(disposable)
|
self.leaveDisposables.add(disposable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -630,7 +638,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
private var screencastAudioDataDisposable: Disposable?
|
private var screencastAudioDataDisposable: Disposable?
|
||||||
private var screencastStateDisposable: Disposable?
|
private var screencastStateDisposable: Disposable?
|
||||||
|
|
||||||
public var isStream = false
|
public let isStream: Bool
|
||||||
|
|
||||||
init(
|
init(
|
||||||
accountContext: AccountContext,
|
accountContext: AccountContext,
|
||||||
@ -642,7 +650,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
peerId: PeerId,
|
peerId: PeerId,
|
||||||
isChannel: Bool,
|
isChannel: Bool,
|
||||||
invite: String?,
|
invite: String?,
|
||||||
joinAsPeerId: PeerId?
|
joinAsPeerId: PeerId?,
|
||||||
|
isStream: Bool
|
||||||
) {
|
) {
|
||||||
self.account = accountContext.account
|
self.account = accountContext.account
|
||||||
self.accountContext = accountContext
|
self.accountContext = accountContext
|
||||||
@ -659,10 +668,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
self.schedulePending = initialCall == nil
|
self.schedulePending = initialCall == nil
|
||||||
self.isScheduled = initialCall == nil || initialCall?.scheduleTimestamp != nil
|
self.isScheduled = initialCall == nil || initialCall?.scheduleTimestamp != nil
|
||||||
|
|
||||||
if let initialCall = initialCall {
|
|
||||||
self.isStream = initialCall.isStream
|
|
||||||
}
|
|
||||||
|
|
||||||
self.stateValue = PresentationGroupCallState.initialValue(myPeerId: self.joinAsPeerId, title: initialCall?.title, scheduleTimestamp: initialCall?.scheduleTimestamp, subscribedToScheduled: initialCall?.subscribedToScheduled ?? false)
|
self.stateValue = PresentationGroupCallState.initialValue(myPeerId: self.joinAsPeerId, title: initialCall?.title, scheduleTimestamp: initialCall?.scheduleTimestamp, subscribedToScheduled: initialCall?.subscribedToScheduled ?? false)
|
||||||
self.statePromise = ValuePromise(self.stateValue)
|
self.statePromise = ValuePromise(self.stateValue)
|
||||||
|
|
||||||
@ -671,6 +676,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
self.isVideoEnabled = true
|
self.isVideoEnabled = true
|
||||||
self.hasVideo = false
|
self.hasVideo = false
|
||||||
self.hasScreencast = false
|
self.hasScreencast = false
|
||||||
|
self.isStream = isStream
|
||||||
|
|
||||||
var didReceiveAudioOutputs = false
|
var didReceiveAudioOutputs = false
|
||||||
|
|
||||||
@ -1228,6 +1234,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
totalCount: 0,
|
totalCount: 0,
|
||||||
isVideoEnabled: callInfo.isVideoEnabled,
|
isVideoEnabled: callInfo.isVideoEnabled,
|
||||||
unmutedVideoLimit: callInfo.unmutedVideoLimit,
|
unmutedVideoLimit: callInfo.unmutedVideoLimit,
|
||||||
|
isStream: callInfo.isStream,
|
||||||
version: 0
|
version: 0
|
||||||
),
|
),
|
||||||
previousServiceState: nil
|
previousServiceState: nil
|
||||||
@ -1677,7 +1684,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
strongSelf.processMyAudioLevel(level: mappedLevel, hasVoice: myLevelHasVoice)
|
strongSelf.processMyAudioLevel(level: mappedLevel, hasVoice: myLevelHasVoice)
|
||||||
strongSelf.isSpeakingPromise.set(orignalMyLevelHasVoice)
|
strongSelf.isSpeakingPromise.set(orignalMyLevelHasVoice)
|
||||||
|
|
||||||
if !missingSsrcs.isEmpty {
|
if !missingSsrcs.isEmpty && !strongSelf.isStream {
|
||||||
strongSelf.participantsContext?.ensureHaveParticipants(ssrcs: missingSsrcs)
|
strongSelf.participantsContext?.ensureHaveParticipants(ssrcs: missingSsrcs)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
@ -2228,6 +2235,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func beginTone(tone: PresentationCallTone) {
|
private func beginTone(tone: PresentationCallTone) {
|
||||||
|
if self.isStream {
|
||||||
|
switch tone {
|
||||||
|
case .groupJoined, .groupLeft:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
var completed: (() -> Void)?
|
var completed: (() -> Void)?
|
||||||
let toneRenderer = PresentationCallToneRenderer(tone: tone, completed: {
|
let toneRenderer = PresentationCallToneRenderer(tone: tone, completed: {
|
||||||
completed?()
|
completed?()
|
||||||
@ -2995,7 +3010,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
|
|
||||||
if let value = value {
|
if let value = value {
|
||||||
strongSelf.initialCall = EngineGroupCallDescription(id: value.id, accessHash: value.accessHash, title: value.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: value.isStream)
|
strongSelf.initialCall = EngineGroupCallDescription(id: value.id, accessHash: value.accessHash, title: value.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: value.isStream)
|
||||||
strongSelf.isStream = value.isStream
|
|
||||||
|
|
||||||
strongSelf.updateSessionState(internalState: .active(value), audioSessionControl: strongSelf.audioSessionControl)
|
strongSelf.updateSessionState(internalState: .active(value), audioSessionControl: strongSelf.audioSessionControl)
|
||||||
} else {
|
} else {
|
||||||
|
@ -161,7 +161,7 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
public var title: String?
|
public var title: String?
|
||||||
public var scheduleTimestamp: Int32?
|
public var scheduleTimestamp: Int32?
|
||||||
public var subscribedToScheduled: Bool
|
public var subscribedToScheduled: Bool
|
||||||
public var isStream: Bool
|
public var isStream: Bool?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
id: Int64,
|
id: Int64,
|
||||||
@ -169,7 +169,7 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
title: String?,
|
title: String?,
|
||||||
scheduleTimestamp: Int32?,
|
scheduleTimestamp: Int32?,
|
||||||
subscribedToScheduled: Bool,
|
subscribedToScheduled: Bool,
|
||||||
isStream: Bool
|
isStream: Bool?
|
||||||
) {
|
) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.accessHash = accessHash
|
self.accessHash = accessHash
|
||||||
@ -185,7 +185,7 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
self.title = decoder.decodeOptionalStringForKey("title")
|
self.title = decoder.decodeOptionalStringForKey("title")
|
||||||
self.scheduleTimestamp = decoder.decodeOptionalInt32ForKey("scheduleTimestamp")
|
self.scheduleTimestamp = decoder.decodeOptionalInt32ForKey("scheduleTimestamp")
|
||||||
self.subscribedToScheduled = decoder.decodeBoolForKey("subscribed", orElse: false)
|
self.subscribedToScheduled = decoder.decodeBoolForKey("subscribed", orElse: false)
|
||||||
self.isStream = decoder.decodeBoolForKey("isStream", orElse: false)
|
self.isStream = decoder.decodeOptionalBoolForKey("isStream_v2")
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
@ -202,7 +202,11 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
encoder.encodeNil(forKey: "scheduleTimestamp")
|
encoder.encodeNil(forKey: "scheduleTimestamp")
|
||||||
}
|
}
|
||||||
encoder.encodeBool(self.subscribedToScheduled, forKey: "subscribed")
|
encoder.encodeBool(self.subscribedToScheduled, forKey: "subscribed")
|
||||||
encoder.encodeBool(self.isStream, forKey: "isStream")
|
if let isStream = self.isStream {
|
||||||
|
encoder.encodeBool(isStream, forKey: "isStream")
|
||||||
|
} else {
|
||||||
|
encoder.encodeNil(forKey: "isStream")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,17 +342,17 @@ public enum GetGroupCallParticipantsError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func _internal_getGroupCallParticipants(account: Account, callId: Int64, accessHash: Int64, offset: String, ssrcs: [UInt32], limit: Int32, sortAscending: Bool?) -> Signal<GroupCallParticipantsContext.State, GetGroupCallParticipantsError> {
|
func _internal_getGroupCallParticipants(account: Account, callId: Int64, accessHash: Int64, offset: String, ssrcs: [UInt32], limit: Int32, sortAscending: Bool?) -> Signal<GroupCallParticipantsContext.State, GetGroupCallParticipantsError> {
|
||||||
let sortAscendingValue: Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int), GetGroupCallParticipantsError>
|
let sortAscendingValue: Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool), GetGroupCallParticipantsError>
|
||||||
|
|
||||||
sortAscendingValue = _internal_getCurrentGroupCall(account: account, callId: callId, accessHash: accessHash)
|
sortAscendingValue = _internal_getCurrentGroupCall(account: account, callId: callId, accessHash: accessHash)
|
||||||
|> mapError { _ -> GetGroupCallParticipantsError in
|
|> mapError { _ -> GetGroupCallParticipantsError in
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
|> mapToSignal { result -> Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int), GetGroupCallParticipantsError> in
|
|> mapToSignal { result -> Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool), GetGroupCallParticipantsError> in
|
||||||
guard let result = result else {
|
guard let result = result else {
|
||||||
return .fail(.generic)
|
return .fail(.generic)
|
||||||
}
|
}
|
||||||
return .single((sortAscending ?? result.info.sortAscending, result.info.scheduleTimestamp, result.info.subscribedToScheduled, result.info.defaultParticipantsAreMuted, result.info.isVideoEnabled, result.info.unmutedVideoLimit))
|
return .single((sortAscending ?? result.info.sortAscending, result.info.scheduleTimestamp, result.info.subscribedToScheduled, result.info.defaultParticipantsAreMuted, result.info.isVideoEnabled, result.info.unmutedVideoLimit, result.info.isStream))
|
||||||
}
|
}
|
||||||
|
|
||||||
return combineLatest(
|
return combineLatest(
|
||||||
@ -369,7 +369,7 @@ func _internal_getGroupCallParticipants(account: Account, callId: Int64, accessH
|
|||||||
let version: Int32
|
let version: Int32
|
||||||
let nextParticipantsFetchOffset: String?
|
let nextParticipantsFetchOffset: String?
|
||||||
|
|
||||||
let (sortAscendingValue, scheduleTimestamp, subscribedToScheduled, defaultParticipantsAreMuted, isVideoEnabled, unmutedVideoLimit) = sortAscendingAndScheduleTimestamp
|
let (sortAscendingValue, scheduleTimestamp, subscribedToScheduled, defaultParticipantsAreMuted, isVideoEnabled, unmutedVideoLimit, isStream) = sortAscendingAndScheduleTimestamp
|
||||||
|
|
||||||
switch result {
|
switch result {
|
||||||
case let .groupParticipants(count, participants, nextOffset, chats, users, apiVersion):
|
case let .groupParticipants(count, participants, nextOffset, chats, users, apiVersion):
|
||||||
@ -423,6 +423,7 @@ func _internal_getGroupCallParticipants(account: Account, callId: Int64, accessH
|
|||||||
totalCount: totalCount,
|
totalCount: totalCount,
|
||||||
isVideoEnabled: isVideoEnabled,
|
isVideoEnabled: isVideoEnabled,
|
||||||
unmutedVideoLimit: unmutedVideoLimit,
|
unmutedVideoLimit: unmutedVideoLimit,
|
||||||
|
isStream: isStream,
|
||||||
version: version
|
version: version
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1047,6 +1048,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
public var totalCount: Int
|
public var totalCount: Int
|
||||||
public var isVideoEnabled: Bool
|
public var isVideoEnabled: Bool
|
||||||
public var unmutedVideoLimit: Int
|
public var unmutedVideoLimit: Int
|
||||||
|
public var isStream: Bool
|
||||||
public var version: Int32
|
public var version: Int32
|
||||||
|
|
||||||
public mutating func mergeActivity(from other: State, myPeerId: PeerId?, previousMyPeerId: PeerId?, mergeActivityTimestamps: Bool) {
|
public mutating func mergeActivity(from other: State, myPeerId: PeerId?, previousMyPeerId: PeerId?, mergeActivityTimestamps: Bool) {
|
||||||
@ -1081,6 +1083,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
totalCount: Int,
|
totalCount: Int,
|
||||||
isVideoEnabled: Bool,
|
isVideoEnabled: Bool,
|
||||||
unmutedVideoLimit: Int,
|
unmutedVideoLimit: Int,
|
||||||
|
isStream: Bool,
|
||||||
version: Int32
|
version: Int32
|
||||||
) {
|
) {
|
||||||
self.participants = participants
|
self.participants = participants
|
||||||
@ -1096,6 +1099,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
self.totalCount = totalCount
|
self.totalCount = totalCount
|
||||||
self.isVideoEnabled = isVideoEnabled
|
self.isVideoEnabled = isVideoEnabled
|
||||||
self.unmutedVideoLimit = unmutedVideoLimit
|
self.unmutedVideoLimit = unmutedVideoLimit
|
||||||
|
self.isStream = isStream
|
||||||
self.version = version
|
self.version = version
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1398,6 +1402,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
totalCount: strongSelf.stateValue.state.totalCount,
|
totalCount: strongSelf.stateValue.state.totalCount,
|
||||||
isVideoEnabled: strongSelf.stateValue.state.isVideoEnabled,
|
isVideoEnabled: strongSelf.stateValue.state.isVideoEnabled,
|
||||||
unmutedVideoLimit: strongSelf.stateValue.state.unmutedVideoLimit,
|
unmutedVideoLimit: strongSelf.stateValue.state.unmutedVideoLimit,
|
||||||
|
isStream: strongSelf.stateValue.state.isStream,
|
||||||
version: strongSelf.stateValue.state.version
|
version: strongSelf.stateValue.state.version
|
||||||
),
|
),
|
||||||
overlayState: strongSelf.stateValue.overlayState
|
overlayState: strongSelf.stateValue.overlayState
|
||||||
@ -1471,6 +1476,14 @@ public final class GroupCallParticipantsContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func removeLocalPeerId() {
|
||||||
|
var state = self.stateValue.state
|
||||||
|
|
||||||
|
state.participants.removeAll(where: { $0.peer.id == self.myPeerId })
|
||||||
|
|
||||||
|
self.stateValue.state = state
|
||||||
|
}
|
||||||
|
|
||||||
private func takeNextActivityRank() -> Int {
|
private func takeNextActivityRank() -> Int {
|
||||||
let value = self.serviceState.nextActivityRank
|
let value = self.serviceState.nextActivityRank
|
||||||
self.serviceState.nextActivityRank += 1
|
self.serviceState.nextActivityRank += 1
|
||||||
@ -1537,6 +1550,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
totalCount: strongSelf.stateValue.state.totalCount,
|
totalCount: strongSelf.stateValue.state.totalCount,
|
||||||
isVideoEnabled: strongSelf.stateValue.state.isVideoEnabled,
|
isVideoEnabled: strongSelf.stateValue.state.isVideoEnabled,
|
||||||
unmutedVideoLimit: strongSelf.stateValue.state.unmutedVideoLimit,
|
unmutedVideoLimit: strongSelf.stateValue.state.unmutedVideoLimit,
|
||||||
|
isStream: strongSelf.stateValue.state.isStream,
|
||||||
version: strongSelf.stateValue.state.version
|
version: strongSelf.stateValue.state.version
|
||||||
),
|
),
|
||||||
overlayState: strongSelf.stateValue.overlayState
|
overlayState: strongSelf.stateValue.overlayState
|
||||||
@ -1758,6 +1772,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
let scheduleTimestamp = strongSelf.stateValue.state.scheduleTimestamp
|
let scheduleTimestamp = strongSelf.stateValue.state.scheduleTimestamp
|
||||||
let subscribedToScheduled = strongSelf.stateValue.state.subscribedToScheduled
|
let subscribedToScheduled = strongSelf.stateValue.state.subscribedToScheduled
|
||||||
let isVideoEnabled = strongSelf.stateValue.state.isVideoEnabled
|
let isVideoEnabled = strongSelf.stateValue.state.isVideoEnabled
|
||||||
|
let isStream = strongSelf.stateValue.state.isStream
|
||||||
let unmutedVideoLimit = strongSelf.stateValue.state.unmutedVideoLimit
|
let unmutedVideoLimit = strongSelf.stateValue.state.unmutedVideoLimit
|
||||||
|
|
||||||
updatedParticipants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: strongSelf.stateValue.state.sortAscending) })
|
updatedParticipants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: strongSelf.stateValue.state.sortAscending) })
|
||||||
@ -1777,6 +1792,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
totalCount: updatedTotalCount,
|
totalCount: updatedTotalCount,
|
||||||
isVideoEnabled: isVideoEnabled,
|
isVideoEnabled: isVideoEnabled,
|
||||||
unmutedVideoLimit: unmutedVideoLimit,
|
unmutedVideoLimit: unmutedVideoLimit,
|
||||||
|
isStream: isStream,
|
||||||
version: update.version
|
version: update.version
|
||||||
),
|
),
|
||||||
overlayState: updatedOverlayState
|
overlayState: updatedOverlayState
|
||||||
@ -2398,7 +2414,7 @@ private func mergeAndSortParticipants(current currentParticipants: [GroupCallPar
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class AudioBroadcastDataSource {
|
public final class AudioBroadcastDataSource {
|
||||||
fileprivate let download: Download
|
let download: Download
|
||||||
|
|
||||||
fileprivate init(download: Download) {
|
fileprivate init(download: Download) {
|
||||||
self.download = download
|
self.download = download
|
||||||
|
@ -132,8 +132,8 @@ public extension TelegramEngine {
|
|||||||
|> take(1)
|
|> take(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func requestStreamState(callId: Int64, accessHash: Int64) -> Signal<EngineCallStreamState?, NoError> {
|
public func requestStreamState(dataSource: AudioBroadcastDataSource, callId: Int64, accessHash: Int64) -> Signal<EngineCallStreamState?, NoError> {
|
||||||
return self.account.network.request(Api.functions.phone.getGroupCallStreamChannels(call: .inputGroupCall(id: callId, accessHash: accessHash)))
|
return dataSource.download.request(Api.functions.phone.getGroupCallStreamChannels(call: .inputGroupCall(id: callId, accessHash: accessHash)))
|
||||||
|> mapToSignal { result -> Signal<EngineCallStreamState?, MTRpcError> in
|
|> mapToSignal { result -> Signal<EngineCallStreamState?, MTRpcError> in
|
||||||
switch result {
|
switch result {
|
||||||
case let .groupCallStreamChannels(channels):
|
case let .groupCallStreamChannels(channels):
|
||||||
|
@ -6,7 +6,7 @@ public final class EngineGroupCallDescription {
|
|||||||
public let title: String?
|
public let title: String?
|
||||||
public let scheduleTimestamp: Int32?
|
public let scheduleTimestamp: Int32?
|
||||||
public let subscribedToScheduled: Bool
|
public let subscribedToScheduled: Bool
|
||||||
public let isStream: Bool
|
public let isStream: Bool?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
id: Int64,
|
id: Int64,
|
||||||
@ -14,7 +14,7 @@ public final class EngineGroupCallDescription {
|
|||||||
title: String?,
|
title: String?,
|
||||||
scheduleTimestamp: Int32?,
|
scheduleTimestamp: Int32?,
|
||||||
subscribedToScheduled: Bool,
|
subscribedToScheduled: Bool,
|
||||||
isStream: Bool
|
isStream: Bool?
|
||||||
) {
|
) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.accessHash = accessHash
|
self.accessHash = accessHash
|
||||||
|
@ -349,7 +349,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
|
|||||||
if let inputCall = chatFullCall {
|
if let inputCall = chatFullCall {
|
||||||
switch inputCall {
|
switch inputCall {
|
||||||
case let .inputGroupCall(id, accessHash):
|
case let .inputGroupCall(id, accessHash):
|
||||||
updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash, title: previous.activeCall?.title, scheduleTimestamp: previous.activeCall?.scheduleTimestamp, subscribedToScheduled: previous.activeCall?.subscribedToScheduled ?? false, isStream: previous.activeCall?.isStream ?? false)
|
updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash, title: previous.activeCall?.title, scheduleTimestamp: previous.activeCall?.scheduleTimestamp, subscribedToScheduled: previous.activeCall?.subscribedToScheduled ?? false, isStream: previous.activeCall?.isStream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,7 +568,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
|
|||||||
if let inputCall = inputCall {
|
if let inputCall = inputCall {
|
||||||
switch inputCall {
|
switch inputCall {
|
||||||
case let .inputGroupCall(id, accessHash):
|
case let .inputGroupCall(id, accessHash):
|
||||||
updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash, title: previous.activeCall?.title, scheduleTimestamp: previous.activeCall?.scheduleTimestamp, subscribedToScheduled: previous.activeCall?.subscribedToScheduled ?? false, isStream: previous.activeCall?.isStream ?? false)
|
updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash, title: previous.activeCall?.title, scheduleTimestamp: previous.activeCall?.scheduleTimestamp, subscribedToScheduled: previous.activeCall?.subscribedToScheduled ?? false, isStream: previous.activeCall?.isStream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,28 @@ private final class NetworkBroadcastPartSource: BroadcastPartSource {
|
|||||||
|
|
||||||
func requestTime(completion: @escaping (Int64) -> Void) -> Disposable {
|
func requestTime(completion: @escaping (Int64) -> Void) -> Disposable {
|
||||||
if self.isExternalStream {
|
if self.isExternalStream {
|
||||||
return self.engine.calls.requestStreamState(callId: self.callId, accessHash: self.accessHash).start(next: { result in
|
let dataSource: Signal<AudioBroadcastDataSource?, NoError>
|
||||||
|
if let dataSourceValue = self.dataSource {
|
||||||
|
dataSource = .single(dataSourceValue)
|
||||||
|
} else {
|
||||||
|
dataSource = self.engine.calls.getAudioBroadcastDataSource(callId: self.callId, accessHash: self.accessHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
let engine = self.engine
|
||||||
|
let callId = self.callId
|
||||||
|
let accessHash = self.accessHash
|
||||||
|
|
||||||
|
return (dataSource
|
||||||
|
|> deliverOn(self.queue)
|
||||||
|
|> mapToSignal { [weak self] dataSource -> Signal<EngineCallStreamState?, NoError> in
|
||||||
|
if let dataSource = dataSource {
|
||||||
|
self?.dataSource = dataSource
|
||||||
|
return engine.calls.requestStreamState(dataSource: dataSource, callId: callId, accessHash: accessHash)
|
||||||
|
} else {
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> deliverOn(self.queue)).start(next: { result in
|
||||||
if let channel = result?.channels.first {
|
if let channel = result?.channels.first {
|
||||||
completion(channel.latestTimestamp)
|
completion(channel.latestTimestamp)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit a424c9919c425454abbf31bc41f8496756e6d8bf
|
Subproject commit d389503ac99d5d6fa16870cc280e83151cba8ccc
|
Loading…
x
Reference in New Issue
Block a user