Various improvements

(cherry picked from commit 7e240c7064)
This commit is contained in:
Isaac 2025-07-08 15:59:17 +04:00
parent 20a85a4ff7
commit 636508fc97
21 changed files with 177 additions and 89 deletions

View file

@ -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 {

View file

@ -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__);
}

View file

@ -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))

View file

@ -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)

View file

@ -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 {

View file

@ -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)

View file

@ -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()

View file

@ -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()
}
}
}

View file

@ -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)

View file

@ -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 {

View file

@ -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([

View file

@ -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()

View file

@ -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)

View file

@ -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()
}

View file

@ -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

View file

@ -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 {

View file

@ -44,6 +44,7 @@ swift_library(
"//submodules/TelegramUI/Components/Gifts/GiftItemComponent",
"//submodules/Utils/RangeSet",
"//submodules/MediaResources",
"//submodules/UIKitRuntimeUtils",
],
visibility = [
"//visibility:public",

View file

@ -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)

View file

@ -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)

View file

@ -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;

View file

@ -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 {