mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
d62c37adf1
@ -13805,6 +13805,13 @@ Sorry for the inconvenience.";
|
||||
"Notification.StarsGift.TransferToChannel" = "%1$@ transferred a unique collectible to %2$@";
|
||||
"Notification.StarsGift.TransferToChannelYou" = "You transferred a unique collectible to %@";
|
||||
|
||||
"Gift.Convert.Success.ChannelText" = "**%1$@** were sent to channel's balance.";
|
||||
"Gift.Convert.Success.ChannelText.Stars_1" = "%@ Star";
|
||||
"Gift.Convert.Success.ChannelText.Stars_any" = "%@ Stars";
|
||||
|
||||
"Stars.Transfer.Terms" = "By purchasing you agree to the [Terms of Service]().";
|
||||
"Stars.Transfer.Terms_URL" = "https://telegram.org/tos/stars";
|
||||
|
||||
"AvatarEditor.PremiumNeeded.Background" = "Subscribe to Telegram Premium to choose this background.";
|
||||
"AvatarEditor.PremiumNeeded.Emoji" = "Subscribe to Telegram Premium to choose this emoji.";
|
||||
"AvatarEditor.PremiumNeeded.Sticker" = "Subscribe to Telegram Premium to choose this sticker.";
|
||||
@ -13981,3 +13988,7 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Privacy.Review.Invite.Title" = "Invitation Settings";
|
||||
"Privacy.Review.Invite.Text" = "You've restricted who can message you, but anyone can still invite you to groups and channels. Would you like to review these settings?";
|
||||
|
||||
"Conversation.VideoTimeLinkCopied" = "Link with start time at %@ copied to clipboard.";
|
||||
"Share.VideoStartAt" = "Start at %@";
|
||||
"SendStarReactions.SubtitleFrom" = "from %@";
|
||||
|
@ -1 +1 @@
|
||||
2510
|
||||
2515
|
||||
|
@ -48,6 +48,7 @@ public final class OpenChatMessageParams {
|
||||
public let gallerySource: GalleryControllerItemSource?
|
||||
public let centralItemUpdated: ((MessageId) -> Void)?
|
||||
public let getSourceRect: (() -> CGRect?)?
|
||||
public let blockInteraction: Promise<Bool>
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
@ -109,5 +110,6 @@ public final class OpenChatMessageParams {
|
||||
self.gallerySource = gallerySource
|
||||
self.centralItemUpdated = centralItemUpdated
|
||||
self.getSourceRect = getSourceRect
|
||||
self.blockInteraction = Promise()
|
||||
}
|
||||
}
|
||||
|
@ -3566,12 +3566,24 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
}
|
||||
})
|
||||
}, openStories: { peerId, avatarNode in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.interaction.openStories?(peerId, avatarNode)
|
||||
}, openPublicPosts: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.interaction.switchToFilter(.publicPosts)
|
||||
}, openMessagesFilter: { sourceNode in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.openMessagesFilter(sourceNode: sourceNode)
|
||||
}, switchMessagesFilter: { filter in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.searchScopePromise.set(.everywhere)
|
||||
})
|
||||
strongSelf.currentEntries = newEntries
|
||||
|
@ -100,7 +100,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
case enableReactionOverrides(Bool)
|
||||
case storiesExperiment(Bool)
|
||||
case storiesJpegExperiment(Bool)
|
||||
case playlistPlayback(Bool)
|
||||
case conferenceDebug(Bool)
|
||||
case enableQuickReactionSwitch(Bool)
|
||||
case disableReloginTokens(Bool)
|
||||
case liveStreamV2(Bool)
|
||||
@ -133,7 +133,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return DebugControllerSection.web.rawValue
|
||||
case .keepChatNavigationStack, .skipReadHistory, .dustEffect, .crashOnSlowQueries, .crashOnMemoryPressure:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .storiesExperiment, .storiesJpegExperiment, .playlistPlayback, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .rippleEffect, .browserExperiment, .localTranscription, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2, .experimentalCallMute, .playerV2, .devRequests, .fakeAds, .enableLocalTranslation:
|
||||
case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .storiesExperiment, .storiesJpegExperiment, .conferenceDebug, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .rippleEffect, .browserExperiment, .localTranscription, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2, .experimentalCallMute, .playerV2, .devRequests, .fakeAds, .enableLocalTranslation:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .logTranslationRecognition, .resetTranslationStates:
|
||||
return DebugControllerSection.translation.rawValue
|
||||
@ -242,7 +242,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return 47
|
||||
case .disableReloginTokens:
|
||||
return 48
|
||||
case .playlistPlayback:
|
||||
case .conferenceDebug:
|
||||
return 49
|
||||
case .enableQuickReactionSwitch:
|
||||
return 50
|
||||
@ -1308,12 +1308,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
})
|
||||
}).start()
|
||||
})
|
||||
case let .playlistPlayback(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Playlist Playback", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
case let .conferenceDebug(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Conference Debug", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
||||
settings.playlistPlayback = value
|
||||
settings.conferenceDebug = value
|
||||
return PreferencesEntry(settings)
|
||||
})
|
||||
}).start()
|
||||
@ -1540,7 +1540,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
||||
entries.append(.storiesJpegExperiment(experimentalSettings.storiesJpegExperiment))
|
||||
entries.append(.disableReloginTokens(experimentalSettings.disableReloginTokens))
|
||||
}
|
||||
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
|
||||
entries.append(.conferenceDebug(experimentalSettings.conferenceDebug))
|
||||
entries.append(.enableQuickReactionSwitch(!experimentalSettings.disableQuickReaction))
|
||||
entries.append(.liveStreamV2(experimentalSettings.liveStreamV2))
|
||||
entries.append(.experimentalCallMute(experimentalSettings.experimentalCallMute))
|
||||
|
@ -1755,7 +1755,6 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let text: String
|
||||
if let timestamp {
|
||||
//TODO:localize
|
||||
let startTimeString: String
|
||||
let hours = timestamp / (60 * 60)
|
||||
let minutes = timestamp % (60 * 60) / 60
|
||||
@ -1765,7 +1764,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
||||
} else {
|
||||
startTimeString = String(format: "%d:%02d", minutes, seconds)
|
||||
}
|
||||
text = "Link with start time at \(startTimeString) copied to clipboard."
|
||||
text = presentationData.strings.Conversation_VideoTimeLinkCopied(startTimeString).string
|
||||
} else {
|
||||
text = presentationData.strings.Conversation_LinkCopied
|
||||
}
|
||||
|
@ -1076,7 +1076,6 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
|
||||
private var customUnembedWhenPortrait: ((OverlayMediaItemNode) -> Bool)?
|
||||
|
||||
private var pictureInPictureContent: AnyObject?
|
||||
private var nativePictureInPictureContent: AnyObject?
|
||||
|
||||
private var activePictureInPictureNavigationController: NavigationController?
|
||||
@ -1544,10 +1543,6 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
strongSelf.videoNode?.setBaseRate(playbackRate)
|
||||
}
|
||||
}
|
||||
|
||||
if strongSelf.nativePictureInPictureContent == nil {
|
||||
strongSelf.setupNativePictureInPicture()
|
||||
}
|
||||
}
|
||||
}
|
||||
self.videoNode = videoNode
|
||||
@ -2963,6 +2958,15 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
}
|
||||
|
||||
@objc func pictureInPictureButtonPressed() {
|
||||
if self.nativePictureInPictureContent == nil {
|
||||
self.setupNativePictureInPicture()
|
||||
}
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
if let currentPictureInPictureNode = self.context.sharedContext.mediaManager.currentPictureInPictureNode as? UniversalVideoGalleryItemNode, let currentItem = currentPictureInPictureNode.item, case let .message(currentMessage, _) = currentItem.contentInfo, case let .message(message, _) = self.item?.contentInfo, currentMessage.id == message.id {
|
||||
if let controller = self.galleryController() as? GalleryController {
|
||||
controller.dismiss(forceAway: true)
|
||||
@ -2978,6 +2982,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func expandPIP() {
|
||||
if #available(iOS 15.0, *) {
|
||||
|
@ -524,6 +524,24 @@ private func chatMessageVideoDatas(postbox: Postbox, userLocation: MediaResource
|
||||
thumbnail = .single(decodedThumbnailData)
|
||||
}
|
||||
} else if let thumbnailResource = thumbnailResource {
|
||||
if autoFetchFullSizeThumbnail, let thumbnailRepresentation = thumbnailRepresentation, (thumbnailRepresentation.dimensions.width > 200 || thumbnailRepresentation.dimensions.height > 200) {
|
||||
thumbnail = Signal { subscriber in
|
||||
let fetchedDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: customUserContentType ?? MediaResourceUserContentType(file: fileReference.media), reference: fileReference.resourceReference(thumbnailRepresentation.resource), statsCategory: .video).start()
|
||||
let thumbnailDisposable = postbox.mediaBox.resourceData(thumbnailRepresentation.resource, attemptSynchronously: synchronousLoad).start(next: { next in
|
||||
let data: Data? = next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: [])
|
||||
if let data {
|
||||
subscriber.putNext(data)
|
||||
} else {
|
||||
subscriber.putNext(nil)
|
||||
}
|
||||
}, error: subscriber.putError, completed: subscriber.putCompletion)
|
||||
|
||||
return ActionDisposable {
|
||||
fetchedDisposable.dispose()
|
||||
thumbnailDisposable.dispose()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
thumbnail = Signal { subscriber in
|
||||
let fetchedDisposable = fetchedMediaResource(mediaBox: postbox.mediaBox, userLocation: userLocation, userContentType: customUserContentType ?? MediaResourceUserContentType(file: fileReference.media), reference: fileReference.resourceReference(thumbnailResource), statsCategory: .video).start()
|
||||
let thumbnailDisposable = postbox.mediaBox.resourceData(thumbnailResource, attemptSynchronously: synchronousLoad).start(next: { next in
|
||||
@ -535,6 +553,7 @@ private func chatMessageVideoDatas(postbox: Postbox, userLocation: MediaResource
|
||||
thumbnailDisposable.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
thumbnail = .single(nil)
|
||||
}
|
||||
|
@ -16,7 +16,8 @@ swift_library(
|
||||
"//submodules/GZip:GZip",
|
||||
"//submodules/rlottie:RLottieBinding",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/ManagedAnimationNode:ManagedAnimationNode"
|
||||
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
|
||||
"//submodules/Components/HierarchyTrackingLayer",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -6,6 +6,7 @@ import SwiftSignalKit
|
||||
import RLottieBinding
|
||||
import GZip
|
||||
import AppBundle
|
||||
import HierarchyTrackingLayer
|
||||
|
||||
public enum SemanticStatusNodeState: Equatable {
|
||||
public struct ProgressAppearance: Equatable {
|
||||
@ -90,7 +91,7 @@ private func svgPath(_ path: StaticString, scale: CGPoint = CGPoint(x: 1.0, y: 1
|
||||
}
|
||||
|
||||
private extension SemanticStatusNodeState {
|
||||
func context(current: SemanticStatusNodeStateContext?) -> SemanticStatusNodeStateContext {
|
||||
func context(current: SemanticStatusNodeStateContext?, animated: Bool) -> SemanticStatusNodeStateContext {
|
||||
switch self {
|
||||
case .none, .download, .play, .pause, .customIcon:
|
||||
let icon: SemanticStatusNodeIcon
|
||||
@ -114,7 +115,7 @@ private extension SemanticStatusNodeState {
|
||||
if current.icon == icon {
|
||||
return current
|
||||
} else if (current.icon == .play && icon == .pause) || (current.icon == .pause && icon == .play) {
|
||||
current.icon = icon
|
||||
current.setIcon(icon: icon, animated: animated)
|
||||
return current
|
||||
} else {
|
||||
return SemanticStatusNodeIconContext(icon: icon)
|
||||
@ -376,6 +377,8 @@ public final class SemanticStatusNode: ASControlNode {
|
||||
private var stateContext: SemanticStatusNodeStateContext
|
||||
private var appearanceContext: SemanticStatusNodeAppearanceContext
|
||||
|
||||
private let hierarchyTrackingLayer: HierarchyTrackingLayer
|
||||
|
||||
private var disposable: Disposable?
|
||||
private var backgroundNodeImage: UIImage?
|
||||
|
||||
@ -391,13 +394,16 @@ public final class SemanticStatusNode: ASControlNode {
|
||||
|
||||
public init(backgroundNodeColor: UIColor, foregroundNodeColor: UIColor, image: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? = nil, overlayForegroundNodeColor: UIColor? = nil, cutout: CGRect? = nil) {
|
||||
self.state = .none
|
||||
self.stateContext = self.state.context(current: nil)
|
||||
self.stateContext = self.state.context(current: nil, animated: false)
|
||||
self.appearanceContext = SemanticStatusNodeAppearanceContext(background: backgroundNodeColor, foreground: foregroundNodeColor, backgroundImage: nil, overlayForeground: overlayForegroundNodeColor, cutout: cutout)
|
||||
self.hierarchyTrackingLayer = HierarchyTrackingLayer()
|
||||
|
||||
super.init()
|
||||
|
||||
self.layer.addSublayer(self.hierarchyTrackingLayer)
|
||||
|
||||
self.isOpaque = false
|
||||
self.displaysAsynchronously = true
|
||||
self.displaysAsynchronously = false
|
||||
|
||||
if let image {
|
||||
self.setBackgroundImage(image, size: CGSize(width: 44.0, height: 44.0))
|
||||
@ -420,7 +426,6 @@ public final class SemanticStatusNode: ASControlNode {
|
||||
animate = true
|
||||
}
|
||||
}
|
||||
|
||||
if self.stateContext.isAnimating {
|
||||
animate = true
|
||||
}
|
||||
@ -449,12 +454,15 @@ public final class SemanticStatusNode: ASControlNode {
|
||||
self.hasState = true
|
||||
animated = false
|
||||
}
|
||||
if !self.hierarchyTrackingLayer.isInHierarchy {
|
||||
animated = false
|
||||
}
|
||||
if self.state != state || self.appearanceContext.cutout != cutout {
|
||||
self.state = state
|
||||
let previousStateContext = self.stateContext
|
||||
let previousAppearanceContext = updateCutout ? self.appearanceContext : nil
|
||||
|
||||
self.stateContext = self.state.context(current: self.stateContext)
|
||||
self.stateContext = self.state.context(current: self.stateContext, animated: animated)
|
||||
self.stateContext.requestUpdate = { [weak self] in
|
||||
self?.setNeedsDisplay()
|
||||
}
|
||||
|
@ -131,11 +131,7 @@ final class SemanticStatusNodeIconContext: SemanticStatusNodeStateContext {
|
||||
}
|
||||
}
|
||||
|
||||
var icon: SemanticStatusNodeIcon {
|
||||
didSet {
|
||||
self.animationNode?.enqueueState(self.icon == .play ? .play : .pause, animated: self.iconImage != nil)
|
||||
}
|
||||
}
|
||||
private(set) var icon: SemanticStatusNodeIcon
|
||||
|
||||
private var animationNode: PlayPauseIconNode?
|
||||
private var iconImage: UIImage?
|
||||
@ -171,6 +167,11 @@ final class SemanticStatusNodeIconContext: SemanticStatusNodeStateContext {
|
||||
|
||||
var requestUpdate: () -> Void = {}
|
||||
|
||||
func setIcon(icon: SemanticStatusNodeIcon, animated: Bool) {
|
||||
self.icon = icon
|
||||
self.animationNode?.enqueueState(self.icon == .play ? .play : .pause, animated: animated)
|
||||
}
|
||||
|
||||
func drawingState(transitionFraction: CGFloat) -> SemanticStatusNodeStateDrawingState {
|
||||
return DrawingState(transitionFraction: transitionFraction, icon: self.icon, iconImage: self.iconImage, iconOffset: self.iconOffset)
|
||||
}
|
||||
|
@ -472,8 +472,7 @@ final class ShareControllerNode: ViewControllerTracingNode, ASScrollViewDelegate
|
||||
self.actionButtonNode.setBackgroundImage(highlightedHalfRoundedBackground, for: .highlighted)
|
||||
|
||||
if let startAtTimestamp = mediaParameters?.startAtTimestamp {
|
||||
//TODO:localize
|
||||
self.startAtTimestampNode = ShareStartAtTimestampNode(titleText: "Start at \(textForTimeout(value: startAtTimestamp))", titleTextColor: self.presentationData.theme.actionSheet.secondaryTextColor, checkNodeTheme: CheckNodeTheme(backgroundColor: presentationData.theme.list.itemCheckColors.fillColor, strokeColor: presentationData.theme.list.itemCheckColors.foregroundColor, borderColor: presentationData.theme.list.itemCheckColors.strokeColor, overlayBorder: false, hasInset: false, hasShadow: false))
|
||||
self.startAtTimestampNode = ShareStartAtTimestampNode(titleText: self.presentationData.strings.Share_VideoStartAt(textForTimeout(value: startAtTimestamp)).string, titleTextColor: self.presentationData.theme.actionSheet.secondaryTextColor, checkNodeTheme: CheckNodeTheme(backgroundColor: presentationData.theme.list.itemCheckColors.fillColor, strokeColor: presentationData.theme.list.itemCheckColors.foregroundColor, borderColor: presentationData.theme.list.itemCheckColors.strokeColor, overlayBorder: false, hasInset: false, hasShadow: false))
|
||||
} else {
|
||||
self.startAtTimestampNode = nil
|
||||
}
|
||||
|
@ -167,6 +167,14 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
||||
self.conferenceAddParticipant?()
|
||||
}
|
||||
|
||||
var isConferencePossible = false
|
||||
if self.call.context.sharedContext.immediateExperimentalUISettings.conferenceDebug {
|
||||
isConferencePossible = true
|
||||
}
|
||||
if let data = self.call.context.currentAppConfiguration.with({ $0 }).data, let value = data["ios_enable_conference"] as? Double {
|
||||
isConferencePossible = value != 0.0
|
||||
}
|
||||
|
||||
self.callScreenState = PrivateCallScreen.State(
|
||||
strings: presentationData.strings,
|
||||
lifecycleState: .connecting,
|
||||
@ -180,7 +188,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
||||
remoteVideo: nil,
|
||||
isRemoteBatteryLow: false,
|
||||
isEnergySavingEnabled: !self.sharedContext.energyUsageSettings.fullTranslucency,
|
||||
isConferencePossible: true
|
||||
isConferencePossible: isConferencePossible
|
||||
)
|
||||
|
||||
self.isMicrophoneMutedDisposable = (call.isMuted
|
||||
@ -520,6 +528,12 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
||||
}
|
||||
|
||||
if var callScreenState = self.callScreenState {
|
||||
if callScreenState.remoteVideo == nil && self.remoteVideo != nil {
|
||||
if let call = self.call as? PresentationCallImpl, let sharedAudioContext = call.sharedAudioContext, case .builtin = sharedAudioContext.currentAudioOutputValue {
|
||||
call.playRemoteCameraTone()
|
||||
}
|
||||
}
|
||||
|
||||
callScreenState.lifecycleState = mappedLifecycleState
|
||||
callScreenState.remoteVideo = self.remoteVideo
|
||||
callScreenState.localVideo = self.localVideo
|
||||
|
@ -15,13 +15,14 @@ import AccountContext
|
||||
import DeviceProximity
|
||||
import PhoneNumberFormat
|
||||
|
||||
final class SharedCallAudioContext {
|
||||
public final class SharedCallAudioContext {
|
||||
let audioDevice: OngoingCallContext.AudioDevice?
|
||||
let callKitIntegration: CallKitIntegration?
|
||||
|
||||
private var audioSessionDisposable: Disposable?
|
||||
private var audioSessionShouldBeActiveDisposable: Disposable?
|
||||
private var isAudioSessionActiveDisposable: Disposable?
|
||||
private var audioOutputStateDisposable: Disposable?
|
||||
|
||||
private(set) var audioSessionControl: ManagedAudioSessionControl?
|
||||
|
||||
@ -32,7 +33,7 @@ final class SharedCallAudioContext {
|
||||
|
||||
private let audioOutputStatePromise = Promise<([AudioSessionOutput], AudioSessionOutput?)>(([], nil))
|
||||
private var audioOutputStateValue: ([AudioSessionOutput], AudioSessionOutput?) = ([], nil)
|
||||
private var currentAudioOutputValue: AudioSessionOutput = .builtin
|
||||
public private(set) var currentAudioOutputValue: AudioSessionOutput = .builtin
|
||||
private var didSetCurrentAudioOutputValue: Bool = false
|
||||
var audioOutputState: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> {
|
||||
return self.audioOutputStatePromise.get()
|
||||
@ -141,12 +142,24 @@ final class SharedCallAudioContext {
|
||||
}
|
||||
self.audioDevice?.setIsAudioSessionActive(value)
|
||||
})
|
||||
|
||||
self.audioOutputStateDisposable = (self.audioOutputStatePromise.get()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.audioOutputStateValue = value
|
||||
if let currentOutput = value.1 {
|
||||
self.currentAudioOutputValue = currentOutput
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.audioSessionDisposable?.dispose()
|
||||
self.audioSessionShouldBeActiveDisposable?.dispose()
|
||||
self.isAudioSessionActiveDisposable?.dispose()
|
||||
self.audioOutputStateDisposable?.dispose()
|
||||
}
|
||||
|
||||
func setCurrentAudioOutput(_ output: AudioSessionOutput) {
|
||||
@ -201,7 +214,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
private let currentNetworkType: NetworkType
|
||||
private let updatedNetworkType: Signal<NetworkType, NoError>
|
||||
|
||||
private var sharedAudioContext: SharedCallAudioContext?
|
||||
public private(set) var sharedAudioContext: SharedCallAudioContext?
|
||||
|
||||
private var sessionState: CallSession?
|
||||
private var callContextState: OngoingCallContextState?
|
||||
@ -1610,6 +1623,29 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
self.useFrontCamera = !self.useFrontCamera
|
||||
self.videoCapturer?.switchVideoInput(isFront: self.useFrontCamera)
|
||||
}
|
||||
|
||||
public func playRemoteCameraTone() {
|
||||
let name: String
|
||||
name = "voip_group_recording_started.mp3"
|
||||
|
||||
self.beginTone(tone: .custom(name: name, loopCount: 1))
|
||||
}
|
||||
|
||||
private func beginTone(tone: PresentationCallTone?) {
|
||||
if let tone, let toneData = presentationCallToneData(tone) {
|
||||
if let sharedAudioContext = self.sharedAudioContext {
|
||||
sharedAudioContext.audioDevice?.setTone(tone: OngoingCallContext.Tone(
|
||||
samples: toneData,
|
||||
sampleRate: 48000,
|
||||
loopCount: tone.loopCount ?? 100000
|
||||
))
|
||||
}
|
||||
} else {
|
||||
if let sharedAudioContext = self.sharedAudioContext {
|
||||
sharedAudioContext.audioDevice?.setTone(tone: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sampleBufferFromPixelBuffer(pixelBuffer: CVPixelBuffer) -> CMSampleBuffer? {
|
||||
|
@ -264,7 +264,6 @@ private final class AvatarUploadToastScreenComponent: Component {
|
||||
containerSize: CGSize(width: availableContentSize.width - contentInsets.left - contentInsets.right - spacing - iconSize.width, height: availableContentSize.height)
|
||||
)
|
||||
|
||||
//TODO:localize
|
||||
let contentSize = self.content.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(AnimatedTextComponent(
|
||||
|
@ -153,7 +153,6 @@ public final class BatchVideoRenderingContext {
|
||||
for (id, targetContext) in self.targetContexts {
|
||||
if targetContext.target != nil {
|
||||
if targetContext.fetchDisposable == nil {
|
||||
//TODO:release pass resource reference
|
||||
targetContext.fetchDisposable = fetchedMediaResource(
|
||||
mediaBox: self.context.account.postbox.mediaBox,
|
||||
userLocation: targetContext.userLocation,
|
||||
|
@ -613,7 +613,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu
|
||||
if let previousParams = self.params, case .active = params.state.lifecycleState {
|
||||
switch previousParams.state.lifecycleState {
|
||||
case .requesting, .ringing, .connecting, .reconnecting:
|
||||
if self.hideEmojiTooltipTimer == nil && !self.areControlsHidden {
|
||||
if self.hideEmojiTooltipTimer == nil && !self.areControlsHidden && self.activeRemoteVideoSource == nil && self.activeLocalVideoSource == nil {
|
||||
self.displayEmojiTooltip = true
|
||||
|
||||
self.hideEmojiTooltipTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false, block: { [weak self] _ in
|
||||
|
@ -43,6 +43,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon",
|
||||
"//submodules/AnimatedCountLabelNode",
|
||||
"//submodules/AudioWaveform",
|
||||
"//submodules/DeviceProximity",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -34,6 +34,7 @@ import ChatMessageItemCommon
|
||||
import TelegramStringFormatting
|
||||
import AnimatedCountLabelNode
|
||||
import AudioWaveform
|
||||
import DeviceProximity
|
||||
|
||||
private struct FetchControls {
|
||||
let fetch: (Bool) -> Void
|
||||
@ -1561,6 +1562,12 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
guard let arguments = self.arguments else {
|
||||
return
|
||||
}
|
||||
|
||||
var animated = animated
|
||||
if DeviceProximityManager.shared().currentValue() {
|
||||
animated = false
|
||||
}
|
||||
|
||||
let incoming = message.effectivelyIncoming(context.account.peerId)
|
||||
let messageTheme = incoming ? presentationData.theme.theme.chat.message.incoming : presentationData.theme.theme.chat.message.outgoing
|
||||
|
||||
|
@ -58,7 +58,10 @@ public class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
openChatMessageMode = .automaticPlayback
|
||||
}
|
||||
|
||||
let _ = item.controllerInteraction.openMessage(item.message, OpenMessageParams(mode: openChatMessageMode, mediaIndex: self.mediaIndex, progress: self.itemNode?.makeProgress()))
|
||||
if !item.controllerInteraction.isOpeningMedia {
|
||||
let params = OpenMessageParams(mode: openChatMessageMode, mediaIndex: self.mediaIndex, progress: self.itemNode?.makeProgress())
|
||||
let _ = item.controllerInteraction.openMessage(item.message, params)
|
||||
}
|
||||
}
|
||||
|
||||
self.interactiveImageNode.activateAgeRestrictedMedia = { [weak self] in
|
||||
|
@ -1794,11 +1794,10 @@ private final class ChatSendStarsScreenComponent: Component {
|
||||
|
||||
let titleSubtitleSpacing: CGFloat = 1.0
|
||||
|
||||
//TODO:localize
|
||||
let subtitleSize = self.subtitle.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: "from \(currentMyPeer.compactDisplayTitle)", font: Font.regular(12.0), textColor: environment.theme.list.itemSecondaryTextColor))
|
||||
text: .plain(NSAttributedString(string: environment.strings.SendStarReactions_SubtitleFrom(currentMyPeer.compactDisplayTitle).string, font: Font.regular(12.0), textColor: environment.theme.list.itemSecondaryTextColor))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - leftButtonFrame.maxX * 2.0, height: 100.0)
|
||||
|
@ -303,6 +303,29 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
|
||||
public var chatIsRotated: Bool = true
|
||||
public var canReadHistory: Bool = false
|
||||
|
||||
private var isOpeningMediaValue: Bool = false
|
||||
public var isOpeningMedia: Bool {
|
||||
return self.isOpeningMediaValue
|
||||
}
|
||||
private var isOpeningMediaDisposable: Disposable?
|
||||
public var isOpeningMediaSignal: Signal<Bool, NoError>? {
|
||||
didSet {
|
||||
self.isOpeningMediaDisposable?.dispose()
|
||||
self.isOpeningMediaDisposable = nil
|
||||
self.isOpeningMediaValue = false
|
||||
|
||||
if let isOpeningMediaSignal = self.isOpeningMediaSignal {
|
||||
self.isOpeningMediaValue = true
|
||||
self.isOpeningMediaDisposable = (isOpeningMediaSignal |> filter { !$0 } |> take(1) |> timeout(1.0, queue: .mainQueue(), alternate: .single(false)) |> deliverOnMainQueue).startStrict(next: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.isOpeningMediaValue = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public init(
|
||||
openMessage: @escaping (Message, OpenMessageParams) -> Bool,
|
||||
openPeer: @escaping (EnginePeer, ChatControllerInteractionNavigateToPeer, MessageReference?, OpenPeerSource) -> Void,
|
||||
@ -538,4 +561,8 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
|
||||
|
||||
self.presentationContext = presentationContext
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.isOpeningMediaDisposable?.dispose()
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,10 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode {
|
||||
}
|
||||
|
||||
var accumulatedRightButtonOffset: CGFloat = canBeExpanded ? 16.0 : 0.0
|
||||
for (_, button) in self.rightButtonNodes {
|
||||
for spec in self.currentRightButtons.reversed() {
|
||||
guard let button = self.rightButtonNodes[spec.key] else {
|
||||
continue
|
||||
}
|
||||
button.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: canBeExpanded, transition: transition)
|
||||
transition.updateSublayerTransformOffset(layer: button.layer, offset: CGPoint(x: accumulatedRightButtonOffset, y: 0.0))
|
||||
if self.backgroundContentColor.alpha != 0.0 {
|
||||
@ -174,6 +177,7 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode {
|
||||
}
|
||||
}
|
||||
|
||||
var accumulatedRightButtonOffset: CGFloat = self.canBeExpanded ? 16.0 : 0.0
|
||||
if self.currentRightButtons != rightButtons || presentationData.strings !== self.presentationData?.strings {
|
||||
self.currentRightButtons = rightButtons
|
||||
|
||||
@ -225,7 +229,10 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode {
|
||||
buttonNode.alpha = 0.0
|
||||
transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor)
|
||||
|
||||
transition.updateSublayerTransformOffset(layer: buttonNode.layer, offset: CGPoint(x: canBeExpanded ? 16.0 : 0.0, y: 0.0))
|
||||
transition.updateSublayerTransformOffset(layer: buttonNode.layer, offset: CGPoint(x: accumulatedRightButtonOffset, y: 0.0))
|
||||
if self.backgroundContentColor.alpha != 0.0 {
|
||||
accumulatedRightButtonOffset -= 6.0
|
||||
}
|
||||
} else {
|
||||
transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame)
|
||||
transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor)
|
||||
|
@ -887,29 +887,33 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
let controllerInteraction = ChatControllerInteraction(openMessage: { [weak self] message, params in
|
||||
guard let strongSelf = self, strongSelf.isNodeLoaded, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) else {
|
||||
guard let self, self.isNodeLoaded, let message = self.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) else {
|
||||
return false
|
||||
}
|
||||
|
||||
let mode = params.mode
|
||||
|
||||
let displayVoiceMessageDiscardAlert: () -> Bool = {
|
||||
if strongSelf.presentVoiceMessageDiscardAlert(action: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
Queue.mainQueue().after(0.1, {
|
||||
let _ = strongSelf.controllerInteraction?.openMessage(message, params)
|
||||
})
|
||||
let displayVoiceMessageDiscardAlert: () -> Bool = { [weak self] in
|
||||
guard let self else {
|
||||
return true
|
||||
}
|
||||
if self.presentVoiceMessageDiscardAlert(action: { [weak self] in
|
||||
Queue.mainQueue().after(0.1, {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = self.controllerInteraction?.openMessage(message, params)
|
||||
})
|
||||
}, performAction: false) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
strongSelf.commitPurposefulAction()
|
||||
strongSelf.dismissAllTooltips()
|
||||
self.commitPurposefulAction()
|
||||
self.dismissAllTooltips()
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
self.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
|
||||
var openMessageByAction = false
|
||||
var isLocation = false
|
||||
@ -923,9 +927,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
if let file = media as? TelegramMediaFile {
|
||||
if file.isInstantVideo {
|
||||
if strongSelf.chatDisplayNode.isInputViewFocused {
|
||||
strongSelf.returnInputViewFocus = true
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
if self.chatDisplayNode.isInputViewFocused {
|
||||
self.returnInputViewFocus = true
|
||||
self.chatDisplayNode.dismissInput()
|
||||
}
|
||||
}
|
||||
if file.isMusic || file.isVoice || file.isInstantVideo {
|
||||
@ -934,7 +938,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
if (file.isVoice || file.isInstantVideo) && message.minAutoremoveOrClearTimeout == viewOnceTimeout {
|
||||
strongSelf.openViewOnceMediaMessage(message)
|
||||
self.openViewOnceMediaMessage(message)
|
||||
return false
|
||||
}
|
||||
} else if file.isVideo {
|
||||
@ -947,7 +951,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
switch extendedMedia {
|
||||
case .preview:
|
||||
if displayVoiceMessageDiscardAlert() {
|
||||
strongSelf.controllerInteraction?.openCheckoutOrReceipt(message.id, params)
|
||||
self.controllerInteraction?.openCheckoutOrReceipt(message.id, params)
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -959,7 +963,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
switch extendedMedia {
|
||||
case .preview:
|
||||
if displayVoiceMessageDiscardAlert() {
|
||||
strongSelf.controllerInteraction?.openCheckoutOrReceipt(message.id, nil)
|
||||
self.controllerInteraction?.openCheckoutOrReceipt(message.id, nil)
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -969,15 +973,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
} else if media is TelegramMediaGiveaway || media is TelegramMediaGiveawayResults {
|
||||
let progress = params.progress
|
||||
let presentationData = strongSelf.presentationData
|
||||
let presentationData = self.presentationData
|
||||
|
||||
var signal = strongSelf.context.engine.payments.premiumGiveawayInfo(peerId: message.id.peerId, messageId: message.id)
|
||||
var signal = self.context.engine.payments.premiumGiveawayInfo(peerId: message.id.peerId, messageId: message.id)
|
||||
let disposable: MetaDisposable
|
||||
if let current = strongSelf.giveawayStatusDisposable {
|
||||
if let current = self.giveawayStatusDisposable {
|
||||
disposable = current
|
||||
} else {
|
||||
disposable = MetaDisposable()
|
||||
strongSelf.giveawayStatusDisposable = disposable
|
||||
self.giveawayStatusDisposable = disposable
|
||||
}
|
||||
|
||||
let progressSignal = Signal<Never, NoError> { [weak self] subscriber in
|
||||
@ -1010,8 +1014,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
disposable.set((signal
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] info in
|
||||
if let strongSelf = self, let info {
|
||||
strongSelf.displayGiveawayStatusInfo(messageId: message.id, giveawayInfo: info)
|
||||
if let self, let info {
|
||||
self.displayGiveawayStatusInfo(messageId: message.id, giveawayInfo: info)
|
||||
}
|
||||
}))
|
||||
|
||||
@ -1024,22 +1028,22 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
case .pinnedMessageUpdated, .gameScore, .setSameChatWallpaper, .giveawayResults, .customText:
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? ReplyMessageAttribute {
|
||||
strongSelf.navigateToMessage(from: message.id, to: .id(attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote.flatMap { quote in NavigateToMessageParams.Quote(string: quote.text, offset: quote.offset) } : nil)))
|
||||
self.navigateToMessage(from: message.id, to: .id(attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote.flatMap { quote in NavigateToMessageParams.Quote(string: quote.text, offset: quote.offset) } : nil)))
|
||||
break
|
||||
}
|
||||
}
|
||||
case let .photoUpdated(image):
|
||||
openMessageByAction = image != nil
|
||||
case .groupPhoneCall, .inviteToGroupPhoneCall:
|
||||
if let activeCall = strongSelf.presentationInterfaceState.activeGroupCallInfo?.activeCall {
|
||||
strongSelf.joinGroupCall(peerId: message.id.peerId, invite: nil, activeCall: EngineGroupCallDescription(id: activeCall.id, accessHash: activeCall.accessHash, title: activeCall.title, scheduleTimestamp: activeCall.scheduleTimestamp, subscribedToScheduled: activeCall.subscribedToScheduled, isStream: activeCall.isStream))
|
||||
if let activeCall = self.presentationInterfaceState.activeGroupCallInfo?.activeCall {
|
||||
self.joinGroupCall(peerId: message.id.peerId, invite: nil, activeCall: EngineGroupCallDescription(id: activeCall.id, accessHash: activeCall.accessHash, title: activeCall.title, scheduleTimestamp: activeCall.scheduleTimestamp, subscribedToScheduled: activeCall.subscribedToScheduled, isStream: activeCall.isStream))
|
||||
} else {
|
||||
var canManageGroupCalls = false
|
||||
if let channel = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel {
|
||||
if let channel = self.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel {
|
||||
if channel.flags.contains(.isCreator) || channel.hasPermission(.manageCalls) {
|
||||
canManageGroupCalls = true
|
||||
}
|
||||
} else if let group = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramGroup {
|
||||
} else if let group = self.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramGroup {
|
||||
if case .creator = group.role {
|
||||
canManageGroupCalls = true
|
||||
} else if case let .admin(rights, _) = group.role {
|
||||
@ -1051,80 +1055,80 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
if canManageGroupCalls {
|
||||
let text: String
|
||||
if let channel = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel, case .broadcast = channel.info {
|
||||
text = strongSelf.presentationData.strings.LiveStream_CreateNewVoiceChatText
|
||||
if let channel = self.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel, case .broadcast = channel.info {
|
||||
text = self.presentationData.strings.LiveStream_CreateNewVoiceChatText
|
||||
} else {
|
||||
text = strongSelf.presentationData.strings.VoiceChat_CreateNewVoiceChatText
|
||||
text = self.presentationData.strings.VoiceChat_CreateNewVoiceChatText
|
||||
}
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.VoiceChat_CreateNewVoiceChatStartNow, action: {
|
||||
if let strongSelf = self {
|
||||
self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.VoiceChat_CreateNewVoiceChatStartNow, action: { [weak self] in
|
||||
if let self {
|
||||
var dismissStatus: (() -> Void)?
|
||||
let statusController = OverlayStatusController(theme: strongSelf.presentationData.theme, type: .loading(cancelled: {
|
||||
let statusController = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: {
|
||||
dismissStatus?()
|
||||
}))
|
||||
dismissStatus = { [weak self, weak statusController] in
|
||||
self?.createVoiceChatDisposable.set(nil)
|
||||
statusController?.dismiss()
|
||||
}
|
||||
strongSelf.present(statusController, in: .window(.root))
|
||||
strongSelf.createVoiceChatDisposable.set((strongSelf.context.engine.calls.createGroupCall(peerId: message.id.peerId, title: nil, scheduleDate: nil, isExternalStream: false)
|
||||
self.present(statusController, in: .window(.root))
|
||||
self.createVoiceChatDisposable.set((self.context.engine.calls.createGroupCall(peerId: message.id.peerId, title: nil, scheduleDate: nil, isExternalStream: false)
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] info in
|
||||
guard let strongSelf = self else {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
strongSelf.joinGroupCall(peerId: message.id.peerId, invite: nil, activeCall: EngineGroupCallDescription(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: info.subscribedToScheduled, isStream: info.isStream))
|
||||
self.joinGroupCall(peerId: message.id.peerId, invite: nil, activeCall: EngineGroupCallDescription(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: info.subscribedToScheduled, isStream: info.isStream))
|
||||
}, error: { [weak self] error in
|
||||
dismissStatus?()
|
||||
|
||||
guard let strongSelf = self else {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let text: String
|
||||
switch error {
|
||||
case .generic, .scheduledTooLate:
|
||||
text = strongSelf.presentationData.strings.Login_UnknownError
|
||||
text = self.presentationData.strings.Login_UnknownError
|
||||
case .anonymousNotAllowed:
|
||||
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
|
||||
text = strongSelf.presentationData.strings.LiveStream_AnonymousDisabledAlertText
|
||||
text = self.presentationData.strings.LiveStream_AnonymousDisabledAlertText
|
||||
} else {
|
||||
text = strongSelf.presentationData.strings.VoiceChat_AnonymousDisabledAlertText
|
||||
text = self.presentationData.strings.VoiceChat_AnonymousDisabledAlertText
|
||||
}
|
||||
}
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}, completed: {
|
||||
dismissStatus?()
|
||||
}))
|
||||
}
|
||||
}), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.VoiceChat_CreateNewVoiceChatSchedule, action: {
|
||||
if let strongSelf = self {
|
||||
strongSelf.context.scheduleGroupCall(peerId: message.id.peerId, parentController: strongSelf)
|
||||
}), TextAlertAction(type: .genericAction, title: self.presentationData.strings.VoiceChat_CreateNewVoiceChatSchedule, action: { [weak self] in
|
||||
if let self {
|
||||
self.context.scheduleGroupCall(peerId: message.id.peerId, parentController: self)
|
||||
}
|
||||
}), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {})], actionLayout: .vertical), in: .window(.root))
|
||||
}), TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: {})], actionLayout: .vertical), in: .window(.root))
|
||||
}
|
||||
}
|
||||
return true
|
||||
case .messageAutoremoveTimeoutUpdated:
|
||||
var canSetupAutoremoveTimeout = false
|
||||
|
||||
if let _ = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat {
|
||||
if let _ = self.presentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat {
|
||||
canSetupAutoremoveTimeout = false
|
||||
} else if let group = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramGroup {
|
||||
} else if let group = self.presentationInterfaceState.renderedPeer?.peer as? TelegramGroup {
|
||||
if !group.hasBannedPermission(.banChangeInfo) {
|
||||
canSetupAutoremoveTimeout = true
|
||||
}
|
||||
} else if let user = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramUser {
|
||||
if user.id != strongSelf.context.account.peerId && user.botInfo == nil {
|
||||
} else if let user = self.presentationInterfaceState.renderedPeer?.peer as? TelegramUser {
|
||||
if user.id != self.context.account.peerId && user.botInfo == nil {
|
||||
canSetupAutoremoveTimeout = true
|
||||
}
|
||||
} else if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel {
|
||||
} else if let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel {
|
||||
if channel.hasPermission(.changeInfo) {
|
||||
canSetupAutoremoveTimeout = true
|
||||
}
|
||||
}
|
||||
|
||||
if canSetupAutoremoveTimeout {
|
||||
strongSelf.presentAutoremoveSetup()
|
||||
self.presentAutoremoveSetup()
|
||||
}
|
||||
case let .paymentSent(currency, _, _, _, _):
|
||||
if currency == "XTR" {
|
||||
@ -1136,14 +1140,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.push(self.context.sharedContext.makeStarsReceiptScreen(context: self.context, receipt: receipt))
|
||||
})
|
||||
} else {
|
||||
strongSelf.present(BotReceiptController(context: strongSelf.context, messageId: message.id), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
self.present(BotReceiptController(context: self.context, messageId: message.id), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}
|
||||
return true
|
||||
case .setChatTheme:
|
||||
strongSelf.presentThemeSelection()
|
||||
self.presentThemeSelection()
|
||||
return true
|
||||
case let .setChatWallpaper(wallpaper, _):
|
||||
guard let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
||||
guard let peer = self.presentationInterfaceState.renderedPeer?.peer else {
|
||||
return true
|
||||
}
|
||||
if let peer = peer as? TelegramChannel {
|
||||
@ -1158,11 +1162,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
return true
|
||||
}
|
||||
guard message.effectivelyIncoming(strongSelf.context.account.peerId), let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
||||
strongSelf.presentThemeSelection()
|
||||
guard message.effectivelyIncoming(self.context.account.peerId), let peer = self.presentationInterfaceState.renderedPeer?.peer else {
|
||||
self.presentThemeSelection()
|
||||
return true
|
||||
}
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
self.chatDisplayNode.dismissInput()
|
||||
var options = WallpaperPresentationOptions()
|
||||
var intensity: Int32?
|
||||
if let settings = wallpaper.settings {
|
||||
@ -1176,7 +1180,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
intensity = settings.intensity
|
||||
}
|
||||
}
|
||||
let wallpaperPreviewController = WallpaperGalleryController(context: strongSelf.context, source: .wallpaper(wallpaper, options, [], intensity, nil, nil), mode: .peer(EnginePeer(peer), true))
|
||||
let wallpaperPreviewController = WallpaperGalleryController(context: self.context, source: .wallpaper(wallpaper, options, [], intensity, nil, nil), mode: .peer(EnginePeer(peer), true))
|
||||
wallpaperPreviewController.apply = { [weak wallpaperPreviewController] entry, options, _, _, brightness, forBoth in
|
||||
var settings: WallpaperSettings?
|
||||
if case let .wallpaper(wallpaper, _) = entry {
|
||||
@ -1189,69 +1193,69 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
settings = WallpaperSettings(blur: options.contains(.blur), motion: options.contains(.motion), colors: baseSettings?.colors ?? [], intensity: intensity, rotation: baseSettings?.rotation)
|
||||
}
|
||||
let _ = (strongSelf.context.engine.themes.setExistingChatWallpaper(messageId: message.id, settings: settings, forBoth: forBoth)
|
||||
let _ = (self.context.engine.themes.setExistingChatWallpaper(messageId: message.id, settings: settings, forBoth: forBoth)
|
||||
|> deliverOnMainQueue).startStandalone()
|
||||
Queue.mainQueue().after(0.1) {
|
||||
wallpaperPreviewController?.dismiss()
|
||||
}
|
||||
}
|
||||
strongSelf.push(wallpaperPreviewController)
|
||||
self.push(wallpaperPreviewController)
|
||||
return true
|
||||
case let .giftPremium(_, _, duration, _, _, _, _):
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
let fromPeerId: PeerId = message.author?.id == strongSelf.context.account.peerId ? strongSelf.context.account.peerId : message.id.peerId
|
||||
let toPeerId: PeerId = message.author?.id == strongSelf.context.account.peerId ? message.id.peerId : strongSelf.context.account.peerId
|
||||
let controller = PremiumIntroScreen(context: strongSelf.context, source: .gift(from: fromPeerId, to: toPeerId, duration: duration, giftCode: nil))
|
||||
strongSelf.push(controller)
|
||||
self.chatDisplayNode.dismissInput()
|
||||
let fromPeerId: PeerId = message.author?.id == self.context.account.peerId ? self.context.account.peerId : message.id.peerId
|
||||
let toPeerId: PeerId = message.author?.id == self.context.account.peerId ? message.id.peerId : self.context.account.peerId
|
||||
let controller = PremiumIntroScreen(context: self.context, source: .gift(from: fromPeerId, to: toPeerId, duration: duration, giftCode: nil))
|
||||
self.push(controller)
|
||||
return true
|
||||
case .starGift, .starGiftUnique:
|
||||
let controller = strongSelf.context.sharedContext.makeGiftViewScreen(context: strongSelf.context, message: EngineMessage(message), shareStory: { [weak self] uniqueGift in
|
||||
if let self {
|
||||
let controller = self.context.sharedContext.makeGiftViewScreen(context: self.context, message: EngineMessage(message), shareStory: { [weak self] uniqueGift in
|
||||
Queue.mainQueue().after(0.15) {
|
||||
if let self {
|
||||
let controller = self.context.sharedContext.makeStorySharingScreen(context: self.context, subject: .gift(uniqueGift), parentController: self)
|
||||
self.push(controller)
|
||||
}
|
||||
}
|
||||
})
|
||||
strongSelf.push(controller)
|
||||
self.push(controller)
|
||||
return true
|
||||
case .giftStars:
|
||||
let controller = strongSelf.context.sharedContext.makeStarsGiftScreen(context: strongSelf.context, message: EngineMessage(message))
|
||||
strongSelf.push(controller)
|
||||
let controller = self.context.sharedContext.makeStarsGiftScreen(context: self.context, message: EngineMessage(message))
|
||||
self.push(controller)
|
||||
return true
|
||||
case let .giftCode(slug, _, _, _, _, _, _, _, _, _, _):
|
||||
strongSelf.openResolved(result: .premiumGiftCode(slug: slug), sourceMessageId: message.id, progress: params.progress)
|
||||
self.openResolved(result: .premiumGiftCode(slug: slug), sourceMessageId: message.id, progress: params.progress)
|
||||
return true
|
||||
case .prizeStars:
|
||||
let controller = strongSelf.context.sharedContext.makeStarsGiftScreen(context: strongSelf.context, message: EngineMessage(message))
|
||||
strongSelf.push(controller)
|
||||
let controller = self.context.sharedContext.makeStarsGiftScreen(context: self.context, message: EngineMessage(message))
|
||||
self.push(controller)
|
||||
return true
|
||||
case let .suggestedProfilePhoto(image):
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
self.chatDisplayNode.dismissInput()
|
||||
if let image = image {
|
||||
if message.effectivelyIncoming(strongSelf.context.account.peerId) {
|
||||
if message.effectivelyIncoming(self.context.account.peerId) {
|
||||
if let emojiMarkup = image.emojiMarkup {
|
||||
let controller = AvatarEditorScreen(context: strongSelf.context, inputData: AvatarEditorScreen.inputData(context: strongSelf.context, isGroup: false), peerType: .user, markup: emojiMarkup)
|
||||
let controller = AvatarEditorScreen(context: self.context, inputData: AvatarEditorScreen.inputData(context: self.context, isGroup: false), peerType: .user, markup: emojiMarkup)
|
||||
controller.imageCompletion = { [weak self] image, commit in
|
||||
if let strongSelf = self {
|
||||
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||
if let self {
|
||||
if let rootController = self.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||
settingsController.updateProfilePhoto(image, mode: .accept, uploadStatus: nil)
|
||||
commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
controller.videoCompletion = { [weak self] image, url, values, markup, commit in
|
||||
if let strongSelf = self {
|
||||
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||
if let self {
|
||||
if let rootController = self.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||
settingsController.updateProfileVideo(image, video: nil, values: nil, markup: markup, mode: .accept, uploadStatus: nil)
|
||||
commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
strongSelf.push(controller)
|
||||
self.push(controller)
|
||||
} else {
|
||||
var selectedNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
||||
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
if let result = itemNode.transitionNode(id: message.id, media: image, adjustRect: false) {
|
||||
selectedNode = result
|
||||
@ -1267,17 +1271,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
senderName = nil
|
||||
}
|
||||
|
||||
legacyAvatarEditor(context: strongSelf.context, media: .message(message: MessageReference(message), media: image), transitionView: transitionView, senderName: senderName, present: { [weak self] c, a in
|
||||
legacyAvatarEditor(context: self.context, media: .message(message: MessageReference(message), media: image), transitionView: transitionView, senderName: senderName, present: { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
}, imageCompletion: { [weak self] image in
|
||||
if let strongSelf = self {
|
||||
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||
if let self {
|
||||
if let rootController = self.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||
settingsController.updateProfilePhoto(image, mode: .accept, uploadStatus: nil)
|
||||
}
|
||||
}
|
||||
}, videoCompletion: { [weak self] image, url, adjustments in
|
||||
if let strongSelf = self {
|
||||
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||
if let self {
|
||||
if let rootController = self.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||
settingsController.oldUpdateProfileVideo(image, asset: AVURLAsset(url: url), adjustments: adjustments, mode: .accept)
|
||||
}
|
||||
}
|
||||
@ -1288,7 +1292,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
case .boostsApplied:
|
||||
strongSelf.controllerInteraction?.openGroupBoostInfo(nil, 0)
|
||||
self.controllerInteraction?.openGroupBoostInfo(nil, 0)
|
||||
return true
|
||||
default:
|
||||
break
|
||||
@ -1299,29 +1303,33 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
let openChatLocation = strongSelf.chatLocation
|
||||
let openChatLocation = self.chatLocation
|
||||
var chatFilterTag: MemoryBuffer?
|
||||
if case let .customTag(value, _) = strongSelf.chatDisplayNode.historyNode.tag {
|
||||
if case let .customTag(value, _) = self.chatDisplayNode.historyNode.tag {
|
||||
chatFilterTag = value
|
||||
}
|
||||
|
||||
var standalone = false
|
||||
if case .customChatContents = strongSelf.chatLocation {
|
||||
if case .customChatContents = self.chatLocation {
|
||||
standalone = true
|
||||
}
|
||||
|
||||
if let adAttribute = message.attributes.first(where: { $0 is AdMessageAttribute }) as? AdMessageAttribute {
|
||||
if let file = message.media.first(where: { $0 is TelegramMediaFile}) as? TelegramMediaFile, file.isVideo && !file.isAnimated {
|
||||
strongSelf.chatDisplayNode.historyNode.adMessagesContext?.markAction(opaqueId: adAttribute.opaqueId, media: true, fullscreen: false)
|
||||
self.chatDisplayNode.historyNode.adMessagesContext?.markAction(opaqueId: adAttribute.opaqueId, media: true, fullscreen: false)
|
||||
} else {
|
||||
strongSelf.controllerInteraction?.activateAdAction(message.id, nil, true, false)
|
||||
self.controllerInteraction?.activateAdAction(message.id, nil, true, false)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, updatedPresentationData: strongSelf.updatedPresentationData, chatLocation: openChatLocation, chatFilterTag: chatFilterTag, chatLocationContextHolder: strongSelf.chatLocationContextHolder, message: message, mediaIndex: params.mediaIndex, standalone: standalone, reverseMessageGalleryOrder: false, mode: mode, navigationController: strongSelf.effectiveNavigationController, dismissInput: {
|
||||
let openChatMessageParams = OpenChatMessageParams(context: context, updatedPresentationData: self.updatedPresentationData, chatLocation: openChatLocation, chatFilterTag: chatFilterTag, chatLocationContextHolder: self.chatLocationContextHolder, message: message, mediaIndex: params.mediaIndex, standalone: standalone, reverseMessageGalleryOrder: false, mode: mode, navigationController: self.effectiveNavigationController, dismissInput: { [weak self] in
|
||||
self?.chatDisplayNode.dismissInput()
|
||||
}, present: { c, a, i in
|
||||
}, present: { [weak self] c, a, i in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
if case .current = i {
|
||||
c.presentationArguments = a
|
||||
c.statusBar.alphaUpdated = { [weak self] transition in
|
||||
@ -1330,14 +1338,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
self.updateStatusBarPresentation(animated: transition.isAnimated)
|
||||
}
|
||||
self?.galleryPresentationContext.present(c, on: PresentationSurfaceLevel(rawValue: 0), blockInteraction: true, completion: {})
|
||||
self.galleryPresentationContext.present(c, on: PresentationSurfaceLevel(rawValue: 0), blockInteraction: true, completion: {})
|
||||
} else {
|
||||
self?.present(c, in: .window(.root), with: a, blockInteraction: true)
|
||||
self.present(c, in: .window(.root), with: a, blockInteraction: true)
|
||||
}
|
||||
}, transitionNode: { messageId, media, adjustRect in
|
||||
}, transitionNode: { [weak self] messageId, media, adjustRect in
|
||||
var selectedNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
||||
if let strongSelf = self {
|
||||
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let self {
|
||||
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
if let result = itemNode.transitionNode(id: messageId, media: media, adjustRect: adjustRect) {
|
||||
selectedNode = result
|
||||
@ -1346,27 +1354,27 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
return selectedNode
|
||||
}, addToTransitionSurface: { view in
|
||||
guard let strongSelf = self else {
|
||||
}, addToTransitionSurface: { [weak self] view in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
strongSelf.chatDisplayNode.historyNode.view.superview?.insertSubview(view, aboveSubview: strongSelf.chatDisplayNode.historyNode.view)
|
||||
}, openUrl: { url in
|
||||
self.chatDisplayNode.historyNode.view.superview?.insertSubview(view, aboveSubview: self.chatDisplayNode.historyNode.view)
|
||||
}, openUrl: { [weak self] url in
|
||||
self?.openUrl(url, concealed: false, skipConcealedAlert: isLocation, message: nil)
|
||||
}, openPeer: { peer, navigation in
|
||||
}, openPeer: { [weak self] peer, navigation in
|
||||
self?.openPeer(peer: EnginePeer(peer), navigation: navigation, fromMessage: nil)
|
||||
}, callPeer: { peerId, isVideo in
|
||||
}, callPeer: { [weak self] peerId, isVideo in
|
||||
self?.controllerInteraction?.callPeer(peerId, isVideo)
|
||||
}, enqueueMessage: { message in
|
||||
}, enqueueMessage: { [weak self] message in
|
||||
self?.sendMessages([message])
|
||||
}, sendSticker: canSendMessagesToChat(strongSelf.presentationInterfaceState) ? { fileReference, sourceNode, sourceRect in
|
||||
}, sendSticker: canSendMessagesToChat(self.presentationInterfaceState) ? { [weak self] fileReference, sourceNode, sourceRect in
|
||||
return self?.controllerInteraction?.sendSticker(fileReference, false, false, nil, false, sourceNode, sourceRect, nil, []) ?? false
|
||||
} : nil, sendEmoji: canSendMessagesToChat(strongSelf.presentationInterfaceState) ? { text, attribute in
|
||||
} : nil, sendEmoji: canSendMessagesToChat(self.presentationInterfaceState) ? { [weak self] text, attribute in
|
||||
self?.controllerInteraction?.sendEmoji(text, attribute, false)
|
||||
} : nil, setupTemporaryHiddenMedia: { signal, centralIndex, galleryMedia in
|
||||
if let strongSelf = self {
|
||||
strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).startStrict(next: { entry in
|
||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
||||
} : nil, setupTemporaryHiddenMedia: { [weak self] signal, centralIndex, galleryMedia in
|
||||
if let self {
|
||||
self.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).startStrict(next: { [weak self] entry in
|
||||
if let self, let controllerInteraction = self.controllerInteraction {
|
||||
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
||||
|
||||
if let entry = entry as? InstantPageGalleryEntry, entry.index == centralIndex {
|
||||
@ -1375,7 +1383,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
controllerInteraction.hiddenMedia = messageIdAndMedia
|
||||
|
||||
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
itemNode.updateHiddenMedia()
|
||||
}
|
||||
@ -1383,10 +1391,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}))
|
||||
}
|
||||
}, chatAvatarHiddenMedia: { signal, media in
|
||||
if let strongSelf = self {
|
||||
strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).startStrict(next: { messageId in
|
||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
||||
}, chatAvatarHiddenMedia: { [weak self] signal, media in
|
||||
if let self {
|
||||
self.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).startStrict(next: { [weak self] messageId in
|
||||
if let self, let controllerInteraction = self.controllerInteraction {
|
||||
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
||||
|
||||
if let messageId = messageId {
|
||||
@ -1395,7 +1403,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
controllerInteraction.hiddenMedia = messageIdAndMedia
|
||||
|
||||
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
itemNode.updateHiddenMedia()
|
||||
}
|
||||
@ -1405,54 +1413,54 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}, actionInteraction: GalleryControllerActionInteraction(
|
||||
openUrl: { [weak self] url, concealed in
|
||||
if let strongSelf = self {
|
||||
strongSelf.openUrl(url, concealed: concealed, message: nil)
|
||||
if let self {
|
||||
self.openUrl(url, concealed: concealed, message: nil)
|
||||
}
|
||||
}, openUrlIn: { [weak self] url in
|
||||
if let strongSelf = self {
|
||||
strongSelf.openUrlIn(url)
|
||||
if let self {
|
||||
self.openUrlIn(url)
|
||||
}
|
||||
}, openPeerMention: { [weak self] mention in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerInteraction?.openPeerMention(mention, nil)
|
||||
if let self {
|
||||
self.controllerInteraction?.openPeerMention(mention, nil)
|
||||
}
|
||||
}, openPeer: { [weak self] peer in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerInteraction?.openPeer(peer, .default, nil, .default)
|
||||
if let self {
|
||||
self.controllerInteraction?.openPeer(peer, .default, nil, .default)
|
||||
}
|
||||
}, openHashtag: { [weak self] peerName, hashtag in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerInteraction?.openHashtag(peerName, hashtag)
|
||||
if let self {
|
||||
self.controllerInteraction?.openHashtag(peerName, hashtag)
|
||||
}
|
||||
}, openBotCommand: { [weak self] command in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerInteraction?.sendBotCommand(nil, command)
|
||||
if let self {
|
||||
self.controllerInteraction?.sendBotCommand(nil, command)
|
||||
}
|
||||
}, openAd: { [weak self] messageId in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerInteraction?.activateAdAction(messageId, nil, true, true)
|
||||
if let self {
|
||||
self.controllerInteraction?.activateAdAction(messageId, nil, true, true)
|
||||
}
|
||||
}, addContact: { [weak self] phoneNumber in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerInteraction?.addContact(phoneNumber)
|
||||
if let self {
|
||||
self.controllerInteraction?.addContact(phoneNumber)
|
||||
}
|
||||
}, storeMediaPlaybackState: { [weak self] messageId, timestamp, playbackRate in
|
||||
guard let strongSelf = self else {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
var storedState: MediaPlaybackStoredState?
|
||||
if let timestamp = timestamp {
|
||||
storedState = MediaPlaybackStoredState(timestamp: timestamp, playbackRate: AudioPlaybackRate(playbackRate))
|
||||
}
|
||||
let _ = updateMediaPlaybackStoredStateInteractively(engine: strongSelf.context.engine, messageId: messageId, state: storedState).startStandalone()
|
||||
let _ = updateMediaPlaybackStoredStateInteractively(engine: self.context.engine, messageId: messageId, state: storedState).startStandalone()
|
||||
}, editMedia: { [weak self] messageId, snapshots, transitionCompletion in
|
||||
guard let strongSelf = self else {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId))
|
||||
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId))
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] message in
|
||||
guard let strongSelf = self, let message = message else {
|
||||
guard let self, let message = message else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -1472,17 +1480,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] {
|
||||
legacyMediaEditor(context: strongSelf.context, peer: peer, threadTitle: strongSelf.threadInfo?.title, media: mediaReference, mode: .draw, initialCaption: NSAttributedString(), snapshots: snapshots, transitionCompletion: {
|
||||
legacyMediaEditor(context: self.context, peer: peer, threadTitle: self.threadInfo?.title, media: mediaReference, mode: .draw, initialCaption: NSAttributedString(), snapshots: snapshots, transitionCompletion: {
|
||||
transitionCompletion()
|
||||
}, getCaptionPanelView: { [weak self] in
|
||||
return self?.getCaptionPanelView(isFile: false)
|
||||
}, sendMessagesWithSignals: { [weak self] signals, _, _, isCaptionAbove in
|
||||
if let strongSelf = self {
|
||||
if let self {
|
||||
var parameters: ChatSendMessageActionSheetController.SendParameters?
|
||||
if isCaptionAbove {
|
||||
parameters = ChatSendMessageActionSheetController.SendParameters(effect: nil, textIsAboveMedia: true)
|
||||
}
|
||||
strongSelf.enqueueMediaMessages(signals: signals, silentPosting: false, parameters: parameters)
|
||||
self.enqueueMediaMessages(signals: signals, silentPosting: false, parameters: parameters)
|
||||
}
|
||||
}, present: { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
@ -1493,18 +1501,22 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self?.canReadHistory.set(canReadHistory)
|
||||
}),
|
||||
getSourceRect: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
guard let self else {
|
||||
return nil
|
||||
}
|
||||
var rect: CGRect?
|
||||
strongSelf.chatDisplayNode.historyNode.forEachVisibleMessageItemNode({ itemNode in
|
||||
self.chatDisplayNode.historyNode.forEachVisibleMessageItemNode({ itemNode in
|
||||
if itemNode.item?.message.id == message.id {
|
||||
rect = itemNode.view.convert(itemNode.contentFrame(), to: nil)
|
||||
}
|
||||
})
|
||||
return rect
|
||||
}
|
||||
))
|
||||
)
|
||||
|
||||
self.controllerInteraction?.isOpeningMediaSignal = openChatMessageParams.blockInteraction.get()
|
||||
|
||||
return context.sharedContext.openChatMessage(openChatMessageParams)
|
||||
}, openPeer: { [weak self] peer, navigation, fromMessage, source in
|
||||
var expandAvatar = false
|
||||
if case let .groupParticipant(storyStats, avatarHeaderNode) = source {
|
||||
|
@ -323,8 +323,12 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
params.blockInteraction.set(.single(true))
|
||||
|
||||
let _ = (gallery
|
||||
|> deliverOnMainQueue).startStandalone(next: { gallery in
|
||||
params.blockInteraction.set(.single(false))
|
||||
|
||||
gallery.centralItemUpdated = { messageId in
|
||||
params.centralItemUpdated?(messageId)
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
public var knockoutWallpaper: Bool
|
||||
public var foldersTabAtBottom: Bool
|
||||
public var playerEmbedding: Bool
|
||||
public var playlistPlayback: Bool
|
||||
public var preferredVideoCodec: String?
|
||||
public var disableVideoAspectScaling: Bool
|
||||
public var enableVoipTcp: Bool
|
||||
@ -65,6 +64,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
public var playerV2: Bool
|
||||
public var devRequests: Bool
|
||||
public var fakeAds: Bool
|
||||
public var conferenceDebug: Bool
|
||||
|
||||
public static var defaultSettings: ExperimentalUISettings {
|
||||
return ExperimentalUISettings(
|
||||
@ -75,7 +75,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
knockoutWallpaper: false,
|
||||
foldersTabAtBottom: false,
|
||||
playerEmbedding: false,
|
||||
playlistPlayback: false,
|
||||
preferredVideoCodec: nil,
|
||||
disableVideoAspectScaling: false,
|
||||
enableVoipTcp: false,
|
||||
@ -107,7 +106,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
autoBenchmarkReflectors: nil,
|
||||
playerV2: false,
|
||||
devRequests: false,
|
||||
fakeAds: false
|
||||
fakeAds: false,
|
||||
conferenceDebug: false
|
||||
)
|
||||
}
|
||||
|
||||
@ -119,7 +119,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
knockoutWallpaper: Bool,
|
||||
foldersTabAtBottom: Bool,
|
||||
playerEmbedding: Bool,
|
||||
playlistPlayback: Bool,
|
||||
preferredVideoCodec: String?,
|
||||
disableVideoAspectScaling: Bool,
|
||||
enableVoipTcp: Bool,
|
||||
@ -151,7 +150,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
autoBenchmarkReflectors: Bool?,
|
||||
playerV2: Bool,
|
||||
devRequests: Bool,
|
||||
fakeAds: Bool
|
||||
fakeAds: Bool,
|
||||
conferenceDebug: Bool
|
||||
) {
|
||||
self.keepChatNavigationStack = keepChatNavigationStack
|
||||
self.skipReadHistory = skipReadHistory
|
||||
@ -160,7 +160,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.knockoutWallpaper = knockoutWallpaper
|
||||
self.foldersTabAtBottom = foldersTabAtBottom
|
||||
self.playerEmbedding = playerEmbedding
|
||||
self.playlistPlayback = playlistPlayback
|
||||
self.preferredVideoCodec = preferredVideoCodec
|
||||
self.disableVideoAspectScaling = disableVideoAspectScaling
|
||||
self.enableVoipTcp = enableVoipTcp
|
||||
@ -193,6 +192,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.playerV2 = playerV2
|
||||
self.devRequests = devRequests
|
||||
self.fakeAds = fakeAds
|
||||
self.conferenceDebug = conferenceDebug
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
@ -205,7 +205,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.knockoutWallpaper = (try container.decodeIfPresent(Int32.self, forKey: "knockoutWallpaper") ?? 0) != 0
|
||||
self.foldersTabAtBottom = (try container.decodeIfPresent(Int32.self, forKey: "foldersTabAtBottom") ?? 0) != 0
|
||||
self.playerEmbedding = (try container.decodeIfPresent(Int32.self, forKey: "playerEmbedding") ?? 0) != 0
|
||||
self.playlistPlayback = (try container.decodeIfPresent(Int32.self, forKey: "playlistPlayback") ?? 0) != 0
|
||||
self.preferredVideoCodec = try container.decodeIfPresent(String.self.self, forKey: "preferredVideoCodec")
|
||||
self.disableVideoAspectScaling = (try container.decodeIfPresent(Int32.self, forKey: "disableVideoAspectScaling") ?? 0) != 0
|
||||
self.enableVoipTcp = (try container.decodeIfPresent(Int32.self, forKey: "enableVoipTcp") ?? 0) != 0
|
||||
@ -238,6 +237,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.playerV2 = try container.decodeIfPresent(Bool.self, forKey: "playerV2") ?? false
|
||||
self.devRequests = try container.decodeIfPresent(Bool.self, forKey: "devRequests") ?? false
|
||||
self.fakeAds = try container.decodeIfPresent(Bool.self, forKey: "fakeAds") ?? false
|
||||
self.conferenceDebug = try container.decodeIfPresent(Bool.self, forKey: "conferenceDebug") ?? false
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
@ -250,7 +250,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
try container.encode((self.knockoutWallpaper ? 1 : 0) as Int32, forKey: "knockoutWallpaper")
|
||||
try container.encode((self.foldersTabAtBottom ? 1 : 0) as Int32, forKey: "foldersTabAtBottom")
|
||||
try container.encode((self.playerEmbedding ? 1 : 0) as Int32, forKey: "playerEmbedding")
|
||||
try container.encode((self.playlistPlayback ? 1 : 0) as Int32, forKey: "playlistPlayback")
|
||||
try container.encodeIfPresent(self.preferredVideoCodec, forKey: "preferredVideoCodec")
|
||||
try container.encode((self.disableVideoAspectScaling ? 1 : 0) as Int32, forKey: "disableVideoAspectScaling")
|
||||
try container.encode((self.enableVoipTcp ? 1 : 0) as Int32, forKey: "enableVoipTcp")
|
||||
@ -283,6 +282,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
try container.encodeIfPresent(self.playerV2, forKey: "playerV2")
|
||||
try container.encodeIfPresent(self.devRequests, forKey: "devRequests")
|
||||
try container.encodeIfPresent(self.fakeAds, forKey: "fakeAds")
|
||||
try container.encodeIfPresent(self.conferenceDebug, forKey: "conferenceDebug")
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user