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.TransferToChannel" = "%1$@ transferred a unique collectible to %2$@";
|
||||||
"Notification.StarsGift.TransferToChannelYou" = "You transferred a unique collectible to %@";
|
"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.Background" = "Subscribe to Telegram Premium to choose this background.";
|
||||||
"AvatarEditor.PremiumNeeded.Emoji" = "Subscribe to Telegram Premium to choose this emoji.";
|
"AvatarEditor.PremiumNeeded.Emoji" = "Subscribe to Telegram Premium to choose this emoji.";
|
||||||
"AvatarEditor.PremiumNeeded.Sticker" = "Subscribe to Telegram Premium to choose this sticker.";
|
"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.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?";
|
"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 gallerySource: GalleryControllerItemSource?
|
||||||
public let centralItemUpdated: ((MessageId) -> Void)?
|
public let centralItemUpdated: ((MessageId) -> Void)?
|
||||||
public let getSourceRect: (() -> CGRect?)?
|
public let getSourceRect: (() -> CGRect?)?
|
||||||
|
public let blockInteraction: Promise<Bool>
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
@ -109,5 +110,6 @@ public final class OpenChatMessageParams {
|
|||||||
self.gallerySource = gallerySource
|
self.gallerySource = gallerySource
|
||||||
self.centralItemUpdated = centralItemUpdated
|
self.centralItemUpdated = centralItemUpdated
|
||||||
self.getSourceRect = getSourceRect
|
self.getSourceRect = getSourceRect
|
||||||
|
self.blockInteraction = Promise()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3566,12 +3566,24 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, openStories: { peerId, avatarNode in
|
}, openStories: { peerId, avatarNode in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
strongSelf.interaction.openStories?(peerId, avatarNode)
|
strongSelf.interaction.openStories?(peerId, avatarNode)
|
||||||
}, openPublicPosts: {
|
}, openPublicPosts: {
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
strongSelf.interaction.switchToFilter(.publicPosts)
|
strongSelf.interaction.switchToFilter(.publicPosts)
|
||||||
}, openMessagesFilter: { sourceNode in
|
}, openMessagesFilter: { sourceNode in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
strongSelf.openMessagesFilter(sourceNode: sourceNode)
|
strongSelf.openMessagesFilter(sourceNode: sourceNode)
|
||||||
}, switchMessagesFilter: { filter in
|
}, switchMessagesFilter: { filter in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
strongSelf.searchScopePromise.set(.everywhere)
|
strongSelf.searchScopePromise.set(.everywhere)
|
||||||
})
|
})
|
||||||
strongSelf.currentEntries = newEntries
|
strongSelf.currentEntries = newEntries
|
||||||
|
@ -100,7 +100,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
case enableReactionOverrides(Bool)
|
case enableReactionOverrides(Bool)
|
||||||
case storiesExperiment(Bool)
|
case storiesExperiment(Bool)
|
||||||
case storiesJpegExperiment(Bool)
|
case storiesJpegExperiment(Bool)
|
||||||
case playlistPlayback(Bool)
|
case conferenceDebug(Bool)
|
||||||
case enableQuickReactionSwitch(Bool)
|
case enableQuickReactionSwitch(Bool)
|
||||||
case disableReloginTokens(Bool)
|
case disableReloginTokens(Bool)
|
||||||
case liveStreamV2(Bool)
|
case liveStreamV2(Bool)
|
||||||
@ -133,7 +133,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return DebugControllerSection.web.rawValue
|
return DebugControllerSection.web.rawValue
|
||||||
case .keepChatNavigationStack, .skipReadHistory, .dustEffect, .crashOnSlowQueries, .crashOnMemoryPressure:
|
case .keepChatNavigationStack, .skipReadHistory, .dustEffect, .crashOnSlowQueries, .crashOnMemoryPressure:
|
||||||
return DebugControllerSection.experiments.rawValue
|
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
|
return DebugControllerSection.experiments.rawValue
|
||||||
case .logTranslationRecognition, .resetTranslationStates:
|
case .logTranslationRecognition, .resetTranslationStates:
|
||||||
return DebugControllerSection.translation.rawValue
|
return DebugControllerSection.translation.rawValue
|
||||||
@ -242,7 +242,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return 47
|
return 47
|
||||||
case .disableReloginTokens:
|
case .disableReloginTokens:
|
||||||
return 48
|
return 48
|
||||||
case .playlistPlayback:
|
case .conferenceDebug:
|
||||||
return 49
|
return 49
|
||||||
case .enableQuickReactionSwitch:
|
case .enableQuickReactionSwitch:
|
||||||
return 50
|
return 50
|
||||||
@ -1308,12 +1308,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
})
|
})
|
||||||
}).start()
|
}).start()
|
||||||
})
|
})
|
||||||
case let .playlistPlayback(value):
|
case let .conferenceDebug(value):
|
||||||
return ItemListSwitchItem(presentationData: presentationData, title: "Playlist Playback", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
return ItemListSwitchItem(presentationData: presentationData, title: "Conference Debug", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||||
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
||||||
settings.playlistPlayback = value
|
settings.conferenceDebug = value
|
||||||
return PreferencesEntry(settings)
|
return PreferencesEntry(settings)
|
||||||
})
|
})
|
||||||
}).start()
|
}).start()
|
||||||
@ -1540,7 +1540,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
|||||||
entries.append(.storiesJpegExperiment(experimentalSettings.storiesJpegExperiment))
|
entries.append(.storiesJpegExperiment(experimentalSettings.storiesJpegExperiment))
|
||||||
entries.append(.disableReloginTokens(experimentalSettings.disableReloginTokens))
|
entries.append(.disableReloginTokens(experimentalSettings.disableReloginTokens))
|
||||||
}
|
}
|
||||||
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
|
entries.append(.conferenceDebug(experimentalSettings.conferenceDebug))
|
||||||
entries.append(.enableQuickReactionSwitch(!experimentalSettings.disableQuickReaction))
|
entries.append(.enableQuickReactionSwitch(!experimentalSettings.disableQuickReaction))
|
||||||
entries.append(.liveStreamV2(experimentalSettings.liveStreamV2))
|
entries.append(.liveStreamV2(experimentalSettings.liveStreamV2))
|
||||||
entries.append(.experimentalCallMute(experimentalSettings.experimentalCallMute))
|
entries.append(.experimentalCallMute(experimentalSettings.experimentalCallMute))
|
||||||
|
@ -1755,7 +1755,6 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let text: String
|
let text: String
|
||||||
if let timestamp {
|
if let timestamp {
|
||||||
//TODO:localize
|
|
||||||
let startTimeString: String
|
let startTimeString: String
|
||||||
let hours = timestamp / (60 * 60)
|
let hours = timestamp / (60 * 60)
|
||||||
let minutes = timestamp % (60 * 60) / 60
|
let minutes = timestamp % (60 * 60) / 60
|
||||||
@ -1765,7 +1764,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||||||
} else {
|
} else {
|
||||||
startTimeString = String(format: "%d:%02d", minutes, seconds)
|
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 {
|
} else {
|
||||||
text = presentationData.strings.Conversation_LinkCopied
|
text = presentationData.strings.Conversation_LinkCopied
|
||||||
}
|
}
|
||||||
|
@ -1076,7 +1076,6 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
|
|
||||||
private var customUnembedWhenPortrait: ((OverlayMediaItemNode) -> Bool)?
|
private var customUnembedWhenPortrait: ((OverlayMediaItemNode) -> Bool)?
|
||||||
|
|
||||||
private var pictureInPictureContent: AnyObject?
|
|
||||||
private var nativePictureInPictureContent: AnyObject?
|
private var nativePictureInPictureContent: AnyObject?
|
||||||
|
|
||||||
private var activePictureInPictureNavigationController: NavigationController?
|
private var activePictureInPictureNavigationController: NavigationController?
|
||||||
@ -1544,10 +1543,6 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
strongSelf.videoNode?.setBaseRate(playbackRate)
|
strongSelf.videoNode?.setBaseRate(playbackRate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if strongSelf.nativePictureInPictureContent == nil {
|
|
||||||
strongSelf.setupNativePictureInPicture()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.videoNode = videoNode
|
self.videoNode = videoNode
|
||||||
@ -2963,6 +2958,15 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func pictureInPictureButtonPressed() {
|
@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 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 {
|
if let controller = self.galleryController() as? GalleryController {
|
||||||
controller.dismiss(forceAway: true)
|
controller.dismiss(forceAway: true)
|
||||||
@ -2978,6 +2982,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func expandPIP() {
|
func expandPIP() {
|
||||||
if #available(iOS 15.0, *) {
|
if #available(iOS 15.0, *) {
|
||||||
|
@ -524,6 +524,24 @@ private func chatMessageVideoDatas(postbox: Postbox, userLocation: MediaResource
|
|||||||
thumbnail = .single(decodedThumbnailData)
|
thumbnail = .single(decodedThumbnailData)
|
||||||
}
|
}
|
||||||
} else if let thumbnailResource = thumbnailResource {
|
} 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
|
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 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
|
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()
|
thumbnailDisposable.dispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
thumbnail = .single(nil)
|
thumbnail = .single(nil)
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,8 @@ swift_library(
|
|||||||
"//submodules/GZip:GZip",
|
"//submodules/GZip:GZip",
|
||||||
"//submodules/rlottie:RLottieBinding",
|
"//submodules/rlottie:RLottieBinding",
|
||||||
"//submodules/AppBundle:AppBundle",
|
"//submodules/AppBundle:AppBundle",
|
||||||
"//submodules/ManagedAnimationNode:ManagedAnimationNode"
|
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
|
||||||
|
"//submodules/Components/HierarchyTrackingLayer",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -6,6 +6,7 @@ import SwiftSignalKit
|
|||||||
import RLottieBinding
|
import RLottieBinding
|
||||||
import GZip
|
import GZip
|
||||||
import AppBundle
|
import AppBundle
|
||||||
|
import HierarchyTrackingLayer
|
||||||
|
|
||||||
public enum SemanticStatusNodeState: Equatable {
|
public enum SemanticStatusNodeState: Equatable {
|
||||||
public struct ProgressAppearance: 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 {
|
private extension SemanticStatusNodeState {
|
||||||
func context(current: SemanticStatusNodeStateContext?) -> SemanticStatusNodeStateContext {
|
func context(current: SemanticStatusNodeStateContext?, animated: Bool) -> SemanticStatusNodeStateContext {
|
||||||
switch self {
|
switch self {
|
||||||
case .none, .download, .play, .pause, .customIcon:
|
case .none, .download, .play, .pause, .customIcon:
|
||||||
let icon: SemanticStatusNodeIcon
|
let icon: SemanticStatusNodeIcon
|
||||||
@ -114,7 +115,7 @@ private extension SemanticStatusNodeState {
|
|||||||
if current.icon == icon {
|
if current.icon == icon {
|
||||||
return current
|
return current
|
||||||
} else if (current.icon == .play && icon == .pause) || (current.icon == .pause && icon == .play) {
|
} else if (current.icon == .play && icon == .pause) || (current.icon == .pause && icon == .play) {
|
||||||
current.icon = icon
|
current.setIcon(icon: icon, animated: animated)
|
||||||
return current
|
return current
|
||||||
} else {
|
} else {
|
||||||
return SemanticStatusNodeIconContext(icon: icon)
|
return SemanticStatusNodeIconContext(icon: icon)
|
||||||
@ -376,6 +377,8 @@ public final class SemanticStatusNode: ASControlNode {
|
|||||||
private var stateContext: SemanticStatusNodeStateContext
|
private var stateContext: SemanticStatusNodeStateContext
|
||||||
private var appearanceContext: SemanticStatusNodeAppearanceContext
|
private var appearanceContext: SemanticStatusNodeAppearanceContext
|
||||||
|
|
||||||
|
private let hierarchyTrackingLayer: HierarchyTrackingLayer
|
||||||
|
|
||||||
private var disposable: Disposable?
|
private var disposable: Disposable?
|
||||||
private var backgroundNodeImage: UIImage?
|
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) {
|
public init(backgroundNodeColor: UIColor, foregroundNodeColor: UIColor, image: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? = nil, overlayForegroundNodeColor: UIColor? = nil, cutout: CGRect? = nil) {
|
||||||
self.state = .none
|
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.appearanceContext = SemanticStatusNodeAppearanceContext(background: backgroundNodeColor, foreground: foregroundNodeColor, backgroundImage: nil, overlayForeground: overlayForegroundNodeColor, cutout: cutout)
|
||||||
|
self.hierarchyTrackingLayer = HierarchyTrackingLayer()
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
self.layer.addSublayer(self.hierarchyTrackingLayer)
|
||||||
|
|
||||||
self.isOpaque = false
|
self.isOpaque = false
|
||||||
self.displaysAsynchronously = true
|
self.displaysAsynchronously = false
|
||||||
|
|
||||||
if let image {
|
if let image {
|
||||||
self.setBackgroundImage(image, size: CGSize(width: 44.0, height: 44.0))
|
self.setBackgroundImage(image, size: CGSize(width: 44.0, height: 44.0))
|
||||||
@ -420,7 +426,6 @@ public final class SemanticStatusNode: ASControlNode {
|
|||||||
animate = true
|
animate = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.stateContext.isAnimating {
|
if self.stateContext.isAnimating {
|
||||||
animate = true
|
animate = true
|
||||||
}
|
}
|
||||||
@ -449,12 +454,15 @@ public final class SemanticStatusNode: ASControlNode {
|
|||||||
self.hasState = true
|
self.hasState = true
|
||||||
animated = false
|
animated = false
|
||||||
}
|
}
|
||||||
|
if !self.hierarchyTrackingLayer.isInHierarchy {
|
||||||
|
animated = false
|
||||||
|
}
|
||||||
if self.state != state || self.appearanceContext.cutout != cutout {
|
if self.state != state || self.appearanceContext.cutout != cutout {
|
||||||
self.state = state
|
self.state = state
|
||||||
let previousStateContext = self.stateContext
|
let previousStateContext = self.stateContext
|
||||||
let previousAppearanceContext = updateCutout ? self.appearanceContext : nil
|
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.stateContext.requestUpdate = { [weak self] in
|
||||||
self?.setNeedsDisplay()
|
self?.setNeedsDisplay()
|
||||||
}
|
}
|
||||||
|
@ -131,11 +131,7 @@ final class SemanticStatusNodeIconContext: SemanticStatusNodeStateContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var icon: SemanticStatusNodeIcon {
|
private(set) var icon: SemanticStatusNodeIcon
|
||||||
didSet {
|
|
||||||
self.animationNode?.enqueueState(self.icon == .play ? .play : .pause, animated: self.iconImage != nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var animationNode: PlayPauseIconNode?
|
private var animationNode: PlayPauseIconNode?
|
||||||
private var iconImage: UIImage?
|
private var iconImage: UIImage?
|
||||||
@ -171,6 +167,11 @@ final class SemanticStatusNodeIconContext: SemanticStatusNodeStateContext {
|
|||||||
|
|
||||||
var requestUpdate: () -> Void = {}
|
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 {
|
func drawingState(transitionFraction: CGFloat) -> SemanticStatusNodeStateDrawingState {
|
||||||
return DrawingState(transitionFraction: transitionFraction, icon: self.icon, iconImage: self.iconImage, iconOffset: self.iconOffset)
|
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)
|
self.actionButtonNode.setBackgroundImage(highlightedHalfRoundedBackground, for: .highlighted)
|
||||||
|
|
||||||
if let startAtTimestamp = mediaParameters?.startAtTimestamp {
|
if let startAtTimestamp = mediaParameters?.startAtTimestamp {
|
||||||
//TODO:localize
|
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))
|
||||||
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))
|
|
||||||
} else {
|
} else {
|
||||||
self.startAtTimestampNode = nil
|
self.startAtTimestampNode = nil
|
||||||
}
|
}
|
||||||
|
@ -167,6 +167,14 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
self.conferenceAddParticipant?()
|
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(
|
self.callScreenState = PrivateCallScreen.State(
|
||||||
strings: presentationData.strings,
|
strings: presentationData.strings,
|
||||||
lifecycleState: .connecting,
|
lifecycleState: .connecting,
|
||||||
@ -180,7 +188,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
remoteVideo: nil,
|
remoteVideo: nil,
|
||||||
isRemoteBatteryLow: false,
|
isRemoteBatteryLow: false,
|
||||||
isEnergySavingEnabled: !self.sharedContext.energyUsageSettings.fullTranslucency,
|
isEnergySavingEnabled: !self.sharedContext.energyUsageSettings.fullTranslucency,
|
||||||
isConferencePossible: true
|
isConferencePossible: isConferencePossible
|
||||||
)
|
)
|
||||||
|
|
||||||
self.isMicrophoneMutedDisposable = (call.isMuted
|
self.isMicrophoneMutedDisposable = (call.isMuted
|
||||||
@ -520,6 +528,12 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
|
|||||||
}
|
}
|
||||||
|
|
||||||
if var callScreenState = self.callScreenState {
|
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.lifecycleState = mappedLifecycleState
|
||||||
callScreenState.remoteVideo = self.remoteVideo
|
callScreenState.remoteVideo = self.remoteVideo
|
||||||
callScreenState.localVideo = self.localVideo
|
callScreenState.localVideo = self.localVideo
|
||||||
|
@ -15,13 +15,14 @@ import AccountContext
|
|||||||
import DeviceProximity
|
import DeviceProximity
|
||||||
import PhoneNumberFormat
|
import PhoneNumberFormat
|
||||||
|
|
||||||
final class SharedCallAudioContext {
|
public final class SharedCallAudioContext {
|
||||||
let audioDevice: OngoingCallContext.AudioDevice?
|
let audioDevice: OngoingCallContext.AudioDevice?
|
||||||
let callKitIntegration: CallKitIntegration?
|
let callKitIntegration: CallKitIntegration?
|
||||||
|
|
||||||
private var audioSessionDisposable: Disposable?
|
private var audioSessionDisposable: Disposable?
|
||||||
private var audioSessionShouldBeActiveDisposable: Disposable?
|
private var audioSessionShouldBeActiveDisposable: Disposable?
|
||||||
private var isAudioSessionActiveDisposable: Disposable?
|
private var isAudioSessionActiveDisposable: Disposable?
|
||||||
|
private var audioOutputStateDisposable: Disposable?
|
||||||
|
|
||||||
private(set) var audioSessionControl: ManagedAudioSessionControl?
|
private(set) var audioSessionControl: ManagedAudioSessionControl?
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ final class SharedCallAudioContext {
|
|||||||
|
|
||||||
private let audioOutputStatePromise = Promise<([AudioSessionOutput], AudioSessionOutput?)>(([], nil))
|
private let audioOutputStatePromise = Promise<([AudioSessionOutput], AudioSessionOutput?)>(([], nil))
|
||||||
private var audioOutputStateValue: ([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
|
private var didSetCurrentAudioOutputValue: Bool = false
|
||||||
var audioOutputState: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> {
|
var audioOutputState: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> {
|
||||||
return self.audioOutputStatePromise.get()
|
return self.audioOutputStatePromise.get()
|
||||||
@ -141,12 +142,24 @@ final class SharedCallAudioContext {
|
|||||||
}
|
}
|
||||||
self.audioDevice?.setIsAudioSessionActive(value)
|
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 {
|
deinit {
|
||||||
self.audioSessionDisposable?.dispose()
|
self.audioSessionDisposable?.dispose()
|
||||||
self.audioSessionShouldBeActiveDisposable?.dispose()
|
self.audioSessionShouldBeActiveDisposable?.dispose()
|
||||||
self.isAudioSessionActiveDisposable?.dispose()
|
self.isAudioSessionActiveDisposable?.dispose()
|
||||||
|
self.audioOutputStateDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
func setCurrentAudioOutput(_ output: AudioSessionOutput) {
|
func setCurrentAudioOutput(_ output: AudioSessionOutput) {
|
||||||
@ -201,7 +214,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
private let currentNetworkType: NetworkType
|
private let currentNetworkType: NetworkType
|
||||||
private let updatedNetworkType: Signal<NetworkType, NoError>
|
private let updatedNetworkType: Signal<NetworkType, NoError>
|
||||||
|
|
||||||
private var sharedAudioContext: SharedCallAudioContext?
|
public private(set) var sharedAudioContext: SharedCallAudioContext?
|
||||||
|
|
||||||
private var sessionState: CallSession?
|
private var sessionState: CallSession?
|
||||||
private var callContextState: OngoingCallContextState?
|
private var callContextState: OngoingCallContextState?
|
||||||
@ -1610,6 +1623,29 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
self.useFrontCamera = !self.useFrontCamera
|
self.useFrontCamera = !self.useFrontCamera
|
||||||
self.videoCapturer?.switchVideoInput(isFront: 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? {
|
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)
|
containerSize: CGSize(width: availableContentSize.width - contentInsets.left - contentInsets.right - spacing - iconSize.width, height: availableContentSize.height)
|
||||||
)
|
)
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
let contentSize = self.content.update(
|
let contentSize = self.content.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
component: AnyComponent(AnimatedTextComponent(
|
component: AnyComponent(AnimatedTextComponent(
|
||||||
|
@ -153,7 +153,6 @@ public final class BatchVideoRenderingContext {
|
|||||||
for (id, targetContext) in self.targetContexts {
|
for (id, targetContext) in self.targetContexts {
|
||||||
if targetContext.target != nil {
|
if targetContext.target != nil {
|
||||||
if targetContext.fetchDisposable == nil {
|
if targetContext.fetchDisposable == nil {
|
||||||
//TODO:release pass resource reference
|
|
||||||
targetContext.fetchDisposable = fetchedMediaResource(
|
targetContext.fetchDisposable = fetchedMediaResource(
|
||||||
mediaBox: self.context.account.postbox.mediaBox,
|
mediaBox: self.context.account.postbox.mediaBox,
|
||||||
userLocation: targetContext.userLocation,
|
userLocation: targetContext.userLocation,
|
||||||
|
@ -613,7 +613,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView, AVPictureInPictu
|
|||||||
if let previousParams = self.params, case .active = params.state.lifecycleState {
|
if let previousParams = self.params, case .active = params.state.lifecycleState {
|
||||||
switch previousParams.state.lifecycleState {
|
switch previousParams.state.lifecycleState {
|
||||||
case .requesting, .ringing, .connecting, .reconnecting:
|
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.displayEmojiTooltip = true
|
||||||
|
|
||||||
self.hideEmojiTooltipTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false, block: { [weak self] _ in
|
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/TelegramUI/Components/Chat/ChatMessageItemCommon",
|
||||||
"//submodules/AnimatedCountLabelNode",
|
"//submodules/AnimatedCountLabelNode",
|
||||||
"//submodules/AudioWaveform",
|
"//submodules/AudioWaveform",
|
||||||
|
"//submodules/DeviceProximity",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -34,6 +34,7 @@ import ChatMessageItemCommon
|
|||||||
import TelegramStringFormatting
|
import TelegramStringFormatting
|
||||||
import AnimatedCountLabelNode
|
import AnimatedCountLabelNode
|
||||||
import AudioWaveform
|
import AudioWaveform
|
||||||
|
import DeviceProximity
|
||||||
|
|
||||||
private struct FetchControls {
|
private struct FetchControls {
|
||||||
let fetch: (Bool) -> Void
|
let fetch: (Bool) -> Void
|
||||||
@ -1561,6 +1562,12 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
guard let arguments = self.arguments else {
|
guard let arguments = self.arguments else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var animated = animated
|
||||||
|
if DeviceProximityManager.shared().currentValue() {
|
||||||
|
animated = false
|
||||||
|
}
|
||||||
|
|
||||||
let incoming = message.effectivelyIncoming(context.account.peerId)
|
let incoming = message.effectivelyIncoming(context.account.peerId)
|
||||||
let messageTheme = incoming ? presentationData.theme.theme.chat.message.incoming : presentationData.theme.theme.chat.message.outgoing
|
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
|
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
|
self.interactiveImageNode.activateAgeRestrictedMedia = { [weak self] in
|
||||||
|
@ -1794,11 +1794,10 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
|
|
||||||
let titleSubtitleSpacing: CGFloat = 1.0
|
let titleSubtitleSpacing: CGFloat = 1.0
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
let subtitleSize = self.subtitle.update(
|
let subtitleSize = self.subtitle.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(MultilineTextComponent(
|
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: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: availableSize.width - leftButtonFrame.maxX * 2.0, height: 100.0)
|
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 chatIsRotated: Bool = true
|
||||||
public var canReadHistory: Bool = false
|
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(
|
public init(
|
||||||
openMessage: @escaping (Message, OpenMessageParams) -> Bool,
|
openMessage: @escaping (Message, OpenMessageParams) -> Bool,
|
||||||
openPeer: @escaping (EnginePeer, ChatControllerInteractionNavigateToPeer, MessageReference?, OpenPeerSource) -> Void,
|
openPeer: @escaping (EnginePeer, ChatControllerInteractionNavigateToPeer, MessageReference?, OpenPeerSource) -> Void,
|
||||||
@ -538,4 +561,8 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
|
|||||||
|
|
||||||
self.presentationContext = presentationContext
|
self.presentationContext = presentationContext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.isOpeningMediaDisposable?.dispose()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,10 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var accumulatedRightButtonOffset: CGFloat = canBeExpanded ? 16.0 : 0.0
|
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)
|
button.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: canBeExpanded, transition: transition)
|
||||||
transition.updateSublayerTransformOffset(layer: button.layer, offset: CGPoint(x: accumulatedRightButtonOffset, y: 0.0))
|
transition.updateSublayerTransformOffset(layer: button.layer, offset: CGPoint(x: accumulatedRightButtonOffset, y: 0.0))
|
||||||
if self.backgroundContentColor.alpha != 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 {
|
if self.currentRightButtons != rightButtons || presentationData.strings !== self.presentationData?.strings {
|
||||||
self.currentRightButtons = rightButtons
|
self.currentRightButtons = rightButtons
|
||||||
|
|
||||||
@ -225,7 +229,10 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode {
|
|||||||
buttonNode.alpha = 0.0
|
buttonNode.alpha = 0.0
|
||||||
transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor)
|
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 {
|
} else {
|
||||||
transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame)
|
transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame)
|
||||||
transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor)
|
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
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
let mode = params.mode
|
let mode = params.mode
|
||||||
|
|
||||||
let displayVoiceMessageDiscardAlert: () -> Bool = {
|
let displayVoiceMessageDiscardAlert: () -> Bool = { [weak self] in
|
||||||
if strongSelf.presentVoiceMessageDiscardAlert(action: { [weak self] in
|
guard let self else {
|
||||||
if let strongSelf = self {
|
return true
|
||||||
Queue.mainQueue().after(0.1, {
|
|
||||||
let _ = strongSelf.controllerInteraction?.openMessage(message, params)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
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) {
|
}, performAction: false) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.commitPurposefulAction()
|
self.commitPurposefulAction()
|
||||||
strongSelf.dismissAllTooltips()
|
self.dismissAllTooltips()
|
||||||
|
|
||||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
self.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||||
|
|
||||||
var openMessageByAction = false
|
var openMessageByAction = false
|
||||||
var isLocation = false
|
var isLocation = false
|
||||||
@ -923,9 +927,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
if let file = media as? TelegramMediaFile {
|
if let file = media as? TelegramMediaFile {
|
||||||
if file.isInstantVideo {
|
if file.isInstantVideo {
|
||||||
if strongSelf.chatDisplayNode.isInputViewFocused {
|
if self.chatDisplayNode.isInputViewFocused {
|
||||||
strongSelf.returnInputViewFocus = true
|
self.returnInputViewFocus = true
|
||||||
strongSelf.chatDisplayNode.dismissInput()
|
self.chatDisplayNode.dismissInput()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if file.isMusic || file.isVoice || file.isInstantVideo {
|
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 {
|
if (file.isVoice || file.isInstantVideo) && message.minAutoremoveOrClearTimeout == viewOnceTimeout {
|
||||||
strongSelf.openViewOnceMediaMessage(message)
|
self.openViewOnceMediaMessage(message)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
} else if file.isVideo {
|
} else if file.isVideo {
|
||||||
@ -947,7 +951,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
switch extendedMedia {
|
switch extendedMedia {
|
||||||
case .preview:
|
case .preview:
|
||||||
if displayVoiceMessageDiscardAlert() {
|
if displayVoiceMessageDiscardAlert() {
|
||||||
strongSelf.controllerInteraction?.openCheckoutOrReceipt(message.id, params)
|
self.controllerInteraction?.openCheckoutOrReceipt(message.id, params)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -959,7 +963,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
switch extendedMedia {
|
switch extendedMedia {
|
||||||
case .preview:
|
case .preview:
|
||||||
if displayVoiceMessageDiscardAlert() {
|
if displayVoiceMessageDiscardAlert() {
|
||||||
strongSelf.controllerInteraction?.openCheckoutOrReceipt(message.id, nil)
|
self.controllerInteraction?.openCheckoutOrReceipt(message.id, nil)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -969,15 +973,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
} else if media is TelegramMediaGiveaway || media is TelegramMediaGiveawayResults {
|
} else if media is TelegramMediaGiveaway || media is TelegramMediaGiveawayResults {
|
||||||
let progress = params.progress
|
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
|
let disposable: MetaDisposable
|
||||||
if let current = strongSelf.giveawayStatusDisposable {
|
if let current = self.giveawayStatusDisposable {
|
||||||
disposable = current
|
disposable = current
|
||||||
} else {
|
} else {
|
||||||
disposable = MetaDisposable()
|
disposable = MetaDisposable()
|
||||||
strongSelf.giveawayStatusDisposable = disposable
|
self.giveawayStatusDisposable = disposable
|
||||||
}
|
}
|
||||||
|
|
||||||
let progressSignal = Signal<Never, NoError> { [weak self] subscriber in
|
let progressSignal = Signal<Never, NoError> { [weak self] subscriber in
|
||||||
@ -1010,8 +1014,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
disposable.set((signal
|
disposable.set((signal
|
||||||
|> deliverOnMainQueue).startStrict(next: { [weak self] info in
|
|> deliverOnMainQueue).startStrict(next: { [weak self] info in
|
||||||
if let strongSelf = self, let info {
|
if let self, let info {
|
||||||
strongSelf.displayGiveawayStatusInfo(messageId: message.id, giveawayInfo: 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:
|
case .pinnedMessageUpdated, .gameScore, .setSameChatWallpaper, .giveawayResults, .customText:
|
||||||
for attribute in message.attributes {
|
for attribute in message.attributes {
|
||||||
if let attribute = attribute as? ReplyMessageAttribute {
|
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
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .photoUpdated(image):
|
case let .photoUpdated(image):
|
||||||
openMessageByAction = image != nil
|
openMessageByAction = image != nil
|
||||||
case .groupPhoneCall, .inviteToGroupPhoneCall:
|
case .groupPhoneCall, .inviteToGroupPhoneCall:
|
||||||
if let activeCall = strongSelf.presentationInterfaceState.activeGroupCallInfo?.activeCall {
|
if let activeCall = self.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))
|
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 {
|
} else {
|
||||||
var canManageGroupCalls = false
|
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) {
|
if channel.flags.contains(.isCreator) || channel.hasPermission(.manageCalls) {
|
||||||
canManageGroupCalls = true
|
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 {
|
if case .creator = group.role {
|
||||||
canManageGroupCalls = true
|
canManageGroupCalls = true
|
||||||
} else if case let .admin(rights, _) = group.role {
|
} else if case let .admin(rights, _) = group.role {
|
||||||
@ -1051,80 +1055,80 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
if canManageGroupCalls {
|
if canManageGroupCalls {
|
||||||
let text: String
|
let text: String
|
||||||
if let channel = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel, case .broadcast = channel.info {
|
if let channel = self.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel, case .broadcast = channel.info {
|
||||||
text = strongSelf.presentationData.strings.LiveStream_CreateNewVoiceChatText
|
text = self.presentationData.strings.LiveStream_CreateNewVoiceChatText
|
||||||
} else {
|
} 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: {
|
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 strongSelf = self {
|
if let self {
|
||||||
var dismissStatus: (() -> Void)?
|
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?()
|
||||||
}))
|
}))
|
||||||
dismissStatus = { [weak self, weak statusController] in
|
dismissStatus = { [weak self, weak statusController] in
|
||||||
self?.createVoiceChatDisposable.set(nil)
|
self?.createVoiceChatDisposable.set(nil)
|
||||||
statusController?.dismiss()
|
statusController?.dismiss()
|
||||||
}
|
}
|
||||||
strongSelf.present(statusController, in: .window(.root))
|
self.present(statusController, in: .window(.root))
|
||||||
strongSelf.createVoiceChatDisposable.set((strongSelf.context.engine.calls.createGroupCall(peerId: message.id.peerId, title: nil, scheduleDate: nil, isExternalStream: false)
|
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
|
|> deliverOnMainQueue).startStrict(next: { [weak self] info in
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return
|
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
|
}, error: { [weak self] error in
|
||||||
dismissStatus?()
|
dismissStatus?()
|
||||||
|
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let text: String
|
let text: String
|
||||||
switch error {
|
switch error {
|
||||||
case .generic, .scheduledTooLate:
|
case .generic, .scheduledTooLate:
|
||||||
text = strongSelf.presentationData.strings.Login_UnknownError
|
text = self.presentationData.strings.Login_UnknownError
|
||||||
case .anonymousNotAllowed:
|
case .anonymousNotAllowed:
|
||||||
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
|
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 {
|
} 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: {
|
}, completed: {
|
||||||
dismissStatus?()
|
dismissStatus?()
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.VoiceChat_CreateNewVoiceChatSchedule, action: {
|
}), TextAlertAction(type: .genericAction, title: self.presentationData.strings.VoiceChat_CreateNewVoiceChatSchedule, action: { [weak self] in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.context.scheduleGroupCall(peerId: message.id.peerId, parentController: strongSelf)
|
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
|
return true
|
||||||
case .messageAutoremoveTimeoutUpdated:
|
case .messageAutoremoveTimeoutUpdated:
|
||||||
var canSetupAutoremoveTimeout = false
|
var canSetupAutoremoveTimeout = false
|
||||||
|
|
||||||
if let _ = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat {
|
if let _ = self.presentationInterfaceState.renderedPeer?.peer as? TelegramSecretChat {
|
||||||
canSetupAutoremoveTimeout = false
|
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) {
|
if !group.hasBannedPermission(.banChangeInfo) {
|
||||||
canSetupAutoremoveTimeout = true
|
canSetupAutoremoveTimeout = true
|
||||||
}
|
}
|
||||||
} else if let user = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramUser {
|
} else if let user = self.presentationInterfaceState.renderedPeer?.peer as? TelegramUser {
|
||||||
if user.id != strongSelf.context.account.peerId && user.botInfo == nil {
|
if user.id != self.context.account.peerId && user.botInfo == nil {
|
||||||
canSetupAutoremoveTimeout = true
|
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) {
|
if channel.hasPermission(.changeInfo) {
|
||||||
canSetupAutoremoveTimeout = true
|
canSetupAutoremoveTimeout = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if canSetupAutoremoveTimeout {
|
if canSetupAutoremoveTimeout {
|
||||||
strongSelf.presentAutoremoveSetup()
|
self.presentAutoremoveSetup()
|
||||||
}
|
}
|
||||||
case let .paymentSent(currency, _, _, _, _):
|
case let .paymentSent(currency, _, _, _, _):
|
||||||
if currency == "XTR" {
|
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))
|
self.push(self.context.sharedContext.makeStarsReceiptScreen(context: self.context, receipt: receipt))
|
||||||
})
|
})
|
||||||
} else {
|
} 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
|
return true
|
||||||
case .setChatTheme:
|
case .setChatTheme:
|
||||||
strongSelf.presentThemeSelection()
|
self.presentThemeSelection()
|
||||||
return true
|
return true
|
||||||
case let .setChatWallpaper(wallpaper, _):
|
case let .setChatWallpaper(wallpaper, _):
|
||||||
guard let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
guard let peer = self.presentationInterfaceState.renderedPeer?.peer else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if let peer = peer as? TelegramChannel {
|
if let peer = peer as? TelegramChannel {
|
||||||
@ -1158,11 +1162,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
guard message.effectivelyIncoming(strongSelf.context.account.peerId), let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
guard message.effectivelyIncoming(self.context.account.peerId), let peer = self.presentationInterfaceState.renderedPeer?.peer else {
|
||||||
strongSelf.presentThemeSelection()
|
self.presentThemeSelection()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
strongSelf.chatDisplayNode.dismissInput()
|
self.chatDisplayNode.dismissInput()
|
||||||
var options = WallpaperPresentationOptions()
|
var options = WallpaperPresentationOptions()
|
||||||
var intensity: Int32?
|
var intensity: Int32?
|
||||||
if let settings = wallpaper.settings {
|
if let settings = wallpaper.settings {
|
||||||
@ -1176,7 +1180,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
intensity = settings.intensity
|
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
|
wallpaperPreviewController.apply = { [weak wallpaperPreviewController] entry, options, _, _, brightness, forBoth in
|
||||||
var settings: WallpaperSettings?
|
var settings: WallpaperSettings?
|
||||||
if case let .wallpaper(wallpaper, _) = entry {
|
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)
|
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()
|
|> deliverOnMainQueue).startStandalone()
|
||||||
Queue.mainQueue().after(0.1) {
|
Queue.mainQueue().after(0.1) {
|
||||||
wallpaperPreviewController?.dismiss()
|
wallpaperPreviewController?.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
strongSelf.push(wallpaperPreviewController)
|
self.push(wallpaperPreviewController)
|
||||||
return true
|
return true
|
||||||
case let .giftPremium(_, _, duration, _, _, _, _):
|
case let .giftPremium(_, _, duration, _, _, _, _):
|
||||||
strongSelf.chatDisplayNode.dismissInput()
|
self.chatDisplayNode.dismissInput()
|
||||||
let fromPeerId: PeerId = message.author?.id == strongSelf.context.account.peerId ? strongSelf.context.account.peerId : message.id.peerId
|
let fromPeerId: PeerId = message.author?.id == self.context.account.peerId ? self.context.account.peerId : message.id.peerId
|
||||||
let toPeerId: PeerId = message.author?.id == strongSelf.context.account.peerId ? message.id.peerId : strongSelf.context.account.peerId
|
let toPeerId: PeerId = message.author?.id == self.context.account.peerId ? message.id.peerId : self.context.account.peerId
|
||||||
let controller = PremiumIntroScreen(context: strongSelf.context, source: .gift(from: fromPeerId, to: toPeerId, duration: duration, giftCode: nil))
|
let controller = PremiumIntroScreen(context: self.context, source: .gift(from: fromPeerId, to: toPeerId, duration: duration, giftCode: nil))
|
||||||
strongSelf.push(controller)
|
self.push(controller)
|
||||||
return true
|
return true
|
||||||
case .starGift, .starGiftUnique:
|
case .starGift, .starGiftUnique:
|
||||||
let controller = strongSelf.context.sharedContext.makeGiftViewScreen(context: strongSelf.context, message: EngineMessage(message), shareStory: { [weak self] uniqueGift in
|
let controller = self.context.sharedContext.makeGiftViewScreen(context: self.context, message: EngineMessage(message), shareStory: { [weak self] uniqueGift in
|
||||||
if let self {
|
|
||||||
Queue.mainQueue().after(0.15) {
|
Queue.mainQueue().after(0.15) {
|
||||||
|
if let self {
|
||||||
let controller = self.context.sharedContext.makeStorySharingScreen(context: self.context, subject: .gift(uniqueGift), parentController: self)
|
let controller = self.context.sharedContext.makeStorySharingScreen(context: self.context, subject: .gift(uniqueGift), parentController: self)
|
||||||
self.push(controller)
|
self.push(controller)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
strongSelf.push(controller)
|
self.push(controller)
|
||||||
return true
|
return true
|
||||||
case .giftStars:
|
case .giftStars:
|
||||||
let controller = strongSelf.context.sharedContext.makeStarsGiftScreen(context: strongSelf.context, message: EngineMessage(message))
|
let controller = self.context.sharedContext.makeStarsGiftScreen(context: self.context, message: EngineMessage(message))
|
||||||
strongSelf.push(controller)
|
self.push(controller)
|
||||||
return true
|
return true
|
||||||
case let .giftCode(slug, _, _, _, _, _, _, _, _, _, _):
|
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
|
return true
|
||||||
case .prizeStars:
|
case .prizeStars:
|
||||||
let controller = strongSelf.context.sharedContext.makeStarsGiftScreen(context: strongSelf.context, message: EngineMessage(message))
|
let controller = self.context.sharedContext.makeStarsGiftScreen(context: self.context, message: EngineMessage(message))
|
||||||
strongSelf.push(controller)
|
self.push(controller)
|
||||||
return true
|
return true
|
||||||
case let .suggestedProfilePhoto(image):
|
case let .suggestedProfilePhoto(image):
|
||||||
strongSelf.chatDisplayNode.dismissInput()
|
self.chatDisplayNode.dismissInput()
|
||||||
if let image = image {
|
if let image = image {
|
||||||
if message.effectivelyIncoming(strongSelf.context.account.peerId) {
|
if message.effectivelyIncoming(self.context.account.peerId) {
|
||||||
if let emojiMarkup = image.emojiMarkup {
|
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
|
controller.imageCompletion = { [weak self] image, commit in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
if let rootController = self.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||||
settingsController.updateProfilePhoto(image, mode: .accept, uploadStatus: nil)
|
settingsController.updateProfilePhoto(image, mode: .accept, uploadStatus: nil)
|
||||||
commit()
|
commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
controller.videoCompletion = { [weak self] image, url, values, markup, commit in
|
controller.videoCompletion = { [weak self] image, url, values, markup, commit in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
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)
|
settingsController.updateProfileVideo(image, video: nil, values: nil, markup: markup, mode: .accept, uploadStatus: nil)
|
||||||
commit()
|
commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
strongSelf.push(controller)
|
self.push(controller)
|
||||||
} else {
|
} else {
|
||||||
var selectedNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
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 itemNode = itemNode as? ChatMessageItemView {
|
||||||
if let result = itemNode.transitionNode(id: message.id, media: image, adjustRect: false) {
|
if let result = itemNode.transitionNode(id: message.id, media: image, adjustRect: false) {
|
||||||
selectedNode = result
|
selectedNode = result
|
||||||
@ -1267,17 +1271,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
senderName = nil
|
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)
|
self?.present(c, in: .window(.root), with: a)
|
||||||
}, imageCompletion: { [weak self] image in
|
}, imageCompletion: { [weak self] image in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
if let rootController = self.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||||
settingsController.updateProfilePhoto(image, mode: .accept, uploadStatus: nil)
|
settingsController.updateProfilePhoto(image, mode: .accept, uploadStatus: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, videoCompletion: { [weak self] image, url, adjustments in
|
}, videoCompletion: { [weak self] image, url, adjustments in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
if let rootController = self.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||||
settingsController.oldUpdateProfileVideo(image, asset: AVURLAsset(url: url), adjustments: adjustments, mode: .accept)
|
settingsController.oldUpdateProfileVideo(image, asset: AVURLAsset(url: url), adjustments: adjustments, mode: .accept)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1288,7 +1292,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case .boostsApplied:
|
case .boostsApplied:
|
||||||
strongSelf.controllerInteraction?.openGroupBoostInfo(nil, 0)
|
self.controllerInteraction?.openGroupBoostInfo(nil, 0)
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
@ -1299,29 +1303,33 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let openChatLocation = strongSelf.chatLocation
|
let openChatLocation = self.chatLocation
|
||||||
var chatFilterTag: MemoryBuffer?
|
var chatFilterTag: MemoryBuffer?
|
||||||
if case let .customTag(value, _) = strongSelf.chatDisplayNode.historyNode.tag {
|
if case let .customTag(value, _) = self.chatDisplayNode.historyNode.tag {
|
||||||
chatFilterTag = value
|
chatFilterTag = value
|
||||||
}
|
}
|
||||||
|
|
||||||
var standalone = false
|
var standalone = false
|
||||||
if case .customChatContents = strongSelf.chatLocation {
|
if case .customChatContents = self.chatLocation {
|
||||||
standalone = true
|
standalone = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if let adAttribute = message.attributes.first(where: { $0 is AdMessageAttribute }) as? AdMessageAttribute {
|
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 {
|
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 {
|
} else {
|
||||||
strongSelf.controllerInteraction?.activateAdAction(message.id, nil, true, false)
|
self.controllerInteraction?.activateAdAction(message.id, nil, true, false)
|
||||||
return true
|
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()
|
self?.chatDisplayNode.dismissInput()
|
||||||
}, present: { c, a, i in
|
}, present: { [weak self] c, a, i in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if case .current = i {
|
if case .current = i {
|
||||||
c.presentationArguments = a
|
c.presentationArguments = a
|
||||||
c.statusBar.alphaUpdated = { [weak self] transition in
|
c.statusBar.alphaUpdated = { [weak self] transition in
|
||||||
@ -1330,14 +1338,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
self.updateStatusBarPresentation(animated: transition.isAnimated)
|
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 {
|
} 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?))?
|
var selectedNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||||
if let itemNode = itemNode as? ChatMessageItemView {
|
if let itemNode = itemNode as? ChatMessageItemView {
|
||||||
if let result = itemNode.transitionNode(id: messageId, media: media, adjustRect: adjustRect) {
|
if let result = itemNode.transitionNode(id: messageId, media: media, adjustRect: adjustRect) {
|
||||||
selectedNode = result
|
selectedNode = result
|
||||||
@ -1346,27 +1354,27 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return selectedNode
|
return selectedNode
|
||||||
}, addToTransitionSurface: { view in
|
}, addToTransitionSurface: { [weak self] view in
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.chatDisplayNode.historyNode.view.superview?.insertSubview(view, aboveSubview: strongSelf.chatDisplayNode.historyNode.view)
|
self.chatDisplayNode.historyNode.view.superview?.insertSubview(view, aboveSubview: self.chatDisplayNode.historyNode.view)
|
||||||
}, openUrl: { url in
|
}, openUrl: { [weak self] url in
|
||||||
self?.openUrl(url, concealed: false, skipConcealedAlert: isLocation, message: nil)
|
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)
|
self?.openPeer(peer: EnginePeer(peer), navigation: navigation, fromMessage: nil)
|
||||||
}, callPeer: { peerId, isVideo in
|
}, callPeer: { [weak self] peerId, isVideo in
|
||||||
self?.controllerInteraction?.callPeer(peerId, isVideo)
|
self?.controllerInteraction?.callPeer(peerId, isVideo)
|
||||||
}, enqueueMessage: { message in
|
}, enqueueMessage: { [weak self] message in
|
||||||
self?.sendMessages([message])
|
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
|
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)
|
self?.controllerInteraction?.sendEmoji(text, attribute, false)
|
||||||
} : nil, setupTemporaryHiddenMedia: { signal, centralIndex, galleryMedia in
|
} : nil, setupTemporaryHiddenMedia: { [weak self] signal, centralIndex, galleryMedia in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).startStrict(next: { entry in
|
self.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).startStrict(next: { [weak self] entry in
|
||||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
if let self, let controllerInteraction = self.controllerInteraction {
|
||||||
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
||||||
|
|
||||||
if let entry = entry as? InstantPageGalleryEntry, entry.index == centralIndex {
|
if let entry = entry as? InstantPageGalleryEntry, entry.index == centralIndex {
|
||||||
@ -1375,7 +1383,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
controllerInteraction.hiddenMedia = messageIdAndMedia
|
controllerInteraction.hiddenMedia = messageIdAndMedia
|
||||||
|
|
||||||
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||||
if let itemNode = itemNode as? ChatMessageItemView {
|
if let itemNode = itemNode as? ChatMessageItemView {
|
||||||
itemNode.updateHiddenMedia()
|
itemNode.updateHiddenMedia()
|
||||||
}
|
}
|
||||||
@ -1383,10 +1391,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}, chatAvatarHiddenMedia: { signal, media in
|
}, chatAvatarHiddenMedia: { [weak self] signal, media in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).startStrict(next: { messageId in
|
self.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).startStrict(next: { [weak self] messageId in
|
||||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
if let self, let controllerInteraction = self.controllerInteraction {
|
||||||
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
||||||
|
|
||||||
if let messageId = messageId {
|
if let messageId = messageId {
|
||||||
@ -1395,7 +1403,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
controllerInteraction.hiddenMedia = messageIdAndMedia
|
controllerInteraction.hiddenMedia = messageIdAndMedia
|
||||||
|
|
||||||
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||||
if let itemNode = itemNode as? ChatMessageItemView {
|
if let itemNode = itemNode as? ChatMessageItemView {
|
||||||
itemNode.updateHiddenMedia()
|
itemNode.updateHiddenMedia()
|
||||||
}
|
}
|
||||||
@ -1405,54 +1413,54 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}, actionInteraction: GalleryControllerActionInteraction(
|
}, actionInteraction: GalleryControllerActionInteraction(
|
||||||
openUrl: { [weak self] url, concealed in
|
openUrl: { [weak self] url, concealed in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.openUrl(url, concealed: concealed, message: nil)
|
self.openUrl(url, concealed: concealed, message: nil)
|
||||||
}
|
}
|
||||||
}, openUrlIn: { [weak self] url in
|
}, openUrlIn: { [weak self] url in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.openUrlIn(url)
|
self.openUrlIn(url)
|
||||||
}
|
}
|
||||||
}, openPeerMention: { [weak self] mention in
|
}, openPeerMention: { [weak self] mention in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.controllerInteraction?.openPeerMention(mention, nil)
|
self.controllerInteraction?.openPeerMention(mention, nil)
|
||||||
}
|
}
|
||||||
}, openPeer: { [weak self] peer in
|
}, openPeer: { [weak self] peer in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.controllerInteraction?.openPeer(peer, .default, nil, .default)
|
self.controllerInteraction?.openPeer(peer, .default, nil, .default)
|
||||||
}
|
}
|
||||||
}, openHashtag: { [weak self] peerName, hashtag in
|
}, openHashtag: { [weak self] peerName, hashtag in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.controllerInteraction?.openHashtag(peerName, hashtag)
|
self.controllerInteraction?.openHashtag(peerName, hashtag)
|
||||||
}
|
}
|
||||||
}, openBotCommand: { [weak self] command in
|
}, openBotCommand: { [weak self] command in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.controllerInteraction?.sendBotCommand(nil, command)
|
self.controllerInteraction?.sendBotCommand(nil, command)
|
||||||
}
|
}
|
||||||
}, openAd: { [weak self] messageId in
|
}, openAd: { [weak self] messageId in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.controllerInteraction?.activateAdAction(messageId, nil, true, true)
|
self.controllerInteraction?.activateAdAction(messageId, nil, true, true)
|
||||||
}
|
}
|
||||||
}, addContact: { [weak self] phoneNumber in
|
}, addContact: { [weak self] phoneNumber in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.controllerInteraction?.addContact(phoneNumber)
|
self.controllerInteraction?.addContact(phoneNumber)
|
||||||
}
|
}
|
||||||
}, storeMediaPlaybackState: { [weak self] messageId, timestamp, playbackRate in
|
}, storeMediaPlaybackState: { [weak self] messageId, timestamp, playbackRate in
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var storedState: MediaPlaybackStoredState?
|
var storedState: MediaPlaybackStoredState?
|
||||||
if let timestamp = timestamp {
|
if let timestamp = timestamp {
|
||||||
storedState = MediaPlaybackStoredState(timestamp: timestamp, playbackRate: AudioPlaybackRate(playbackRate))
|
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
|
}, editMedia: { [weak self] messageId, snapshots, transitionCompletion in
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return
|
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
|
|> deliverOnMainQueue).startStandalone(next: { [weak self] message in
|
||||||
guard let strongSelf = self, let message = message else {
|
guard let self, let message = message else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1472,17 +1480,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] {
|
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()
|
transitionCompletion()
|
||||||
}, getCaptionPanelView: { [weak self] in
|
}, getCaptionPanelView: { [weak self] in
|
||||||
return self?.getCaptionPanelView(isFile: false)
|
return self?.getCaptionPanelView(isFile: false)
|
||||||
}, sendMessagesWithSignals: { [weak self] signals, _, _, isCaptionAbove in
|
}, sendMessagesWithSignals: { [weak self] signals, _, _, isCaptionAbove in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
var parameters: ChatSendMessageActionSheetController.SendParameters?
|
var parameters: ChatSendMessageActionSheetController.SendParameters?
|
||||||
if isCaptionAbove {
|
if isCaptionAbove {
|
||||||
parameters = ChatSendMessageActionSheetController.SendParameters(effect: nil, textIsAboveMedia: true)
|
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
|
}, present: { [weak self] c, a in
|
||||||
self?.present(c, in: .window(.root), with: a)
|
self?.present(c, in: .window(.root), with: a)
|
||||||
@ -1493,18 +1501,22 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self?.canReadHistory.set(canReadHistory)
|
self?.canReadHistory.set(canReadHistory)
|
||||||
}),
|
}),
|
||||||
getSourceRect: { [weak self] in
|
getSourceRect: { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var rect: CGRect?
|
var rect: CGRect?
|
||||||
strongSelf.chatDisplayNode.historyNode.forEachVisibleMessageItemNode({ itemNode in
|
self.chatDisplayNode.historyNode.forEachVisibleMessageItemNode({ itemNode in
|
||||||
if itemNode.item?.message.id == message.id {
|
if itemNode.item?.message.id == message.id {
|
||||||
rect = itemNode.view.convert(itemNode.contentFrame(), to: nil)
|
rect = itemNode.view.convert(itemNode.contentFrame(), to: nil)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return rect
|
return rect
|
||||||
}
|
}
|
||||||
))
|
)
|
||||||
|
|
||||||
|
self.controllerInteraction?.isOpeningMediaSignal = openChatMessageParams.blockInteraction.get()
|
||||||
|
|
||||||
|
return context.sharedContext.openChatMessage(openChatMessageParams)
|
||||||
}, openPeer: { [weak self] peer, navigation, fromMessage, source in
|
}, openPeer: { [weak self] peer, navigation, fromMessage, source in
|
||||||
var expandAvatar = false
|
var expandAvatar = false
|
||||||
if case let .groupParticipant(storyStats, avatarHeaderNode) = source {
|
if case let .groupParticipant(storyStats, avatarHeaderNode) = source {
|
||||||
|
@ -323,8 +323,12 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params.blockInteraction.set(.single(true))
|
||||||
|
|
||||||
let _ = (gallery
|
let _ = (gallery
|
||||||
|> deliverOnMainQueue).startStandalone(next: { gallery in
|
|> deliverOnMainQueue).startStandalone(next: { gallery in
|
||||||
|
params.blockInteraction.set(.single(false))
|
||||||
|
|
||||||
gallery.centralItemUpdated = { messageId in
|
gallery.centralItemUpdated = { messageId in
|
||||||
params.centralItemUpdated?(messageId)
|
params.centralItemUpdated?(messageId)
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
public var knockoutWallpaper: Bool
|
public var knockoutWallpaper: Bool
|
||||||
public var foldersTabAtBottom: Bool
|
public var foldersTabAtBottom: Bool
|
||||||
public var playerEmbedding: Bool
|
public var playerEmbedding: Bool
|
||||||
public var playlistPlayback: Bool
|
|
||||||
public var preferredVideoCodec: String?
|
public var preferredVideoCodec: String?
|
||||||
public var disableVideoAspectScaling: Bool
|
public var disableVideoAspectScaling: Bool
|
||||||
public var enableVoipTcp: Bool
|
public var enableVoipTcp: Bool
|
||||||
@ -65,6 +64,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
public var playerV2: Bool
|
public var playerV2: Bool
|
||||||
public var devRequests: Bool
|
public var devRequests: Bool
|
||||||
public var fakeAds: Bool
|
public var fakeAds: Bool
|
||||||
|
public var conferenceDebug: Bool
|
||||||
|
|
||||||
public static var defaultSettings: ExperimentalUISettings {
|
public static var defaultSettings: ExperimentalUISettings {
|
||||||
return ExperimentalUISettings(
|
return ExperimentalUISettings(
|
||||||
@ -75,7 +75,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
knockoutWallpaper: false,
|
knockoutWallpaper: false,
|
||||||
foldersTabAtBottom: false,
|
foldersTabAtBottom: false,
|
||||||
playerEmbedding: false,
|
playerEmbedding: false,
|
||||||
playlistPlayback: false,
|
|
||||||
preferredVideoCodec: nil,
|
preferredVideoCodec: nil,
|
||||||
disableVideoAspectScaling: false,
|
disableVideoAspectScaling: false,
|
||||||
enableVoipTcp: false,
|
enableVoipTcp: false,
|
||||||
@ -107,7 +106,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
autoBenchmarkReflectors: nil,
|
autoBenchmarkReflectors: nil,
|
||||||
playerV2: false,
|
playerV2: false,
|
||||||
devRequests: false,
|
devRequests: false,
|
||||||
fakeAds: false
|
fakeAds: false,
|
||||||
|
conferenceDebug: false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +119,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
knockoutWallpaper: Bool,
|
knockoutWallpaper: Bool,
|
||||||
foldersTabAtBottom: Bool,
|
foldersTabAtBottom: Bool,
|
||||||
playerEmbedding: Bool,
|
playerEmbedding: Bool,
|
||||||
playlistPlayback: Bool,
|
|
||||||
preferredVideoCodec: String?,
|
preferredVideoCodec: String?,
|
||||||
disableVideoAspectScaling: Bool,
|
disableVideoAspectScaling: Bool,
|
||||||
enableVoipTcp: Bool,
|
enableVoipTcp: Bool,
|
||||||
@ -151,7 +150,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
autoBenchmarkReflectors: Bool?,
|
autoBenchmarkReflectors: Bool?,
|
||||||
playerV2: Bool,
|
playerV2: Bool,
|
||||||
devRequests: Bool,
|
devRequests: Bool,
|
||||||
fakeAds: Bool
|
fakeAds: Bool,
|
||||||
|
conferenceDebug: Bool
|
||||||
) {
|
) {
|
||||||
self.keepChatNavigationStack = keepChatNavigationStack
|
self.keepChatNavigationStack = keepChatNavigationStack
|
||||||
self.skipReadHistory = skipReadHistory
|
self.skipReadHistory = skipReadHistory
|
||||||
@ -160,7 +160,6 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
self.knockoutWallpaper = knockoutWallpaper
|
self.knockoutWallpaper = knockoutWallpaper
|
||||||
self.foldersTabAtBottom = foldersTabAtBottom
|
self.foldersTabAtBottom = foldersTabAtBottom
|
||||||
self.playerEmbedding = playerEmbedding
|
self.playerEmbedding = playerEmbedding
|
||||||
self.playlistPlayback = playlistPlayback
|
|
||||||
self.preferredVideoCodec = preferredVideoCodec
|
self.preferredVideoCodec = preferredVideoCodec
|
||||||
self.disableVideoAspectScaling = disableVideoAspectScaling
|
self.disableVideoAspectScaling = disableVideoAspectScaling
|
||||||
self.enableVoipTcp = enableVoipTcp
|
self.enableVoipTcp = enableVoipTcp
|
||||||
@ -193,6 +192,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
|||||||
self.playerV2 = playerV2
|
self.playerV2 = playerV2
|
||||||
self.devRequests = devRequests
|
self.devRequests = devRequests
|
||||||
self.fakeAds = fakeAds
|
self.fakeAds = fakeAds
|
||||||
|
self.conferenceDebug = conferenceDebug
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
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.knockoutWallpaper = (try container.decodeIfPresent(Int32.self, forKey: "knockoutWallpaper") ?? 0) != 0
|
||||||
self.foldersTabAtBottom = (try container.decodeIfPresent(Int32.self, forKey: "foldersTabAtBottom") ?? 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.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.preferredVideoCodec = try container.decodeIfPresent(String.self.self, forKey: "preferredVideoCodec")
|
||||||
self.disableVideoAspectScaling = (try container.decodeIfPresent(Int32.self, forKey: "disableVideoAspectScaling") ?? 0) != 0
|
self.disableVideoAspectScaling = (try container.decodeIfPresent(Int32.self, forKey: "disableVideoAspectScaling") ?? 0) != 0
|
||||||
self.enableVoipTcp = (try container.decodeIfPresent(Int32.self, forKey: "enableVoipTcp") ?? 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.playerV2 = try container.decodeIfPresent(Bool.self, forKey: "playerV2") ?? false
|
||||||
self.devRequests = try container.decodeIfPresent(Bool.self, forKey: "devRequests") ?? false
|
self.devRequests = try container.decodeIfPresent(Bool.self, forKey: "devRequests") ?? false
|
||||||
self.fakeAds = try container.decodeIfPresent(Bool.self, forKey: "fakeAds") ?? 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 {
|
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.knockoutWallpaper ? 1 : 0) as Int32, forKey: "knockoutWallpaper")
|
||||||
try container.encode((self.foldersTabAtBottom ? 1 : 0) as Int32, forKey: "foldersTabAtBottom")
|
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.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.encodeIfPresent(self.preferredVideoCodec, forKey: "preferredVideoCodec")
|
||||||
try container.encode((self.disableVideoAspectScaling ? 1 : 0) as Int32, forKey: "disableVideoAspectScaling")
|
try container.encode((self.disableVideoAspectScaling ? 1 : 0) as Int32, forKey: "disableVideoAspectScaling")
|
||||||
try container.encode((self.enableVoipTcp ? 1 : 0) as Int32, forKey: "enableVoipTcp")
|
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.playerV2, forKey: "playerV2")
|
||||||
try container.encodeIfPresent(self.devRequests, forKey: "devRequests")
|
try container.encodeIfPresent(self.devRequests, forKey: "devRequests")
|
||||||
try container.encodeIfPresent(self.fakeAds, forKey: "fakeAds")
|
try container.encodeIfPresent(self.fakeAds, forKey: "fakeAds")
|
||||||
|
try container.encodeIfPresent(self.conferenceDebug, forKey: "conferenceDebug")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user