Various improvements
This commit is contained in:
parent
b335bb83df
commit
7e240c7064
21 changed files with 177 additions and 89 deletions
|
|
@ -6,19 +6,24 @@ import SwiftSignalKit
|
|||
import TelegramAudio
|
||||
import Display
|
||||
|
||||
public enum CallAlreadyInProgressType {
|
||||
case peer(EnginePeer.Id?)
|
||||
case external
|
||||
}
|
||||
|
||||
public enum RequestCallResult {
|
||||
case requested
|
||||
case alreadyInProgress(EnginePeer.Id?)
|
||||
case alreadyInProgress(CallAlreadyInProgressType)
|
||||
}
|
||||
|
||||
public enum JoinGroupCallManagerResult {
|
||||
case joined
|
||||
case alreadyInProgress(EnginePeer.Id?)
|
||||
case alreadyInProgress(CallAlreadyInProgressType)
|
||||
}
|
||||
|
||||
public enum RequestScheduleGroupCallResult {
|
||||
case success
|
||||
case alreadyInProgress(EnginePeer.Id?)
|
||||
case alreadyInProgress(CallAlreadyInProgressType)
|
||||
}
|
||||
|
||||
public struct CallAuxiliaryServer {
|
||||
|
|
|
|||
|
|
@ -2794,7 +2794,7 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
|
|||
ASDisplayNodeAssertMainThread();
|
||||
ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"You should never call -didEnterHierarchy directly. Appearance is automatically managed by ASDisplayNode");
|
||||
ASDisplayNodeAssert(!_flags.isExitingHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
|
||||
ASDisplayNodeAssert(_flags.isInHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
|
||||
//ASDisplayNodeAssert(_flags.isInHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
|
||||
ASAssertUnlocked(__instanceLock__);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -854,6 +854,12 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
|||
avatarFrame.origin.x = revealOffset + leftInset - 52.0
|
||||
transition.updateFrameAdditive(node: self.avatarNode, frame: avatarFrame)
|
||||
|
||||
if let conferenceAvatarListNode = self.conferenceAvatarListNode {
|
||||
var conferenceAvatarFrame = conferenceAvatarListNode.frame
|
||||
conferenceAvatarFrame.origin.x = avatarFrame.minX + floor((avatarFrame.width - conferenceAvatarFrame.width) / 2.0)
|
||||
transition.updateFrameAdditive(node: conferenceAvatarListNode, frame: conferenceAvatarFrame)
|
||||
}
|
||||
|
||||
transition.updateFrameAdditive(node: self.titleNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset, y: self.titleNode.frame.minY), size: self.titleNode.bounds.size))
|
||||
|
||||
transition.updateFrameAdditive(node: self.statusNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset, y: self.statusNode.frame.minY), size: self.statusNode.bounds.size))
|
||||
|
|
|
|||
|
|
@ -519,10 +519,13 @@ public final class CallListController: TelegramBaseController {
|
|||
return
|
||||
}
|
||||
|
||||
var dismissSelectionController: (() -> Void)?
|
||||
|
||||
let options = [ContactListAdditionalOption(title: self.presentationData.strings.CallList_NewCallLink, icon: .generic(PresentationResourcesItemList.linkIcon(presentationData.theme)!), action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
dismissSelectionController?()
|
||||
self.createGroupCall(peerIds: [], isVideo: false)
|
||||
}, clearHighlightAutomatically: true)]
|
||||
|
||||
|
|
@ -546,6 +549,10 @@ public final class CallListController: TelegramBaseController {
|
|||
if let navigationController = self.context.sharedContext.mainWindow?.viewController as? NavigationController {
|
||||
navigationController.pushViewController(controller)
|
||||
}
|
||||
|
||||
dismissSelectionController = { [weak controller] in
|
||||
controller?.dismiss()
|
||||
}
|
||||
|
||||
let _ = (controller.result
|
||||
|> take(1)
|
||||
|
|
|
|||
|
|
@ -350,6 +350,9 @@ final class CallListControllerNode: ASDisplayNode {
|
|||
let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
|
||||
var items: [ActionSheetItem] = []
|
||||
|
||||
//TODO:localize
|
||||
items.append(ActionSheetTextItem(title: "Do you want to delete the information about this call?", parseMarkdown: true))
|
||||
|
||||
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_DeleteMessagesFor(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
guard let strongSelf = self else {
|
||||
|
|
|
|||
|
|
@ -936,9 +936,6 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||
}
|
||||
|
||||
override func animateIn(from node: (ASDisplayNode, CGRect, () -> (UIView?, UIView?)), addToTransitionSurface: (UIView) -> Void, completion: @escaping () -> Void) {
|
||||
let wasCaptureProtected = self.imageNode.captureProtected
|
||||
self.imageNode.captureProtected = false
|
||||
|
||||
let contentNode = self.tilingNode ?? self.imageNode
|
||||
|
||||
var transformedFrame = node.0.view.convert(node.0.view.bounds, to: contentNode.view)
|
||||
|
|
@ -980,14 +977,8 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||
|
||||
let positionDuration: Double = 0.21
|
||||
|
||||
copyView.layer.animatePosition(from: CGPoint(x: transformedSelfFrame.midX, y: transformedSelfFrame.midY), to: CGPoint(x: transformedCopyViewFinalFrame.midX, y: transformedCopyViewFinalFrame.midY), duration: positionDuration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { [weak copyView, weak self] _ in
|
||||
copyView.layer.animatePosition(from: CGPoint(x: transformedSelfFrame.midX, y: transformedSelfFrame.midY), to: CGPoint(x: transformedCopyViewFinalFrame.midX, y: transformedCopyViewFinalFrame.midY), duration: positionDuration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { [weak copyView] _ in
|
||||
copyView?.removeFromSuperview()
|
||||
|
||||
if wasCaptureProtected {
|
||||
Queue.mainQueue().after(0.2) {
|
||||
self?.imageNode.captureProtected = true
|
||||
}
|
||||
}
|
||||
})
|
||||
let scale = CGSize(width: transformedCopyViewFinalFrame.size.width / transformedSelfFrame.size.width, height: transformedCopyViewFinalFrame.size.height / transformedSelfFrame.size.height)
|
||||
copyView.layer.animate(from: NSValue(caTransform3D: CATransform3DIdentity), to: NSValue(caTransform3D: CATransform3DMakeScale(scale.width, scale.height, 1.0)), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25, removeOnCompletion: false)
|
||||
|
|
|
|||
|
|
@ -422,28 +422,25 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
}
|
||||
|
||||
public func requestCall(context: AccountContext, peerId: PeerId, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult {
|
||||
var alreadyInCall: Bool = false
|
||||
var alreadyInCallWithPeerId: PeerId?
|
||||
var alreadyInCallType: CallAlreadyInProgressType?
|
||||
|
||||
if let call = self.currentCall {
|
||||
alreadyInCall = true
|
||||
alreadyInCallWithPeerId = call.peerId
|
||||
alreadyInCallType = .peer(call.peerId)
|
||||
} else if let currentGroupCall = self.currentGroupCallValue {
|
||||
alreadyInCall = true
|
||||
switch currentGroupCall {
|
||||
case let .conferenceSource(conferenceSource):
|
||||
alreadyInCallWithPeerId = conferenceSource.peerId
|
||||
alreadyInCallType = .peer(conferenceSource.peerId)
|
||||
case let .group(groupCall):
|
||||
alreadyInCallWithPeerId = groupCall.peerId
|
||||
alreadyInCallType = .peer(groupCall.peerId)
|
||||
}
|
||||
} else {
|
||||
if CXCallObserver().calls.contains(where: { $0.hasEnded == false }) {
|
||||
alreadyInCall = true
|
||||
alreadyInCallType = .external
|
||||
}
|
||||
}
|
||||
|
||||
if alreadyInCall, !endCurrentIfAny {
|
||||
return .alreadyInProgress(alreadyInCallWithPeerId)
|
||||
if let alreadyInCallType, !endCurrentIfAny {
|
||||
return .alreadyInProgress(alreadyInCallType)
|
||||
}
|
||||
if let _ = callKitIntegrationIfEnabled(self.callKitIntegration, settings: self.callSettings) {
|
||||
let begin: () -> Void = { [weak self] in
|
||||
|
|
@ -942,9 +939,9 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
} else {
|
||||
switch currentGroupCall {
|
||||
case let .conferenceSource(conferenceSource):
|
||||
return .alreadyInProgress(conferenceSource.peerId)
|
||||
return .alreadyInProgress(.peer(conferenceSource.peerId))
|
||||
case let .group(groupCall):
|
||||
return .alreadyInProgress(groupCall.peerId)
|
||||
return .alreadyInProgress(.peer(groupCall.peerId))
|
||||
}
|
||||
}
|
||||
} else if let currentCall = self.currentCall {
|
||||
|
|
@ -955,7 +952,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
begin()
|
||||
}))
|
||||
} else {
|
||||
return .alreadyInProgress(currentCall.peerId)
|
||||
return .alreadyInProgress(.peer(currentCall.peerId))
|
||||
}
|
||||
} else {
|
||||
begin()
|
||||
|
|
@ -1001,9 +998,9 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
} else {
|
||||
switch currentGroupCall {
|
||||
case let .conferenceSource(conferenceSource):
|
||||
return .alreadyInProgress(conferenceSource.peerId)
|
||||
return .alreadyInProgress(.peer(conferenceSource.peerId))
|
||||
case let .group(groupCall):
|
||||
return .alreadyInProgress(groupCall.peerId)
|
||||
return .alreadyInProgress(.peer(groupCall.peerId))
|
||||
}
|
||||
}
|
||||
} else if let currentCall = self.currentCall {
|
||||
|
|
@ -1014,7 +1011,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
begin()
|
||||
}))
|
||||
} else {
|
||||
return .alreadyInProgress(currentCall.peerId)
|
||||
return .alreadyInProgress(.peer(currentCall.peerId))
|
||||
}
|
||||
} else {
|
||||
begin()
|
||||
|
|
@ -1194,9 +1191,9 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
} else {
|
||||
switch currentGroupCall {
|
||||
case let .conferenceSource(conferenceSource):
|
||||
return .alreadyInProgress(conferenceSource.peerId)
|
||||
return .alreadyInProgress(.peer(conferenceSource.peerId))
|
||||
case let .group(groupCall):
|
||||
return .alreadyInProgress(groupCall.peerId)
|
||||
return .alreadyInProgress(.peer(groupCall.peerId))
|
||||
}
|
||||
}
|
||||
} else if let currentCall = self.currentCall {
|
||||
|
|
@ -1207,7 +1204,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||
begin()
|
||||
}))
|
||||
} else {
|
||||
return .alreadyInProgress(currentCall.peerId)
|
||||
return .alreadyInProgress(.peer(currentCall.peerId))
|
||||
}
|
||||
} else {
|
||||
begin()
|
||||
|
|
|
|||
|
|
@ -349,8 +349,8 @@ private final class ConferenceCallE2EContextStateImpl: ConferenceCallE2EContextS
|
|||
return self.call.participants().map { $0.userId }
|
||||
}
|
||||
|
||||
func applyBlock(block: Data) {
|
||||
self.call.applyBlock(block)
|
||||
func applyBlock(block: Data) -> Bool {
|
||||
return self.call.applyBlock(block)
|
||||
}
|
||||
|
||||
func applyBroadcastBlock(block: Data) {
|
||||
|
|
@ -636,6 +636,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
}
|
||||
|
||||
private var participantsContextStateDisposable = MetaDisposable()
|
||||
private var isFailedEventDisposable: Disposable?
|
||||
private var temporaryParticipantsContext: GroupCallParticipantsContext?
|
||||
private var participantsContext: GroupCallParticipantsContext?
|
||||
|
||||
|
|
@ -1244,6 +1245,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
self.peerUpdatesSubscription?.dispose()
|
||||
self.screencastStateDisposable?.dispose()
|
||||
self.pendingDisconnedUpgradedConferenceCallTimer?.invalidate()
|
||||
self.participantsContextStateDisposable.dispose()
|
||||
self.isFailedEventDisposable?.dispose()
|
||||
}
|
||||
|
||||
private func switchToTemporaryParticipantsContext(sourceContext: GroupCallParticipantsContext?, oldMyPeerId: PeerId) {
|
||||
|
|
@ -2616,6 +2619,16 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
}
|
||||
}))
|
||||
|
||||
self.isFailedEventDisposable = (participantsContext.isFailedEvent
|
||||
|> filter { $0 }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] isFailed in
|
||||
guard let self, isFailed else {
|
||||
return
|
||||
}
|
||||
let _ = self.leave(terminateIfPossible: false).startStandalone()
|
||||
})
|
||||
|
||||
let engine = self.accountContext.engine
|
||||
self.memberEventsPipeDisposable.set((participantsContext.memberEvents
|
||||
|> mapToSignal { event -> Signal<PresentationGroupCallMemberEvent, NoError> in
|
||||
|
|
@ -3748,6 +3761,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||
self.conferenceInvitationContexts.removeValue(forKey: peerId)
|
||||
if let messageId = conferenceInvitationContext.messageId {
|
||||
self.accountContext.engine.account.callSessionManager.dropOutgoingConferenceRequest(messageId: messageId)
|
||||
let _ = self.accountContext.engine.messages.deleteMessagesInteractively(messageIds: [messageId], type: .forEveryone).startStandalone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -610,9 +610,7 @@ final class VideoChatExpandedParticipantThumbnailsComponent: Component {
|
|||
self.scrollView.canCancelContentTouches = true
|
||||
self.scrollView.clipsToBounds = false
|
||||
self.scrollView.contentInsetAdjustmentBehavior = .never
|
||||
if #available(iOS 13.0, *) {
|
||||
self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false
|
||||
}
|
||||
self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false
|
||||
self.scrollView.showsVerticalScrollIndicator = false
|
||||
self.scrollView.showsHorizontalScrollIndicator = false
|
||||
self.scrollView.alwaysBounceHorizontal = false
|
||||
|
|
@ -620,6 +618,7 @@ final class VideoChatExpandedParticipantThumbnailsComponent: Component {
|
|||
self.scrollView.scrollsToTop = false
|
||||
self.scrollView.delegate = self
|
||||
self.scrollView.clipsToBounds = true
|
||||
self.scrollView.disablesInteractiveTransitionGestureRecognizer = true
|
||||
|
||||
self.addSubview(self.scrollView)
|
||||
}
|
||||
|
|
@ -698,7 +697,7 @@ final class VideoChatExpandedParticipantThumbnailsComponent: Component {
|
|||
transition.animateScale(view: itemComponentView, from: 0.001, to: 1.0)
|
||||
}
|
||||
}
|
||||
transition.setFrame(view: itemComponentView, frame: itemFrame)
|
||||
itemTransition.setFrame(view: itemComponentView, frame: itemFrame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -743,6 +742,7 @@ final class VideoChatExpandedParticipantThumbnailsComponent: Component {
|
|||
let size = CGSize(width: availableSize.width, height: itemLayout.contentSize.height)
|
||||
|
||||
self.ignoreScrolling = true
|
||||
let previousOffsetX = self.scrollView.bounds.minX
|
||||
if self.scrollView.bounds.size != size {
|
||||
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size))
|
||||
}
|
||||
|
|
@ -750,6 +750,9 @@ final class VideoChatExpandedParticipantThumbnailsComponent: Component {
|
|||
if self.scrollView.contentSize != contentSize {
|
||||
self.scrollView.contentSize = contentSize
|
||||
}
|
||||
if self.scrollView.isDragging {
|
||||
self.scrollView.bounds.origin.x = previousOffsetX
|
||||
}
|
||||
self.ignoreScrolling = false
|
||||
|
||||
self.updateScrolling(transition: transition)
|
||||
|
|
|
|||
|
|
@ -481,7 +481,7 @@ final class VideoChatScreenComponent: Component {
|
|||
if target === participantsView {
|
||||
return nil
|
||||
}
|
||||
if let target = target as? UIScrollView {
|
||||
if let target = target as? UIScrollView, !target.disablesInteractiveTransitionGestureRecognizer {
|
||||
return target
|
||||
}
|
||||
if let parent = target.superview {
|
||||
|
|
|
|||
|
|
@ -313,14 +313,17 @@ extension VideoChatScreenComponent.View {
|
|||
|
||||
if groupCall.isConference {
|
||||
groupCall.kickPeer(id: peer.id)
|
||||
|
||||
//TODO:localize
|
||||
self.presentUndoOverlay(content: .banned(text: "You removed \(peer.displayTitle(strings: environment.strings, displayOrder: nameDisplayOrder)) from this call. They will no longer be able to join using the invite link."), action: { _ in return false })
|
||||
} else {
|
||||
if let callPeerId = groupCall.peerId {
|
||||
let _ = groupCall.accountContext.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: groupCall.accountContext.engine, peerId: callPeerId, memberId: peer.id, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max)).start()
|
||||
groupCall.removedPeer(peer.id)
|
||||
}
|
||||
|
||||
self.presentUndoOverlay(content: .banned(text: environment.strings.VoiceChat_RemovedPeerText(peer.displayTitle(strings: environment.strings, displayOrder: nameDisplayOrder)).string), action: { _ in return false })
|
||||
}
|
||||
|
||||
self.presentUndoOverlay(content: .banned(text: environment.strings.VoiceChat_RemovedPeerText(peer.displayTitle(strings: environment.strings, displayOrder: nameDisplayOrder)).string), action: { _ in return false })
|
||||
}))
|
||||
|
||||
actionSheet.setItemGroups([
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ public protocol ConferenceCallE2EContextState: AnyObject {
|
|||
func getParticipants() -> [ConferenceCallE2EContext.BlockchainParticipant]
|
||||
func getParticipantLatencies() -> [Int64: Double]
|
||||
|
||||
func applyBlock(block: Data)
|
||||
func applyBlock(block: Data) -> Bool
|
||||
func applyBroadcastBlock(block: Data)
|
||||
|
||||
func generateRemoveParticipantsBlock(participantIds: [Int64]) -> Data?
|
||||
|
|
@ -51,6 +51,7 @@ public final class ConferenceCallE2EContext {
|
|||
|
||||
let e2eEncryptionKeyHashValue = ValuePromise<Data?>(nil)
|
||||
let blockchainParticipantsValue = ValuePromise<[BlockchainParticipant]>([])
|
||||
let isFailed = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
|
||||
private var e2ePoll0Offset: Int?
|
||||
private var e2ePoll0Timer: Foundation.Timer?
|
||||
|
|
@ -167,35 +168,38 @@ public final class ConferenceCallE2EContext {
|
|||
let keyPair = self.keyPair
|
||||
let userId = self.userId
|
||||
let initializeState = self.initializeState
|
||||
let (outBlocks, outEmoji, outBlockchainParticipants, participantLatencies) = self.state.with({ callState -> ([Data], Data, [BlockchainParticipant], [Int64: Double]) in
|
||||
let (outBlocks, outEmoji, outBlockchainParticipants, participantLatencies, hadFailure) = self.state.with({ callState -> ([Data], Data, [BlockchainParticipant], [Int64: Double], Bool) in
|
||||
if let state = callState.state {
|
||||
var hadFailure = false
|
||||
for block in blocks {
|
||||
if subChainId == 0 {
|
||||
state.applyBlock(block: block)
|
||||
if !state.applyBlock(block: block) {
|
||||
hadFailure = true
|
||||
}
|
||||
} else if subChainId == 1 {
|
||||
state.applyBroadcastBlock(block: block)
|
||||
}
|
||||
}
|
||||
return (state.takeOutgoingBroadcastBlocks(), state.getEmojiState() ?? Data(), state.getParticipants(), state.getParticipantLatencies())
|
||||
return (state.takeOutgoingBroadcastBlocks(), state.getEmojiState() ?? Data(), state.getParticipants(), state.getParticipantLatencies(), hadFailure)
|
||||
} else {
|
||||
if subChainId == 0 {
|
||||
guard let block = blocks.last else {
|
||||
return ([], Data(), [], [:])
|
||||
return ([], Data(), [], [:], false)
|
||||
}
|
||||
guard let state = initializeState(keyPair, userId, block) else {
|
||||
return ([], Data(), [], [:])
|
||||
return ([], Data(), [], [:], false)
|
||||
}
|
||||
callState.state = state
|
||||
for block in callState.pendingIncomingBroadcastBlocks {
|
||||
state.applyBroadcastBlock(block: block)
|
||||
}
|
||||
callState.pendingIncomingBroadcastBlocks.removeAll()
|
||||
return (state.takeOutgoingBroadcastBlocks(), state.getEmojiState() ?? Data(), state.getParticipants(), state.getParticipantLatencies())
|
||||
return (state.takeOutgoingBroadcastBlocks(), state.getEmojiState() ?? Data(), state.getParticipants(), state.getParticipantLatencies(), false)
|
||||
} else if subChainId == 1 {
|
||||
callState.pendingIncomingBroadcastBlocks.append(contentsOf: blocks)
|
||||
return ([], Data(), [], [:])
|
||||
return ([], Data(), [], [:], false)
|
||||
} else {
|
||||
return ([], Data(), [], [:])
|
||||
return ([], Data(), [], [:], false)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -209,6 +213,10 @@ public final class ConferenceCallE2EContext {
|
|||
#if DEBUG
|
||||
print("Latencies: \(participantLatencies)")
|
||||
#endif
|
||||
|
||||
if hadFailure {
|
||||
self.isFailed.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
private func e2ePoll(subChainId: Int) {
|
||||
|
|
@ -454,6 +462,12 @@ public final class ConferenceCallE2EContext {
|
|||
return impl.blockchainParticipantsValue.get().start(next: subscriber.putNext)
|
||||
}
|
||||
}
|
||||
|
||||
public var isFailed: Signal<Bool, NoError> {
|
||||
return self.impl.signalWith { impl, subscriber in
|
||||
return impl.isFailed.get().start(next: subscriber.putNext)
|
||||
}
|
||||
}
|
||||
|
||||
public init(engine: TelegramEngine, callId: Int64, accessHash: Int64, userId: Int64, reference: InternalGroupCallReference, keyPair: TelegramKeyPair, initializeState: @escaping (TelegramKeyPair, Int64, Data) -> ConferenceCallE2EContextState?) {
|
||||
let queue = Queue.mainQueue()
|
||||
|
|
|
|||
|
|
@ -199,8 +199,8 @@ private func synchronizeEmojiKeywords(postbox: Postbox, transaction: Transaction
|
|||
var items: [EmojiKeywordItem] = []
|
||||
var index: Int32 = 0
|
||||
for apiEmojiKeyword in keywords {
|
||||
if case let .emojiKeyword(keyword, emoticons) = apiEmojiKeyword, !emoticons.isEmpty {
|
||||
let keyword = keyword.replacingOccurrences(of: " ", with: "")
|
||||
if case let .emojiKeyword(fullKeyword, emoticons) = apiEmojiKeyword, !emoticons.isEmpty {
|
||||
let keyword = fullKeyword
|
||||
let indexKeys = stringIndexTokens(keyword, transliteration: .none).map { $0.toMemoryBuffer() }
|
||||
let item = EmojiKeywordItem(index: ItemCollectionItemIndex(index: index, id: keywordCollectionItemId(keyword, inputLanguageCode: operation.inputLanguageCode)), collectionId: collectionId.id, keyword: keyword, emoticons: emoticons, indexKeys: indexKeys)
|
||||
items.append(item)
|
||||
|
|
|
|||
|
|
@ -1687,6 +1687,11 @@ public final class GroupCallParticipantsContext {
|
|||
return self.memberEventsPipe.signal()
|
||||
}
|
||||
|
||||
private let isFailedEventPromise = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
public var isFailedEvent: Signal<Bool, NoError> {
|
||||
return self.isFailedEventPromise.get()
|
||||
}
|
||||
|
||||
private var updateQueue: [Update.StateUpdate] = []
|
||||
private var isProcessingUpdate: Bool = false
|
||||
private let disposable = MetaDisposable()
|
||||
|
|
@ -1715,6 +1720,7 @@ public final class GroupCallParticipantsContext {
|
|||
public private(set) var serviceState: ServiceState
|
||||
|
||||
private var e2eStateUpdateDisposable: Disposable?
|
||||
private var e2eIsFailedDisposable: Disposable?
|
||||
private var pendingBlockchainState: [ResolvedBlockchainParticipant]?
|
||||
private var pendingApplyBlockchainStateTimer: Foundation.Timer?
|
||||
|
||||
|
|
@ -1889,6 +1895,18 @@ public final class GroupCallParticipantsContext {
|
|||
self.applyPendingBlockchainState()
|
||||
}
|
||||
})
|
||||
|
||||
self.e2eIsFailedDisposable = (e2eContext.isFailed
|
||||
|> filter { $0 }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] isFailed in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if isFailed {
|
||||
self.isFailedEventPromise.set(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1902,6 +1920,7 @@ public final class GroupCallParticipantsContext {
|
|||
self.resetInviteLinksDisposable.dispose()
|
||||
self.subscribeDisposable.dispose()
|
||||
self.e2eStateUpdateDisposable?.dispose()
|
||||
self.e2eIsFailedDisposable?.dispose()
|
||||
self.pendingApplyBlockchainStateTimer?.invalidate()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2394,10 +2394,16 @@ func _internal_updatePeerStoriesHidden(account: Account, id: PeerId, isHidden: B
|
|||
guard let peer = transaction.getPeer(id) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let user = peer as? TelegramUser {
|
||||
updatePeersCustom(transaction: transaction, peers: [user.withUpdatedStoriesHidden(isHidden)], update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
if isHidden {
|
||||
if !transaction.isPeerContact(peerId: user.id) {
|
||||
let _ = _internal_removeRecentPeer(account: account, peerId: id).startStandalone()
|
||||
}
|
||||
}
|
||||
} else if let channel = peer as? TelegramChannel {
|
||||
updatePeersCustom(transaction: transaction, peers: [channel.withUpdatedStoriesHidden(isHidden)], update: { _, updated in
|
||||
return updated
|
||||
|
|
|
|||
|
|
@ -760,20 +760,14 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
if suggestedPost != nil {
|
||||
let backgroundFrame = contentFrame
|
||||
|
||||
if item.context.sharedContext.energyUsageSettings.fullTranslucency {
|
||||
if strongSelf.backgroundNode == nil {
|
||||
if let backgroundNode = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
|
||||
strongSelf.backgroundNode = backgroundNode
|
||||
backgroundNode.addSubnode(strongSelf.backgroundColorNode)
|
||||
strongSelf.insertSubnode(backgroundNode, at: 0)
|
||||
}
|
||||
}
|
||||
strongSelf.backgroundColorNode.isHidden = true
|
||||
} else {
|
||||
if strongSelf.backgroundMaskNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.backgroundMaskNode, at: 0)
|
||||
if strongSelf.backgroundNode == nil {
|
||||
if let backgroundNode = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
|
||||
strongSelf.backgroundNode = backgroundNode
|
||||
backgroundNode.addSubnode(strongSelf.backgroundColorNode)
|
||||
strongSelf.insertSubnode(backgroundNode, at: 0)
|
||||
}
|
||||
}
|
||||
strongSelf.backgroundColorNode.isHidden = true
|
||||
|
||||
if let backgroundNode = strongSelf.backgroundNode {
|
||||
backgroundNode.clipsToBounds = true
|
||||
|
|
@ -810,20 +804,14 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
//strongSelf.spoilerTextNode?.visibilityRect = subRect
|
||||
}
|
||||
} else if let (offset, image) = backgroundMaskImage {
|
||||
if item.context.sharedContext.energyUsageSettings.fullTranslucency {
|
||||
if strongSelf.backgroundNode == nil {
|
||||
if let backgroundNode = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
|
||||
strongSelf.backgroundNode = backgroundNode
|
||||
backgroundNode.addSubnode(strongSelf.backgroundColorNode)
|
||||
strongSelf.insertSubnode(backgroundNode, at: 0)
|
||||
}
|
||||
}
|
||||
strongSelf.backgroundColorNode.isHidden = true
|
||||
} else {
|
||||
if strongSelf.backgroundMaskNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.backgroundMaskNode, at: 0)
|
||||
if strongSelf.backgroundNode == nil {
|
||||
if let backgroundNode = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
|
||||
strongSelf.backgroundNode = backgroundNode
|
||||
backgroundNode.addSubnode(strongSelf.backgroundColorNode)
|
||||
strongSelf.insertSubnode(backgroundNode, at: 0)
|
||||
}
|
||||
}
|
||||
strongSelf.backgroundColorNode.isHidden = true
|
||||
|
||||
if backgroundMaskUpdated {
|
||||
if let backgroundNode = strongSelf.backgroundNode {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/Gifts/GiftItemComponent",
|
||||
"//submodules/Utils/RangeSet",
|
||||
"//submodules/MediaResources",
|
||||
"//submodules/UIKitRuntimeUtils",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import TextNodeWithEntities
|
|||
import RangeSet
|
||||
import GiftItemComponent
|
||||
import MediaResources
|
||||
import UIKitRuntimeUtils
|
||||
|
||||
private struct FetchControls {
|
||||
let fetch: (Bool) -> Void
|
||||
|
|
@ -3198,7 +3199,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||
dateAndStatusNode.isHidden = true
|
||||
}
|
||||
|
||||
let view: UIView?
|
||||
var view: UIView?
|
||||
if let strongSelf = self, strongSelf.imageNode.captureProtected {
|
||||
let imageView = UIImageView()
|
||||
imageView.contentMode = .scaleToFill
|
||||
|
|
@ -3207,9 +3208,13 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||
if imageView.layer.contents == nil {
|
||||
imageView.layer.contents = imageView.image?.cgImage
|
||||
}
|
||||
setLayerDisableScreenshots(imageView.layer, true)
|
||||
strongSelf.imageNode.view.superview?.insertSubview(imageView, aboveSubview: strongSelf.imageNode.view)
|
||||
|
||||
view = self?.view.snapshotContentTree(unhide: true)
|
||||
if let view {
|
||||
setLayerDisableScreenshots(view.layer, true)
|
||||
}
|
||||
imageView.removeFromSuperview()
|
||||
} else {
|
||||
view = self?.view.snapshotContentTree(unhide: true)
|
||||
|
|
|
|||
|
|
@ -620,12 +620,12 @@ public final class AccountContextImpl: AccountContext {
|
|||
|
||||
public func joinGroupCall(peerId: PeerId, invite: String?, requestJoinAsPeerId: ((@escaping (PeerId?) -> Void) -> Void)?, activeCall: EngineGroupCallDescription) {
|
||||
let callResult = self.sharedContext.callManager?.joinGroupCall(context: self, peerId: peerId, invite: invite, requestJoinAsPeerId: requestJoinAsPeerId, initialCall: activeCall, endCurrentIfAny: false)
|
||||
if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult {
|
||||
if currentPeerId == peerId {
|
||||
if let callResult = callResult, case let .alreadyInProgress(currentCallType) = callResult {
|
||||
if case let .peer(currentPeerId) = currentCallType, currentPeerId == peerId {
|
||||
self.sharedContext.navigateToCurrentCall()
|
||||
} else {
|
||||
let dataInput: Signal<(EnginePeer?, EnginePeer?), NoError>
|
||||
if let currentPeerId = currentPeerId {
|
||||
if case let .peer(currentPeerId) = currentCallType, let currentPeerId {
|
||||
dataInput = self.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: currentPeerId)
|
||||
|
|
@ -710,9 +710,9 @@ public final class AccountContextImpl: AccountContext {
|
|||
endCurrentIfAny: false,
|
||||
unmuteByDefault: unmuteByDefault
|
||||
)
|
||||
if case let .alreadyInProgress(currentPeerId) = result {
|
||||
if case let .alreadyInProgress(currentCallType) = result {
|
||||
let dataInput: Signal<EnginePeer?, NoError>
|
||||
if let currentPeerId {
|
||||
if case let .peer(currentPeerId) = currentCallType, let currentPeerId {
|
||||
dataInput = self.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: currentPeerId)
|
||||
)
|
||||
|
|
@ -785,6 +785,31 @@ public final class AccountContextImpl: AccountContext {
|
|||
)
|
||||
})]), on: .root)
|
||||
}
|
||||
} else if case .peer = currentCallType {
|
||||
let text: String
|
||||
//TODO:localize
|
||||
text = "End current call and start a conference?";
|
||||
strongSelf.sharedContext.mainWindow?.present(textAlertController(context: strongSelf, title: presentationData.strings.Call_CallInProgressTitle, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = callManager.joinConferenceCall(
|
||||
accountContext: self,
|
||||
initialCall: EngineGroupCallDescription(
|
||||
id: call.id,
|
||||
accessHash: call.accessHash,
|
||||
title: nil,
|
||||
scheduleTimestamp: nil,
|
||||
subscribedToScheduled: false,
|
||||
isStream: false
|
||||
),
|
||||
reference: call.reference,
|
||||
beginWithVideo: isVideo,
|
||||
invitePeerIds: [],
|
||||
endCurrentIfAny: true,
|
||||
unmuteByDefault: unmuteByDefault
|
||||
)
|
||||
})]), on: .root)
|
||||
} else {
|
||||
strongSelf.sharedContext.mainWindow?.present(textAlertController(context: strongSelf, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_ExternalCallInProgressMessage, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
|
||||
})]), on: .root)
|
||||
|
|
@ -798,13 +823,13 @@ public final class AccountContextImpl: AccountContext {
|
|||
return
|
||||
}
|
||||
|
||||
if case let .alreadyInProgress(currentPeerId) = callResult {
|
||||
if currentPeerId == peerId {
|
||||
if case let .alreadyInProgress(currentCallType) = callResult {
|
||||
if case let .peer(currentPeerId) = currentCallType, currentPeerId == peerId {
|
||||
completion()
|
||||
self.sharedContext.navigateToCurrentCall()
|
||||
} else {
|
||||
let dataInput: Signal<(EnginePeer?, EnginePeer?), NoError>
|
||||
if let currentPeerId = currentPeerId {
|
||||
if case let .peer(currentPeerId) = currentCallType, let currentPeerId {
|
||||
dataInput = self.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: currentPeerId)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (NSDictionary<NSNumber *, NSNumber *> *)participantLatencies;
|
||||
|
||||
- (void)applyBlock:(NSData *)block;
|
||||
- (bool)applyBlock:(NSData *)block;
|
||||
- (void)applyBroadcastBlock:(NSData *)block;
|
||||
|
||||
- (nullable NSData *)generateRemoveParticipantsBlock:(NSArray<NSNumber *> *)participantIds;
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ static NSString *hexStringFromData(NSData *data) {
|
|||
return @{};
|
||||
}
|
||||
|
||||
- (void)applyBlock:(NSData *)block {
|
||||
- (bool)applyBlock:(NSData *)block {
|
||||
std::string mappedBlock((uint8_t *)block.bytes, ((uint8_t *)block.bytes) + block.length);
|
||||
|
||||
#if DEBUG
|
||||
|
|
@ -260,8 +260,9 @@ static NSString *hexStringFromData(NSData *data) {
|
|||
|
||||
auto result = tde2e_api::call_apply_block(_callId, mappedBlock);
|
||||
if (!result.is_ok()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
- (void)applyBroadcastBlock:(NSData *)block {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue