mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 19:30:29 +00:00
Merge branch 'master' of github.com:peter-iakovlev/TelegramUI
# Conflicts: # TelegramUI/TelegramController.swift
This commit is contained in:
commit
b0815c344e
@ -993,6 +993,7 @@
|
|||||||
D0EC6EA61EB9FC2400EBF1C3 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D07551901DDA4FC70073E051 /* libc++.tbd */; };
|
D0EC6EA61EB9FC2400EBF1C3 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D07551901DDA4FC70073E051 /* libc++.tbd */; };
|
||||||
D0EC6EBD1EBA100F00EBF1C3 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0EC6EBC1EBA100F00EBF1C3 /* CoreAudio.framework */; };
|
D0EC6EBD1EBA100F00EBF1C3 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0EC6EBC1EBA100F00EBF1C3 /* CoreAudio.framework */; };
|
||||||
D0EC6FFD1EBA1F2400EBF1C3 /* OngoingCallThreadLocalContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = D0EC6FFC1EBA1F2400EBF1C3 /* OngoingCallThreadLocalContext.mm */; };
|
D0EC6FFD1EBA1F2400EBF1C3 /* OngoingCallThreadLocalContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = D0EC6FFC1EBA1F2400EBF1C3 /* OngoingCallThreadLocalContext.mm */; };
|
||||||
|
D0EEE9A12165585F001292A6 /* DocumentPreviewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EEE9A02165585F001292A6 /* DocumentPreviewController.swift */; };
|
||||||
D0F0AAE01EC1E12C005EE2A5 /* PresentationCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F0AADF1EC1E12C005EE2A5 /* PresentationCall.swift */; };
|
D0F0AAE01EC1E12C005EE2A5 /* PresentationCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F0AADF1EC1E12C005EE2A5 /* PresentationCall.swift */; };
|
||||||
D0F0AAE21EC20EF8005EE2A5 /* CallControllerStatusNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F0AAE11EC20EF8005EE2A5 /* CallControllerStatusNode.swift */; };
|
D0F0AAE21EC20EF8005EE2A5 /* CallControllerStatusNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F0AAE11EC20EF8005EE2A5 /* CallControllerStatusNode.swift */; };
|
||||||
D0F0AAE41EC21AAA005EE2A5 /* CallControllerButtonsNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F0AAE31EC21AAA005EE2A5 /* CallControllerButtonsNode.swift */; };
|
D0F0AAE41EC21AAA005EE2A5 /* CallControllerButtonsNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F0AAE31EC21AAA005EE2A5 /* CallControllerButtonsNode.swift */; };
|
||||||
@ -1970,6 +1971,7 @@
|
|||||||
D0EC6FFC1EBA1F2400EBF1C3 /* OngoingCallThreadLocalContext.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = OngoingCallThreadLocalContext.mm; sourceTree = "<group>"; };
|
D0EC6FFC1EBA1F2400EBF1C3 /* OngoingCallThreadLocalContext.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = OngoingCallThreadLocalContext.mm; sourceTree = "<group>"; };
|
||||||
D0ED5D4A1DC806D7007CBB15 /* ApplicationSpecificData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApplicationSpecificData.swift; sourceTree = "<group>"; };
|
D0ED5D4A1DC806D7007CBB15 /* ApplicationSpecificData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApplicationSpecificData.swift; sourceTree = "<group>"; };
|
||||||
D0EE97191D88BCA0006C18E1 /* ChatInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatInfo.swift; sourceTree = "<group>"; };
|
D0EE97191D88BCA0006C18E1 /* ChatInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatInfo.swift; sourceTree = "<group>"; };
|
||||||
|
D0EEE9A02165585F001292A6 /* DocumentPreviewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentPreviewController.swift; sourceTree = "<group>"; };
|
||||||
D0EF40DC1E72F00E000DFCD4 /* SelectivePrivacySettingsPeersController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectivePrivacySettingsPeersController.swift; sourceTree = "<group>"; };
|
D0EF40DC1E72F00E000DFCD4 /* SelectivePrivacySettingsPeersController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectivePrivacySettingsPeersController.swift; sourceTree = "<group>"; };
|
||||||
D0EF40DE1E73100D000DFCD4 /* ChatHistoryNavigationStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatHistoryNavigationStack.swift; sourceTree = "<group>"; };
|
D0EF40DE1E73100D000DFCD4 /* ChatHistoryNavigationStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatHistoryNavigationStack.swift; sourceTree = "<group>"; };
|
||||||
D0EFD8951DDE8249009E508A /* LegacyLocationPicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyLocationPicker.swift; sourceTree = "<group>"; };
|
D0EFD8951DDE8249009E508A /* LegacyLocationPicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyLocationPicker.swift; sourceTree = "<group>"; };
|
||||||
@ -3779,6 +3781,14 @@
|
|||||||
name = "Peer Info";
|
name = "Peer Info";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
D0EEE99F2165583B001292A6 /* Document */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D0EEE9A02165585F001292A6 /* DocumentPreviewController.swift */,
|
||||||
|
);
|
||||||
|
name = Document;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
D0F53BF51E79592300117362 /* Sign Up */ = {
|
D0F53BF51E79592300117362 /* Sign Up */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -4132,6 +4142,7 @@
|
|||||||
D0B7F8DF1D8A17D20045D939 /* Collection */,
|
D0B7F8DF1D8A17D20045D939 /* Collection */,
|
||||||
D0F69E4F1D6B8BC40046BCD6 /* Gallery */,
|
D0F69E4F1D6B8BC40046BCD6 /* Gallery */,
|
||||||
D0575AF81EA0FD94006F2541 /* Avatar Gallery */,
|
D0575AF81EA0FD94006F2541 /* Avatar Gallery */,
|
||||||
|
D0EEE99F2165583B001292A6 /* Document */,
|
||||||
D0104F261F471702004E4881 /* Instant Page Gallery */,
|
D0104F261F471702004E4881 /* Instant Page Gallery */,
|
||||||
D0F69E671D6B8C030046BCD6 /* Map Input */,
|
D0F69E671D6B8C030046BCD6 /* Map Input */,
|
||||||
D07827CC1E03F32C00071108 /* Instant Page */,
|
D07827CC1E03F32C00071108 /* Instant Page */,
|
||||||
@ -5210,6 +5221,7 @@
|
|||||||
D06CF82920D0119500AC4CFF /* SecureIdAuthListFieldNode.swift in Sources */,
|
D06CF82920D0119500AC4CFF /* SecureIdAuthListFieldNode.swift in Sources */,
|
||||||
D0EC6DCD1EB9F58900EBF1C3 /* ChatInputContextPanelNode.swift in Sources */,
|
D0EC6DCD1EB9F58900EBF1C3 /* ChatInputContextPanelNode.swift in Sources */,
|
||||||
D0F8C399201774AF00236FC5 /* FeedGroupingControllerNode.swift in Sources */,
|
D0F8C399201774AF00236FC5 /* FeedGroupingControllerNode.swift in Sources */,
|
||||||
|
D0EEE9A12165585F001292A6 /* DocumentPreviewController.swift in Sources */,
|
||||||
D0EC6DCE1EB9F58900EBF1C3 /* HorizontalStickersChatContextPanelNode.swift in Sources */,
|
D0EC6DCE1EB9F58900EBF1C3 /* HorizontalStickersChatContextPanelNode.swift in Sources */,
|
||||||
D0BCC3D2203F0A6C008126C2 /* StringForMessageTimestampStatus.swift in Sources */,
|
D0BCC3D2203F0A6C008126C2 /* StringForMessageTimestampStatus.swift in Sources */,
|
||||||
D0EC6DCF1EB9F58900EBF1C3 /* HorizontalStickerGridItem.swift in Sources */,
|
D0EC6DCF1EB9F58900EBF1C3 /* HorizontalStickerGridItem.swift in Sources */,
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<key>TelegramUI.xcscheme</key>
|
<key>TelegramUI.xcscheme</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>3</integer>
|
<integer>2</integer>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>SuppressBuildableAutocreation</key>
|
<key>SuppressBuildableAutocreation</key>
|
||||||
|
|||||||
@ -315,7 +315,8 @@ private let hasApplePaySupport: Bool = PKPaymentAuthorizationViewController.canM
|
|||||||
private var applePayProviders = Set<String>([
|
private var applePayProviders = Set<String>([
|
||||||
"stripe",
|
"stripe",
|
||||||
"sberbank",
|
"sberbank",
|
||||||
"yandex"
|
"yandex",
|
||||||
|
"privatbank"
|
||||||
])
|
])
|
||||||
|
|
||||||
private func availablePaymentMethods(current: BotCheckoutPaymentMethod?, supportsApplePay: Bool) -> [BotCheckoutPaymentMethod] {
|
private func availablePaymentMethods(current: BotCheckoutPaymentMethod?, supportsApplePay: Bool) -> [BotCheckoutPaymentMethod] {
|
||||||
|
|||||||
@ -2647,27 +2647,29 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin
|
|||||||
self.interfaceInteraction = interfaceInteraction
|
self.interfaceInteraction = interfaceInteraction
|
||||||
self.chatDisplayNode.interfaceInteraction = interfaceInteraction
|
self.chatDisplayNode.interfaceInteraction = interfaceInteraction
|
||||||
|
|
||||||
self.galleryHiddenMesageAndMediaDisposable.set(self.account.telegramApplicationContext.mediaManager.galleryHiddenMediaManager.hiddenIds().start(next: { [weak self] ids in
|
if let mediaManager = self.account.telegramApplicationContext.mediaManager {
|
||||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
self.galleryHiddenMesageAndMediaDisposable.set(mediaManager.galleryHiddenMediaManager.hiddenIds().start(next: { [weak self] ids in
|
||||||
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
||||||
|
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
||||||
for id in ids {
|
|
||||||
if case let .chat(messageId, media) = id {
|
|
||||||
messageIdAndMedia[messageId] = [media]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//if controllerInteraction.hiddenMedia != messageIdAndMedia {
|
|
||||||
controllerInteraction.hiddenMedia = messageIdAndMedia
|
|
||||||
|
|
||||||
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
for id in ids {
|
||||||
if let itemNode = itemNode as? ChatMessageItemView {
|
if case let .chat(messageId, media) = id {
|
||||||
itemNode.updateHiddenMedia()
|
messageIdAndMedia[messageId] = [media]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//}
|
|
||||||
}
|
//if controllerInteraction.hiddenMedia != messageIdAndMedia {
|
||||||
}))
|
controllerInteraction.hiddenMedia = messageIdAndMedia
|
||||||
|
|
||||||
|
strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||||
|
if let itemNode = itemNode as? ChatMessageItemView {
|
||||||
|
itemNode.updateHiddenMedia()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
self.chatDisplayNode.dismissAsOverlay = { [weak self] in
|
self.chatDisplayNode.dismissAsOverlay = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
@ -3692,8 +3694,11 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin
|
|||||||
self.audioRecorderFeedback = HapticFeedback()
|
self.audioRecorderFeedback = HapticFeedback()
|
||||||
self.audioRecorderFeedback?.prepareTap()
|
self.audioRecorderFeedback?.prepareTap()
|
||||||
}
|
}
|
||||||
self.audioRecorder.set(applicationContext.mediaManager.audioRecorder(beginWithTone: beginWithTone, applicationBindings: applicationContext.applicationBindings, beganWithTone: { _ in
|
|
||||||
}))
|
if let mediaManager = applicationContext.mediaManager {
|
||||||
|
self.audioRecorder.set(mediaManager.audioRecorder(beginWithTone: beginWithTone, applicationBindings: applicationContext.applicationBindings, beganWithTone: { _ in
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,7 +72,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
|||||||
|
|
||||||
private let deleteButton: UIButton
|
private let deleteButton: UIButton
|
||||||
private let actionButton: UIButton
|
private let actionButton: UIButton
|
||||||
private let textNode: ASTextNode
|
private let textNode: ImmediateTextNode
|
||||||
private let authorNameNode: ASTextNode
|
private let authorNameNode: ASTextNode
|
||||||
private let dateNode: ASTextNode
|
private let dateNode: ASTextNode
|
||||||
private let backwardButton: HighlightableButtonNode
|
private let backwardButton: HighlightableButtonNode
|
||||||
@ -138,7 +138,8 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
|||||||
self.deleteButton.setImage(deleteImage, for: [.normal])
|
self.deleteButton.setImage(deleteImage, for: [.normal])
|
||||||
self.actionButton.setImage(actionImage, for: [.normal])
|
self.actionButton.setImage(actionImage, for: [.normal])
|
||||||
|
|
||||||
self.textNode = ASTextNode()
|
self.textNode = ImmediateTextNode()
|
||||||
|
self.textNode.maximumNumberOfLines = 10
|
||||||
self.textNode.isLayerBacked = true
|
self.textNode.isLayerBacked = true
|
||||||
self.authorNameNode = ASTextNode()
|
self.authorNameNode = ASTextNode()
|
||||||
self.authorNameNode.maximumNumberOfLines = 1
|
self.authorNameNode.maximumNumberOfLines = 1
|
||||||
@ -295,7 +296,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode {
|
|||||||
let sideInset: CGFloat = 8.0 + leftInset
|
let sideInset: CGFloat = 8.0 + leftInset
|
||||||
let topInset: CGFloat = 8.0
|
let topInset: CGFloat = 8.0
|
||||||
let textBottomInset: CGFloat = 8.0
|
let textBottomInset: CGFloat = 8.0
|
||||||
let textSize = self.textNode.measure(CGSize(width: width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
let textSize = self.textNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||||
panelHeight += textSize.height + topInset + textBottomInset
|
panelHeight += textSize.height + topInset + textBottomInset
|
||||||
textFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: textSize)
|
textFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: textSize)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -328,7 +328,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
var inlineImageDimensions: CGSize?
|
var inlineImageDimensions: CGSize?
|
||||||
var inlineImageSize: CGSize?
|
var inlineImageSize: CGSize?
|
||||||
var updateInlineImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
|
var updateInlineImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
|
||||||
var textCutout: TextNodeCutout?
|
var textCutout = TextNodeCutout()
|
||||||
var initialWidth: CGFloat = CGFloat.greatestFiniteMagnitude
|
var initialWidth: CGFloat = CGFloat.greatestFiniteMagnitude
|
||||||
var refineContentImageLayout: ((CGSize, ImageCorners) -> (CGFloat, (CGFloat) -> (CGSize, (ContainedViewLayoutTransition) -> ChatMessageInteractiveMediaNode)))?
|
var refineContentImageLayout: ((CGSize, ImageCorners) -> (CGFloat, (CGFloat) -> (CGSize, (ContainedViewLayoutTransition) -> ChatMessageInteractiveMediaNode)))?
|
||||||
var refineContentFileLayout: ((CGSize) -> (CGFloat, (CGFloat) -> (CGSize, () -> ChatMessageInteractiveFileNode)))?
|
var refineContentFileLayout: ((CGSize) -> (CGFloat, (CGFloat) -> (CGSize, () -> ChatMessageInteractiveFileNode)))?
|
||||||
@ -435,7 +435,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
inlineImageSize = CGSize(width: 54.0, height: 54.0)
|
inlineImageSize = CGSize(width: 54.0, height: 54.0)
|
||||||
|
|
||||||
if let inlineImageSize = inlineImageSize {
|
if let inlineImageSize = inlineImageSize {
|
||||||
textCutout = TextNodeCutout(position: .TopRight, size: CGSize(width: inlineImageSize.width + 10.0, height: inlineImageSize.height + 10.0))
|
textCutout.topRight = CGSize(width: inlineImageSize.width + 10.0, height: inlineImageSize.height + 10.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,23 +508,28 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
updatedAdditionalImageBadge = currentAdditionalImageBadgeNode ?? ChatMessageInteractiveMediaBadge()
|
updatedAdditionalImageBadge = currentAdditionalImageBadgeNode ?? ChatMessageInteractiveMediaBadge()
|
||||||
}
|
}
|
||||||
|
|
||||||
let (textLayout, textApply) = textAsyncLayout(TextNodeLayoutArguments(attributedString: textString, backgroundColor: nil, maximumNumberOfLines: 12, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: textCutout, insets: UIEdgeInsets()))
|
var upatedTextCutout = textCutout
|
||||||
|
if statusInText, let (statusSize, _) = statusSizeAndApply {
|
||||||
|
upatedTextCutout.bottomRight = statusSize
|
||||||
|
}
|
||||||
|
|
||||||
|
let (textLayout, textApply) = textAsyncLayout(TextNodeLayoutArguments(attributedString: textString, backgroundColor: nil, maximumNumberOfLines: 12, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: upatedTextCutout, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
var textFrame = CGRect(origin: CGPoint(), size: textLayout.size)
|
var textFrame = CGRect(origin: CGPoint(), size: textLayout.size)
|
||||||
|
|
||||||
var statusFrame: CGRect?
|
var statusFrame: CGRect?
|
||||||
|
|
||||||
if statusInText, let (statusSize, _) = statusSizeAndApply {
|
if statusInText, let (statusSize, _) = statusSizeAndApply {
|
||||||
var frame = CGRect(origin: CGPoint(), size: statusSize)
|
var frame = CGRect(origin: CGPoint(x: textFrame.maxX - statusSize.width, y: textFrame.maxY - statusSize.height), size: statusSize)
|
||||||
|
|
||||||
let trailingLineWidth = textLayout.trailingLineWidth
|
/*let trailingLineWidth = textLayout.trailingLineWidth
|
||||||
if textLayout.size.width - trailingLineWidth >= statusSize.width {
|
if textLayout.size.width - trailingLineWidth >= statusSize.width {
|
||||||
frame.origin = CGPoint(x: textFrame.maxX - statusSize.width, y: textFrame.maxY - statusSize.height)
|
frame.origin = CGPoint(x: textFrame.maxX - statusSize.width, y: textFrame.maxY - statusSize.height)
|
||||||
} else if trailingLineWidth + statusSize.width < textConstrainedSize.width {
|
} else if trailingLineWidth + statusSize.width < textConstrainedSize.width {
|
||||||
frame.origin = CGPoint(x: textFrame.minX + trailingLineWidth, y: textFrame.maxY - statusSize.height)
|
frame.origin = CGPoint(x: textFrame.minX + trailingLineWidth, y: textFrame.maxY - statusSize.height)
|
||||||
} else {
|
} else {
|
||||||
frame.origin = CGPoint(x: textFrame.maxX - statusSize.width, y: textFrame.maxY)
|
frame.origin = CGPoint(x: textFrame.maxX - statusSize.width, y: textFrame.maxY)
|
||||||
}
|
}*/
|
||||||
|
|
||||||
if let inlineImageSize = inlineImageSize {
|
if let inlineImageSize = inlineImageSize {
|
||||||
if frame.origin.y < inlineImageSize.height + 4.0 {
|
if frame.origin.y < inlineImageSize.height + 4.0 {
|
||||||
|
|||||||
@ -101,7 +101,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
case .playbackStatus:
|
case .playbackStatus:
|
||||||
if let account = self.account, let applicationContext = account.applicationContext as? TelegramApplicationContext, let message = self.message, let type = peerMessageMediaPlayerType(message) {
|
if let account = self.account, let applicationContext = account.applicationContext as? TelegramApplicationContext, let message = self.message, let type = peerMessageMediaPlayerType(message) {
|
||||||
applicationContext.mediaManager.playlistControl(.playback(.togglePlayPause), type: type)
|
applicationContext.mediaManager?.playlistControl(.playback(.togglePlayPause), type: type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -418,7 +418,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
waveformScrubbingNode.hitTestSlop = UIEdgeInsetsMake(-10.0, 0.0, -10.0, 0.0)
|
waveformScrubbingNode.hitTestSlop = UIEdgeInsetsMake(-10.0, 0.0, -10.0, 0.0)
|
||||||
waveformScrubbingNode.seek = { timestamp in
|
waveformScrubbingNode.seek = { timestamp in
|
||||||
if let strongSelf = self, let account = strongSelf.account, let message = strongSelf.message, let type = peerMessageMediaPlayerType(message) {
|
if let strongSelf = self, let account = strongSelf.account, let message = strongSelf.message, let type = peerMessageMediaPlayerType(message) {
|
||||||
account.telegramApplicationContext.mediaManager.playlistControl(.seek(timestamp), type: type)
|
account.telegramApplicationContext.mediaManager?.playlistControl(.seek(timestamp), type: type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
waveformScrubbingNode.status = strongSelf.playbackStatus.get()
|
waveformScrubbingNode.status = strongSelf.playbackStatus.get()
|
||||||
|
|||||||
@ -328,28 +328,30 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
videoNode?.removeFromSupernode()
|
videoNode?.removeFromSupernode()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
let videoNode = UniversalVideoNode(postbox: item.account.postbox, audioSession: item.account.telegramApplicationContext.mediaManager.audioSession, manager: item.account.telegramApplicationContext.mediaManager.universalVideoManager, decoration: ChatBubbleInstantVideoDecoration(diameter: displaySize.width + 2.0, backgroundImage: instantVideoBackgroundImage, tapped: {
|
if let mediaManager = item.account.telegramApplicationContext.mediaManager {
|
||||||
if let strongSelf = self {
|
let videoNode = UniversalVideoNode(postbox: item.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: ChatBubbleInstantVideoDecoration(diameter: displaySize.width + 2.0, backgroundImage: instantVideoBackgroundImage, tapped: {
|
||||||
if let item = strongSelf.item {
|
if let strongSelf = self {
|
||||||
if strongSelf.infoBackgroundNode.alpha.isZero {
|
if let item = strongSelf.item {
|
||||||
item.account.telegramApplicationContext.mediaManager.playlistControl(.playback(.togglePlayPause), type: .voice)
|
if strongSelf.infoBackgroundNode.alpha.isZero {
|
||||||
} else {
|
item.account.telegramApplicationContext.mediaManager?.playlistControl(.playback(.togglePlayPause), type: .voice)
|
||||||
//let _ = item.controllerInteraction.openMessage(item.message)
|
} else {
|
||||||
|
//let _ = item.controllerInteraction.openMessage(item.message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}), content: NativeVideoContent(id: .message(item.message.id, item.message.stableId, telegramFile.fileId), fileReference: .message(message: MessageReference(item.message), media: telegramFile), streamVideo: false, enableSound: false), priority: .embedded, autoplay: true)
|
||||||
}), content: NativeVideoContent(id: .message(item.message.id, item.message.stableId, telegramFile.fileId), fileReference: .message(message: MessageReference(item.message), media: telegramFile), streamVideo: false, enableSound: false), priority: .embedded, autoplay: true)
|
let previousVideoNode = strongSelf.videoNode
|
||||||
let previousVideoNode = strongSelf.videoNode
|
strongSelf.videoNode = videoNode
|
||||||
strongSelf.videoNode = videoNode
|
strongSelf.insertSubnode(videoNode, belowSubnode: previousVideoNode ?? strongSelf.dateAndStatusNode)
|
||||||
strongSelf.insertSubnode(videoNode, belowSubnode: previousVideoNode ?? strongSelf.dateAndStatusNode)
|
videoNode.canAttachContent = strongSelf.shouldAcquireVideoContext
|
||||||
videoNode.canAttachContent = strongSelf.shouldAcquireVideoContext
|
|
||||||
|
|
||||||
if isSecretMedia {
|
if isSecretMedia {
|
||||||
let updatedSecretPlaceholderSignal = chatSecretMessageVideo(account: item.account, videoReference: .message(message: MessageReference(item.message), media: telegramFile))
|
let updatedSecretPlaceholderSignal = chatSecretMessageVideo(account: item.account, videoReference: .message(message: MessageReference(item.message), media: telegramFile))
|
||||||
strongSelf.secretVideoPlaceholder.setSignal(updatedSecretPlaceholderSignal)
|
strongSelf.secretVideoPlaceholder.setSignal(updatedSecretPlaceholderSignal)
|
||||||
if strongSelf.secretVideoPlaceholder.supernode == nil {
|
if strongSelf.secretVideoPlaceholder.supernode == nil {
|
||||||
strongSelf.insertSubnode(strongSelf.secretVideoPlaceholderBackground, belowSubnode: videoNode)
|
strongSelf.insertSubnode(strongSelf.secretVideoPlaceholderBackground, belowSubnode: videoNode)
|
||||||
strongSelf.insertSubnode(strongSelf.secretVideoPlaceholder, belowSubnode: videoNode)
|
strongSelf.insertSubnode(strongSelf.secretVideoPlaceholder, belowSubnode: videoNode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
strongSelf.secretVideoPlaceholder.removeFromSupernode()
|
strongSelf.secretVideoPlaceholder.removeFromSupernode()
|
||||||
@ -548,7 +550,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let item = self.item, let videoNode = self.videoNode, videoNode.frame.contains(location) {
|
if let _ = self.item, let videoNode = self.videoNode, videoNode.frame.contains(location) {
|
||||||
self.activateVideoPlayback()
|
self.activateVideoPlayback()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -572,7 +574,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if self.infoBackgroundNode.alpha.isZero {
|
if self.infoBackgroundNode.alpha.isZero {
|
||||||
item.account.telegramApplicationContext.mediaManager.playlistControl(.playback(.togglePlayPause), type: .voice)
|
item.account.telegramApplicationContext.mediaManager?.playlistControl(.playback(.togglePlayPause), type: .voice)
|
||||||
} else {
|
} else {
|
||||||
let _ = item.controllerInteraction.openMessage(item.message)
|
let _ = item.controllerInteraction.openMessage(item.message)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -384,9 +384,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
|
|||||||
strongSelf.videoNode = nil
|
strongSelf.videoNode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if replaceVideoNode, let updatedVideoFile = updateVideoFile {
|
if replaceVideoNode, let updatedVideoFile = updateVideoFile, let mediaManager = account.telegramApplicationContext.mediaManager {
|
||||||
let cornerRadius: CGFloat = arguments.corners.topLeft.radius
|
let cornerRadius: CGFloat = arguments.corners.topLeft.radius
|
||||||
let videoNode = UniversalVideoNode(postbox: account.postbox, audioSession: account.telegramApplicationContext.mediaManager.audioSession, manager: account.telegramApplicationContext.mediaManager.universalVideoManager, decoration: ChatBubbleVideoDecoration(cornerRadius: cornerRadius, nativeSize: nativeSize), content: NativeVideoContent(id: .message(message.id, message.stableId, updatedVideoFile.fileId), fileReference: .message(message: MessageReference(message), media: updatedVideoFile), enableSound: false, fetchAutomatically: false), priority: .embedded)
|
let videoNode = UniversalVideoNode(postbox: account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: ChatBubbleVideoDecoration(cornerRadius: cornerRadius, nativeSize: nativeSize), content: NativeVideoContent(id: .message(message.id, message.stableId, updatedVideoFile.fileId), fileReference: .message(message: MessageReference(message), media: updatedVideoFile), enableSound: false, fetchAutomatically: false), priority: .embedded)
|
||||||
videoNode.isUserInteractionEnabled = false
|
videoNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
strongSelf.videoNode = videoNode
|
strongSelf.videoNode = videoNode
|
||||||
|
|||||||
@ -168,7 +168,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
|
|
||||||
var cutout: TextNodeCutout?
|
var cutout: TextNodeCutout?
|
||||||
if let statusSize = statusSize {
|
if let statusSize = statusSize {
|
||||||
cutout = TextNodeCutout(position: .BottomRight, size: statusSize)
|
cutout = TextNodeCutout(bottomRight: statusSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: cutout, insets: UIEdgeInsets()))
|
let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: cutout, insets: UIEdgeInsets()))
|
||||||
@ -178,16 +178,14 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
|
|
||||||
var statusFrame: CGRect?
|
var statusFrame: CGRect?
|
||||||
if let statusSize = statusSize {
|
if let statusSize = statusSize {
|
||||||
var frame = CGRect(origin: CGPoint(), size: statusSize)
|
var frame = CGRect(origin: CGPoint(x: textFrame.maxX - statusSize.width, y: textFrame.maxY - statusSize.height), size: statusSize)
|
||||||
|
|
||||||
let trailingLineWidth = textLayout.trailingLineWidth
|
/*let trailingLineWidth = textLayout.trailingLineWidth
|
||||||
if textSize.width - trailingLineWidth >= statusSize.width {
|
if textSize.width - trailingLineWidth >= statusSize.width {
|
||||||
frame.origin = CGPoint(x: textFrame.maxX - statusSize.width, y: textFrame.maxY - statusSize.height)
|
frame.origin = CGPoint(x: textFrame.maxX - statusSize.width, y: textFrame.maxY - statusSize.height)
|
||||||
} else if trailingLineWidth + statusSize.width < textConstrainedSize.width {
|
} else if trailingLineWidth + statusSize.width < textConstrainedSize.width {
|
||||||
frame.origin = CGPoint(x: textFrame.minX + trailingLineWidth, y: textFrame.maxY - statusSize.height)
|
frame.origin = CGPoint(x: textFrame.minX + trailingLineWidth, y: textFrame.maxY - statusSize.height)
|
||||||
} else {
|
}*/
|
||||||
frame.origin = CGPoint(x: textFrame.maxX - statusSize.width, y: textFrame.maxY)
|
|
||||||
}
|
|
||||||
statusFrame = frame
|
statusFrame = frame
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -397,27 +397,30 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
self.historyDisposable = appliedTransition.start()
|
self.historyDisposable = appliedTransition.start()
|
||||||
|
|
||||||
self.galleryHiddenMesageAndMediaDisposable.set(self.account.telegramApplicationContext.mediaManager.galleryHiddenMediaManager.hiddenIds().start(next: { [weak self] ids in
|
|
||||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
if let mediaManager = self.account.telegramApplicationContext.mediaManager {
|
||||||
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
self.galleryHiddenMesageAndMediaDisposable.set(mediaManager.galleryHiddenMediaManager.hiddenIds().start(next: { [weak self] ids in
|
||||||
|
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
||||||
for id in ids {
|
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
||||||
if case let .chat(messageId, media) = id {
|
|
||||||
messageIdAndMedia[messageId] = [media]
|
for id in ids {
|
||||||
|
if case let .chat(messageId, media) = id {
|
||||||
|
messageIdAndMedia[messageId] = [media]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
//if controllerInteraction.hiddenMedia != messageIdAndMedia {
|
||||||
//if controllerInteraction.hiddenMedia != messageIdAndMedia {
|
controllerInteraction.hiddenMedia = messageIdAndMedia
|
||||||
controllerInteraction.hiddenMedia = messageIdAndMedia
|
|
||||||
|
strongSelf.listNode.forEachItemNode { itemNode in
|
||||||
strongSelf.listNode.forEachItemNode { itemNode in
|
if let itemNode = itemNode as? ChatMessageItemView {
|
||||||
if let itemNode = itemNode as? ChatMessageItemView {
|
itemNode.updateHiddenMedia()
|
||||||
itemNode.updateHiddenMedia()
|
}
|
||||||
}
|
}
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
//}
|
}))
|
||||||
}
|
}
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
|||||||
@ -109,8 +109,8 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
|
|||||||
if self.mediaPlayer != nil {
|
if self.mediaPlayer != nil {
|
||||||
self.mediaPlayer?.pause()
|
self.mediaPlayer?.pause()
|
||||||
}
|
}
|
||||||
if let account = self.account {
|
if let account = self.account, let mediaManager = account.telegramApplicationContext.mediaManager {
|
||||||
let mediaPlayer = MediaPlayer(audioSessionManager: account.telegramApplicationContext.mediaManager.audioSession, postbox: account.postbox, resourceReference: .standalone(resource: recordedMediaPreview.resource), streamable: false, video: false, preferSoftwareDecoding: false, enableSound: true, fetchAutomatically: true)
|
let mediaPlayer = MediaPlayer(audioSessionManager: mediaManager.audioSession, postbox: account.postbox, resourceReference: .standalone(resource: recordedMediaPreview.resource), streamable: false, video: false, preferSoftwareDecoding: false, enableSound: true, fetchAutomatically: true)
|
||||||
self.mediaPlayer = mediaPlayer
|
self.mediaPlayer = mediaPlayer
|
||||||
self.durationLabel.defaultDuration = Double(recordedMediaPreview.duration)
|
self.durationLabel.defaultDuration = Double(recordedMediaPreview.duration)
|
||||||
self.durationLabel.status = mediaPlayer.status
|
self.durationLabel.status = mediaPlayer.status
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import AsyncDisplayKit
|
|||||||
|
|
||||||
final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode {
|
final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||||
private let separatorNode: ASDisplayNode
|
private let separatorNode: ASDisplayNode
|
||||||
private let titleNode: ASTextNode
|
private let titleNode: ImmediateTextNode
|
||||||
|
|
||||||
private var theme: PresentationTheme?
|
private var theme: PresentationTheme?
|
||||||
private var strings: PresentationStrings?
|
private var strings: PresentationStrings?
|
||||||
@ -13,7 +13,7 @@ final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
self.separatorNode = ASDisplayNode()
|
self.separatorNode = ASDisplayNode()
|
||||||
self.separatorNode.isLayerBacked = true
|
self.separatorNode.isLayerBacked = true
|
||||||
|
|
||||||
self.titleNode = ASTextNode()
|
self.titleNode = ImmediateTextNode()
|
||||||
self.titleNode.maximumNumberOfLines = 1
|
self.titleNode.maximumNumberOfLines = 1
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
@ -38,7 +38,7 @@ final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
|
|
||||||
let panelHeight: CGFloat = 40.0
|
let panelHeight: CGFloat = 40.0
|
||||||
|
|
||||||
let titleSize = self.titleNode.measure(CGSize(width: width - leftInset - rightInset, height: 100.0))
|
let titleSize = self.titleNode.updateLayout(CGSize(width: width - leftInset - rightInset, height: 100.0))
|
||||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floor((width - titleSize.width) / 2.0), y: floor((panelHeight - titleSize.height) / 2.0)), size: titleSize))
|
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floor((width - titleSize.width) / 2.0), y: floor((panelHeight - titleSize.height) / 2.0)), size: titleSize))
|
||||||
|
|
||||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel)))
|
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel)))
|
||||||
|
|||||||
@ -14,7 +14,7 @@ enum ChatTitleContent {
|
|||||||
private final class ChatTitleNetworkStatusNode: ASDisplayNode {
|
private final class ChatTitleNetworkStatusNode: ASDisplayNode {
|
||||||
private var theme: PresentationTheme
|
private var theme: PresentationTheme
|
||||||
|
|
||||||
private let titleNode: ASTextNode
|
private let titleNode: ImmediateTextNode
|
||||||
private let activityIndicator: ActivityIndicator
|
private let activityIndicator: ActivityIndicator
|
||||||
|
|
||||||
var title: String = "" {
|
var title: String = "" {
|
||||||
@ -28,11 +28,10 @@ private final class ChatTitleNetworkStatusNode: ASDisplayNode {
|
|||||||
init(theme: PresentationTheme) {
|
init(theme: PresentationTheme) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
|
||||||
self.titleNode = ASTextNode()
|
self.titleNode = ImmediateTextNode()
|
||||||
self.titleNode.isLayerBacked = true
|
self.titleNode.isLayerBacked = true
|
||||||
self.titleNode.displaysAsynchronously = false
|
self.titleNode.displaysAsynchronously = false
|
||||||
self.titleNode.maximumNumberOfLines = 1
|
self.titleNode.maximumNumberOfLines = 1
|
||||||
self.titleNode.truncationMode = .byTruncatingTail
|
|
||||||
self.titleNode.isOpaque = false
|
self.titleNode.isOpaque = false
|
||||||
self.titleNode.isUserInteractionEnabled = false
|
self.titleNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
@ -57,7 +56,7 @@ private final class ChatTitleNetworkStatusNode: ASDisplayNode {
|
|||||||
let indicatorSize = self.activityIndicator.bounds.size
|
let indicatorSize = self.activityIndicator.bounds.size
|
||||||
let indicatorPadding = indicatorSize.width + 6.0
|
let indicatorPadding = indicatorSize.width + 6.0
|
||||||
|
|
||||||
let titleSize = self.titleNode.measure(CGSize(width: max(1.0, size.width - indicatorPadding), height: size.height))
|
let titleSize = self.titleNode.updateLayout(CGSize(width: max(1.0, size.width - indicatorPadding), height: size.height))
|
||||||
let combinedHeight = titleSize.height
|
let combinedHeight = titleSize.height
|
||||||
|
|
||||||
let titleFrame = CGRect(origin: CGPoint(x: indicatorPadding + floor((size.width - titleSize.width - indicatorPadding) / 2.0), y: floor((size.height - combinedHeight) / 2.0)), size: titleSize)
|
let titleFrame = CGRect(origin: CGPoint(x: indicatorPadding + floor((size.width - titleSize.width - indicatorPadding) / 2.0), y: floor((size.height - combinedHeight) / 2.0)), size: titleSize)
|
||||||
@ -81,11 +80,11 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
private var dateTimeFormat: PresentationDateTimeFormat
|
private var dateTimeFormat: PresentationDateTimeFormat
|
||||||
|
|
||||||
private let contentContainer: ASDisplayNode
|
private let contentContainer: ASDisplayNode
|
||||||
private let titleNode: ASTextNode
|
private let titleNode: ImmediateTextNode
|
||||||
private let titleLeftIconNode: ASImageNode
|
private let titleLeftIconNode: ASImageNode
|
||||||
private let titleRightIconNode: ASImageNode
|
private let titleRightIconNode: ASImageNode
|
||||||
private let infoNode: ASTextNode
|
private let infoNode: ImmediateTextNode
|
||||||
private let typingNode: ASTextNode
|
private let typingNode: ImmediateTextNode
|
||||||
private var typingIndicator: TGModernConversationTitleActivityIndicator?
|
private var typingIndicator: TGModernConversationTitleActivityIndicator?
|
||||||
private let button: HighlightTrackingButtonNode
|
private let button: HighlightTrackingButtonNode
|
||||||
|
|
||||||
@ -428,10 +427,9 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
|
|
||||||
self.contentContainer = ASDisplayNode()
|
self.contentContainer = ASDisplayNode()
|
||||||
|
|
||||||
self.titleNode = ASTextNode()
|
self.titleNode = ImmediateTextNode()
|
||||||
self.titleNode.displaysAsynchronously = false
|
self.titleNode.displaysAsynchronously = false
|
||||||
self.titleNode.maximumNumberOfLines = 1
|
self.titleNode.maximumNumberOfLines = 1
|
||||||
self.titleNode.truncationMode = .byTruncatingTail
|
|
||||||
self.titleNode.isOpaque = false
|
self.titleNode.isOpaque = false
|
||||||
|
|
||||||
self.titleLeftIconNode = ASImageNode()
|
self.titleLeftIconNode = ASImageNode()
|
||||||
@ -444,16 +442,14 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
self.titleRightIconNode.displayWithoutProcessing = true
|
self.titleRightIconNode.displayWithoutProcessing = true
|
||||||
self.titleRightIconNode.displaysAsynchronously = false
|
self.titleRightIconNode.displaysAsynchronously = false
|
||||||
|
|
||||||
self.infoNode = ASTextNode()
|
self.infoNode = ImmediateTextNode()
|
||||||
self.infoNode.displaysAsynchronously = false
|
self.infoNode.displaysAsynchronously = false
|
||||||
self.infoNode.maximumNumberOfLines = 1
|
self.infoNode.maximumNumberOfLines = 1
|
||||||
self.infoNode.truncationMode = .byTruncatingTail
|
|
||||||
self.infoNode.isOpaque = false
|
self.infoNode.isOpaque = false
|
||||||
|
|
||||||
self.typingNode = ASTextNode()
|
self.typingNode = ImmediateTextNode()
|
||||||
self.typingNode.displaysAsynchronously = false
|
self.typingNode.displaysAsynchronously = false
|
||||||
self.typingNode.maximumNumberOfLines = 1
|
self.typingNode.maximumNumberOfLines = 1
|
||||||
self.typingNode.truncationMode = .byTruncatingTail
|
|
||||||
self.typingNode.isOpaque = false
|
self.typingNode.isOpaque = false
|
||||||
|
|
||||||
self.button = HighlightTrackingButtonNode()
|
self.button = HighlightTrackingButtonNode()
|
||||||
@ -548,9 +544,9 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if size.height > 40.0 {
|
if size.height > 40.0 {
|
||||||
let titleSize = self.titleNode.measure(CGSize(width: clearBounds.width - leftIconWidth - rightIconWidth, height: size.height))
|
let titleSize = self.titleNode.updateLayout(CGSize(width: clearBounds.width - leftIconWidth - rightIconWidth, height: size.height))
|
||||||
let infoSize = self.infoNode.measure(clearBounds.size)
|
let infoSize = self.infoNode.updateLayout(clearBounds.size)
|
||||||
let typingSize = self.typingNode.measure(clearBounds.size)
|
let typingSize = self.typingNode.updateLayout(clearBounds.size)
|
||||||
let titleInfoSpacing: CGFloat = 0.0
|
let titleInfoSpacing: CGFloat = 0.0
|
||||||
|
|
||||||
var titleFrame: CGRect
|
var titleFrame: CGRect
|
||||||
@ -590,9 +586,9 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX + 3.0, y: titleFrame.minY + 7.0), size: image.size)
|
self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX + 3.0, y: titleFrame.minY + 7.0), size: image.size)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let titleSize = self.titleNode.measure(CGSize(width: floor(clearBounds.width / 2.0 - leftIconWidth - rightIconWidth), height: size.height))
|
let titleSize = self.titleNode.updateLayout(CGSize(width: floor(clearBounds.width / 2.0 - leftIconWidth - rightIconWidth), height: size.height))
|
||||||
let infoSize = self.infoNode.measure(CGSize(width: floor(clearBounds.width / 2.0), height: size.height))
|
let infoSize = self.infoNode.updateLayout(CGSize(width: floor(clearBounds.width / 2.0), height: size.height))
|
||||||
let typingSize = self.typingNode.measure(CGSize(width: floor(clearBounds.width / 2.0), height: size.height))
|
let typingSize = self.typingNode.updateLayout(CGSize(width: floor(clearBounds.width / 2.0), height: size.height))
|
||||||
|
|
||||||
let titleInfoSpacing: CGFloat = 8.0
|
let titleInfoSpacing: CGFloat = 8.0
|
||||||
let combinedWidth = titleSize.width + leftIconWidth + rightIconWidth + infoSize.width + titleInfoSpacing
|
let combinedWidth = titleSize.width + leftIconWidth + rightIconWidth + infoSize.width + titleInfoSpacing
|
||||||
|
|||||||
@ -222,6 +222,142 @@ private final class DeviceContactDataModernContext: DeviceContactDataContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func withAddressBook(_ f: (ABAddressBook) -> Void) {
|
||||||
|
let addressBookRef = ABAddressBookCreateWithOptions(nil, nil)
|
||||||
|
|
||||||
|
if let addressBook = addressBookRef?.takeRetainedValue() {
|
||||||
|
f(addressBook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class DeviceContactDataLegacyContext: DeviceContactDataContext {
|
||||||
|
var currentContacts: [DeviceContactStableId: DeviceContactBasicData] = [:]
|
||||||
|
|
||||||
|
init(queue: Queue, updated: @escaping ([DeviceContactStableId: DeviceContactBasicData]) -> Void) {
|
||||||
|
self.currentContacts = self.retrieveContacts()
|
||||||
|
updated(self.currentContacts)
|
||||||
|
/*let handle = NotificationCenter.default.addObserver(forName: NSNotification.Name.CNContactStoreDidChange, object: nil, queue: nil, using: { [weak self] _ in
|
||||||
|
queue.async {
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let contacts = strongSelf.retrieveContacts()
|
||||||
|
if strongSelf.currentContacts != contacts {
|
||||||
|
strongSelf.currentContacts = contacts
|
||||||
|
updated(strongSelf.currentContacts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})*/
|
||||||
|
//self.updateHandle = handle
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
/*if let updateHandle = updateHandle {
|
||||||
|
NotificationCenter.default.removeObserver(updateHandle)
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private func retrieveContacts() -> [DeviceContactStableId: DeviceContactBasicData] {
|
||||||
|
var result: [DeviceContactStableId: DeviceContactBasicData] = [:]
|
||||||
|
withAddressBook { addressBook in
|
||||||
|
guard let peopleRef = ABAddressBookCopyArrayOfAllPeople(addressBook)?.takeRetainedValue() else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for recordRef in peopleRef as NSArray {
|
||||||
|
let record = recordRef as ABRecord
|
||||||
|
let (stableId, basicData) = DeviceContactDataLegacyContext.parseContact(record)
|
||||||
|
result[stableId] = basicData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getContactById(stableId: String) -> ABRecord? {
|
||||||
|
let recordId: ABRecordID
|
||||||
|
if stableId.hasPrefix("ab-"), let idValue = Int(String(stableId[stableId.index(stableId.startIndex, offsetBy: 3)])) {
|
||||||
|
recordId = Int32(clamping: idValue)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var result: ABRecord?
|
||||||
|
withAddressBook { addressBook in
|
||||||
|
result = ABAddressBookGetPersonWithRecordID(addressBook, recordId)?.takeUnretainedValue()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func parseContact(_ contact: ABRecord) -> (DeviceContactStableId, DeviceContactBasicData) {
|
||||||
|
let stableId = "ab-\(ABRecordGetRecordID(contact))"
|
||||||
|
var firstName = ""
|
||||||
|
var lastName = ""
|
||||||
|
if let value = ABRecordCopyValue(contact, kABPersonFirstNameProperty)?.takeRetainedValue() {
|
||||||
|
firstName = value as! CFString as String
|
||||||
|
}
|
||||||
|
if let value = ABRecordCopyValue(contact, kABPersonLastNameProperty)?.takeRetainedValue() {
|
||||||
|
lastName = value as! CFString as String
|
||||||
|
}
|
||||||
|
|
||||||
|
var phoneNumbers: [DeviceContactPhoneNumberData] = []
|
||||||
|
if let value = ABRecordCopyValue(contact, kABPersonPhoneProperty)?.takeRetainedValue() {
|
||||||
|
let phones = value as ABMultiValue
|
||||||
|
let count = ABMultiValueGetCount(phones)
|
||||||
|
for i in 0 ..< count {
|
||||||
|
if let phoneRef = ABMultiValueCopyValueAtIndex(phones, i)?.takeRetainedValue() {
|
||||||
|
let phone = phoneRef as! CFString as String
|
||||||
|
var label = ""
|
||||||
|
if let labelRef = ABMultiValueCopyLabelAtIndex(phones, i)?.takeRetainedValue() {
|
||||||
|
label = labelRef as String
|
||||||
|
}
|
||||||
|
phoneNumbers.append(DeviceContactPhoneNumberData(label: label, value: phone))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (stableId, DeviceContactBasicData(firstName: firstName, lastName: lastName, phoneNumbers: phoneNumbers))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getExtendedContactData(stableId: DeviceContactStableId) -> DeviceContactExtendedData? {
|
||||||
|
if let contact = self.getContactById(stableId: stableId) {
|
||||||
|
let basicData = DeviceContactDataLegacyContext.parseContact(contact).1
|
||||||
|
return DeviceContactExtendedData(basicData: basicData, middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [])
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendContactData(_ contactData: DeviceContactExtendedData, to stableId: DeviceContactStableId) -> DeviceContactExtendedData? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createContactWithData(_ contactData: DeviceContactExtendedData) -> (DeviceContactStableId, DeviceContactExtendedData)? {
|
||||||
|
var result: (DeviceContactStableId, DeviceContactExtendedData)?
|
||||||
|
withAddressBook { addressBook in
|
||||||
|
let contact = ABPersonCreate()?.takeRetainedValue()
|
||||||
|
ABRecordSetValue(contact, kABPersonFirstNameProperty, contactData.basicData.firstName as CFString, nil)
|
||||||
|
ABRecordSetValue(contact, kABPersonLastNameProperty, contactData.basicData.lastName as CFString, nil)
|
||||||
|
|
||||||
|
let phones = ABMultiValueCreateMutable(ABPropertyType(kABMultiStringPropertyType))?.takeRetainedValue()
|
||||||
|
for phone in contactData.basicData.phoneNumbers {
|
||||||
|
ABMultiValueAddValueAndLabel(phones, phone.value as CFString, phone.label as CFString, nil)
|
||||||
|
}
|
||||||
|
ABRecordSetValue(contact, kABPersonPhoneProperty, phones, nil)
|
||||||
|
|
||||||
|
if ABAddressBookAddRecord(addressBook, contact, nil) {
|
||||||
|
ABAddressBookSave(addressBook, nil)
|
||||||
|
|
||||||
|
let stableId = "ab-\(ABRecordGetRecordID(contact))"
|
||||||
|
if let contact = self.getContactById(stableId: stableId) {
|
||||||
|
let parsedContact = DeviceContactDataLegacyContext.parseContact(contact).1
|
||||||
|
result = (stableId, DeviceContactExtendedData(basicData: parsedContact, middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: []))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final class ExtendedContactDataContext {
|
private final class ExtendedContactDataContext {
|
||||||
var value: DeviceContactExtendedData?
|
var value: DeviceContactExtendedData?
|
||||||
let subscribers = Bag<(DeviceContactExtendedData) -> Void>()
|
let subscribers = Bag<(DeviceContactExtendedData) -> Void>()
|
||||||
@ -273,8 +409,20 @@ private final class DeviceContactDataManagerImpl {
|
|||||||
strongSelf.updateAll(stableIdToBasicContactData)
|
strongSelf.updateAll(stableIdToBasicContactData)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
strongSelf.dataContext = DeviceContactDataLegacyContext(queue: strongSelf.queue, updated: { stableIdToBasicContactData in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.updateAll(stableIdToBasicContactData)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*strongSelf.dataContext = DeviceContactDataLegacyContext(queue: strongSelf.queue, updated: { stableIdToBasicContactData in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.updateAll(stableIdToBasicContactData)
|
||||||
|
})*/
|
||||||
} else {
|
} else {
|
||||||
strongSelf.updateAll([:])
|
strongSelf.updateAll([:])
|
||||||
}
|
}
|
||||||
|
|||||||
103
TelegramUI/DocumentPreviewController.swift
Normal file
103
TelegramUI/DocumentPreviewController.swift
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
import TelegramCore
|
||||||
|
import QuickLook
|
||||||
|
import Display
|
||||||
|
|
||||||
|
private final class DocumentPreviewItem: NSObject, QLPreviewItem {
|
||||||
|
private let url: URL
|
||||||
|
private let title: String
|
||||||
|
|
||||||
|
var previewItemURL: URL? {
|
||||||
|
return self.url
|
||||||
|
}
|
||||||
|
|
||||||
|
var previewItemTitle: String? {
|
||||||
|
return self.title
|
||||||
|
}
|
||||||
|
|
||||||
|
init(url: URL, title: String) {
|
||||||
|
self.url = url
|
||||||
|
self.title = title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class DocumentPreviewController: UINavigationController, QLPreviewControllerDelegate, QLPreviewControllerDataSource {
|
||||||
|
private let postbox: Postbox
|
||||||
|
private let file: TelegramMediaFile
|
||||||
|
|
||||||
|
private var item: DocumentPreviewItem?
|
||||||
|
|
||||||
|
init(theme: PresentationTheme, strings: PresentationStrings, postbox: Postbox, file: TelegramMediaFile) {
|
||||||
|
self.postbox = postbox
|
||||||
|
self.file = file
|
||||||
|
|
||||||
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
|
||||||
|
self.navigationBar.barTintColor = theme.rootController.navigationBar.backgroundColor
|
||||||
|
self.navigationBar.tintColor = theme.rootController.navigationBar.accentTextColor
|
||||||
|
self.navigationBar.shadowImage = generateImage(CGSize(width: 1.0, height: 1.0), rotatedContext: { size, context in
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
context.setFillColor(theme.rootController.navigationBar.separatorColor.cgColor)
|
||||||
|
context.fill(CGRect(origin: CGPoint(), size: CGSize(width: 1.0, height: UIScreenPixel)))
|
||||||
|
})
|
||||||
|
self.navigationBar.isTranslucent = false
|
||||||
|
self.navigationBar.titleTextAttributes = [NSAttributedStringKey.font: Font.semibold(17.0), NSAttributedStringKey.foregroundColor: theme.rootController.navigationBar.primaryTextColor]
|
||||||
|
|
||||||
|
let controller = QLPreviewController(nibName: nil, bundle: nil)
|
||||||
|
controller.navigation_setDismiss({ [weak self] in
|
||||||
|
//self?.cancelPressed()
|
||||||
|
}, rootController: self)
|
||||||
|
controller.delegate = self
|
||||||
|
controller.dataSource = self
|
||||||
|
controller.navigationItem.setLeftBarButton(UIBarButtonItem(title: strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed)), animated: false)
|
||||||
|
self.setViewControllers([controller], animated: false)
|
||||||
|
|
||||||
|
var pathExtension: String?
|
||||||
|
if let fileName = self.file.fileName {
|
||||||
|
let pathExtensionCandidate = (fileName as NSString).pathExtension
|
||||||
|
if !pathExtensionCandidate.isEmpty {
|
||||||
|
pathExtension = pathExtensionCandidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let path = self.postbox.mediaBox.completedResourcePath(self.file.resource, pathExtension: pathExtension) {
|
||||||
|
self.item = DocumentPreviewItem(url: URL(fileURLWithPath: path), title: self.file.fileName ?? strings.Message_File)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func cancelPressed() {
|
||||||
|
self.presentingViewController?.dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
|
||||||
|
if self.item != nil {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
|
||||||
|
if let item = self.item {
|
||||||
|
return item
|
||||||
|
} else {
|
||||||
|
assertionFailure()
|
||||||
|
return DocumentPreviewItem(url: URL(fileURLWithPath: ""), title: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func previewControllerWillDismiss(_ controller: QLPreviewController) {
|
||||||
|
self.cancelPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
func previewControllerDidDismiss(_ controller: QLPreviewController) {
|
||||||
|
//self.cancelPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,8 +10,8 @@ final class EditAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
|
|
||||||
let closeButton: ASButtonNode
|
let closeButton: ASButtonNode
|
||||||
let lineNode: ASImageNode
|
let lineNode: ASImageNode
|
||||||
let titleNode: ASTextNode
|
let titleNode: ImmediateTextNode
|
||||||
let textNode: ASTextNode
|
let textNode: ImmediateTextNode
|
||||||
let imageNode: TransformImageNode
|
let imageNode: TransformImageNode
|
||||||
|
|
||||||
private let activityIndicator: ActivityIndicator
|
private let activityIndicator: ActivityIndicator
|
||||||
@ -68,13 +68,11 @@ final class EditAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
self.lineNode.displaysAsynchronously = false
|
self.lineNode.displaysAsynchronously = false
|
||||||
self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(theme)
|
self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(theme)
|
||||||
|
|
||||||
self.titleNode = ASTextNode()
|
self.titleNode = ImmediateTextNode()
|
||||||
self.titleNode.truncationMode = .byTruncatingTail
|
|
||||||
self.titleNode.maximumNumberOfLines = 1
|
self.titleNode.maximumNumberOfLines = 1
|
||||||
self.titleNode.displaysAsynchronously = false
|
self.titleNode.displaysAsynchronously = false
|
||||||
|
|
||||||
self.textNode = ASTextNode()
|
self.textNode = ImmediateTextNode()
|
||||||
self.textNode.truncationMode = .byTruncatingTail
|
|
||||||
self.textNode.maximumNumberOfLines = 1
|
self.textNode.maximumNumberOfLines = 1
|
||||||
self.textNode.displaysAsynchronously = false
|
self.textNode.displaysAsynchronously = false
|
||||||
self.textNode.isUserInteractionEnabled = true
|
self.textNode.isUserInteractionEnabled = true
|
||||||
@ -308,10 +306,10 @@ final class EditAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
}
|
}
|
||||||
self.imageNode.frame = CGRect(origin: CGPoint(x: leftInset + 9.0, y: 8.0), size: CGSize(width: 35.0, height: 35.0))
|
self.imageNode.frame = CGRect(origin: CGPoint(x: leftInset + 9.0, y: 8.0), size: CGSize(width: 35.0, height: 35.0))
|
||||||
|
|
||||||
let titleSize = self.titleNode.measure(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height))
|
let titleSize = self.titleNode.updateLayout(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height))
|
||||||
self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset, y: 7.0), size: titleSize)
|
self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset, y: 7.0), size: titleSize)
|
||||||
|
|
||||||
let textSize = self.textNode.measure(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height))
|
let textSize = self.textNode.updateLayout(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height))
|
||||||
self.textNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset, y: 25.0), size: textSize)
|
self.textNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset, y: 25.0), size: textSize)
|
||||||
|
|
||||||
self.tapNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: bounds.width - leftInset - rightInset - closeButtonSize.width - 4.0, height: bounds.height))
|
self.tapNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: bounds.width - leftInset - rightInset - closeButtonSize.width - 4.0, height: bounds.height))
|
||||||
|
|||||||
@ -19,10 +19,14 @@ private func internalMessageFileMediaPlaybackStatus(account: Account, file: Tele
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let (playlistId, itemId) = peerMessagesMediaPlaylistAndItemId(message, isRecentActions: isRecentActions) {
|
if let (playlistId, itemId) = peerMessagesMediaPlaylistAndItemId(message, isRecentActions: isRecentActions) {
|
||||||
return account.telegramApplicationContext.mediaManager.filteredPlaylistState(playlistId: playlistId, itemId: itemId, type: playerType)
|
if let mediaManager = account.telegramApplicationContext.mediaManager {
|
||||||
|
return mediaManager.filteredPlaylistState(playlistId: playlistId, itemId: itemId, type: playerType)
|
||||||
|> mapToSignal { state -> Signal<MediaPlayerStatus?, NoError> in
|
|> mapToSignal { state -> Signal<MediaPlayerStatus?, NoError> in
|
||||||
return .single(state?.status)
|
return .single(state?.status)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,8 +64,8 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
|
|
||||||
let closeButton: ASButtonNode
|
let closeButton: ASButtonNode
|
||||||
let lineNode: ASImageNode
|
let lineNode: ASImageNode
|
||||||
let titleNode: ASTextNode
|
let titleNode: ImmediateTextNode
|
||||||
let textNode: ASTextNode
|
let textNode: ImmediateTextNode
|
||||||
|
|
||||||
var theme: PresentationTheme
|
var theme: PresentationTheme
|
||||||
|
|
||||||
@ -83,13 +83,11 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
self.lineNode.displaysAsynchronously = false
|
self.lineNode.displaysAsynchronously = false
|
||||||
self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(theme)
|
self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(theme)
|
||||||
|
|
||||||
self.titleNode = ASTextNode()
|
self.titleNode = ImmediateTextNode()
|
||||||
self.titleNode.truncationMode = .byTruncatingTail
|
|
||||||
self.titleNode.maximumNumberOfLines = 1
|
self.titleNode.maximumNumberOfLines = 1
|
||||||
self.titleNode.displaysAsynchronously = false
|
self.titleNode.displaysAsynchronously = false
|
||||||
|
|
||||||
self.textNode = ASTextNode()
|
self.textNode = ImmediateTextNode()
|
||||||
self.textNode.truncationMode = .byTruncatingTail
|
|
||||||
self.textNode.maximumNumberOfLines = 1
|
self.textNode.maximumNumberOfLines = 1
|
||||||
self.textNode.displaysAsynchronously = false
|
self.textNode.displaysAsynchronously = false
|
||||||
|
|
||||||
@ -174,10 +172,10 @@ final class ForwardAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
|
|
||||||
self.lineNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 8.0), size: CGSize(width: 2.0, height: bounds.size.height - 10.0))
|
self.lineNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 8.0), size: CGSize(width: 2.0, height: bounds.size.height - 10.0))
|
||||||
|
|
||||||
let titleSize = self.titleNode.measure(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset, height: bounds.size.height))
|
let titleSize = self.titleNode.updateLayout(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset, height: bounds.size.height))
|
||||||
self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset, y: 7.0), size: titleSize)
|
self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset, y: 7.0), size: titleSize)
|
||||||
|
|
||||||
let textSize = self.textNode.measure(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset, height: bounds.size.height))
|
let textSize = self.textNode.updateLayout(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset, height: bounds.size.height))
|
||||||
self.textNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset, y: 25.0), size: textSize)
|
self.textNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset, y: 25.0), size: textSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -72,26 +72,9 @@ private func mediaForMessage(message: Message) -> Media? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private let internalExtensions = Set<String>([
|
private let internalExtensions = Set<String>([
|
||||||
"txt",
|
|
||||||
"doc",
|
|
||||||
"docx",
|
|
||||||
"xls",
|
|
||||||
"xlsx",
|
|
||||||
"ppt",
|
|
||||||
"pptx",
|
|
||||||
"php",
|
|
||||||
"cpp",
|
|
||||||
"h",
|
|
||||||
"swift",
|
|
||||||
"m",
|
|
||||||
"mm",
|
|
||||||
"java",
|
|
||||||
"jpg",
|
"jpg",
|
||||||
"png",
|
"png",
|
||||||
"jpeg",
|
"jpeg"
|
||||||
"json",
|
|
||||||
"rs",
|
|
||||||
"cs"
|
|
||||||
])
|
])
|
||||||
|
|
||||||
private let internalNotSupportedExtensions = Set<String>([
|
private let internalNotSupportedExtensions = Set<String>([
|
||||||
@ -99,17 +82,12 @@ private let internalNotSupportedExtensions = Set<String>([
|
|||||||
])
|
])
|
||||||
|
|
||||||
private let internalMimeTypes = Set<String>([
|
private let internalMimeTypes = Set<String>([
|
||||||
"application/pdf",
|
|
||||||
"application/postscript",
|
|
||||||
"application/text"
|
|
||||||
])
|
])
|
||||||
|
|
||||||
private let internalMimePrefixes: [String] = [
|
private let internalMimePrefixes: [String] = [
|
||||||
"image/jpeg",
|
"image/jpeg",
|
||||||
"image/jpg",
|
"image/jpg",
|
||||||
"image/png",
|
"image/png"
|
||||||
"text/",
|
|
||||||
"application/vnd.ms-"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
private let supportedVideoMimeTypes = Set<String>([
|
private let supportedVideoMimeTypes = Set<String>([
|
||||||
@ -455,13 +433,15 @@ class GalleryController: ViewController {
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
self.hiddenMediaManagerIndex = account.telegramApplicationContext.mediaManager.galleryHiddenMediaManager.addSource(self._hiddenMedia.get() |> map { messageIdAndMedia in
|
if let mediaManager = account.telegramApplicationContext.mediaManager {
|
||||||
if let (messageId, media) = messageIdAndMedia {
|
self.hiddenMediaManagerIndex = mediaManager.galleryHiddenMediaManager.addSource(self._hiddenMedia.get() |> map { messageIdAndMedia in
|
||||||
return .chat(messageId, media)
|
if let (messageId, media) = messageIdAndMedia {
|
||||||
} else {
|
return .chat(messageId, media)
|
||||||
return nil
|
} else {
|
||||||
}
|
return nil
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required init(coder aDecoder: NSCoder) {
|
required init(coder aDecoder: NSCoder) {
|
||||||
@ -471,8 +451,8 @@ class GalleryController: ViewController {
|
|||||||
deinit {
|
deinit {
|
||||||
self.disposable.dispose()
|
self.disposable.dispose()
|
||||||
self.centralItemAttributesDisposable.dispose()
|
self.centralItemAttributesDisposable.dispose()
|
||||||
if let hiddenMediaManagerIndex = self.hiddenMediaManagerIndex {
|
if let hiddenMediaManagerIndex = self.hiddenMediaManagerIndex, let mediaManager = self.account.telegramApplicationContext.mediaManager {
|
||||||
self.account.telegramApplicationContext.mediaManager.galleryHiddenMediaManager.removeSource(hiddenMediaManagerIndex)
|
mediaManager.galleryHiddenMediaManager.removeSource(hiddenMediaManagerIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -347,10 +347,6 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if let applicationContext = item.account.applicationContext as? TelegramApplicationContext {
|
|
||||||
strongSelf.videoNode.acquireContext(account: item.account, mediaManager: applicationContext.mediaManager, id: ChatContextResultManagedMediaId(result: item.result), resource: videoResource, priority: 1)
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -130,7 +130,7 @@ final class InstantPageAudioNode: ASDisplayNode, InstantPageNode {
|
|||||||
self.scrubbingNode.seek = { [weak self] timestamp in
|
self.scrubbingNode.seek = { [weak self] timestamp in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if let _ = strongSelf.playbackState {
|
if let _ = strongSelf.playbackState {
|
||||||
strongSelf.account.telegramApplicationContext.mediaManager.playlistControl(.seek(timestamp), type: strongSelf.playlistType)
|
strongSelf.account.telegramApplicationContext.mediaManager?.playlistControl(.seek(timestamp), type: strongSelf.playlistType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,12 +174,12 @@ final class InstantPageAudioNode: ASDisplayNode, InstantPageNode {
|
|||||||
}
|
}
|
||||||
})*/
|
})*/
|
||||||
|
|
||||||
self.scrubbingNode.status = account.telegramApplicationContext.mediaManager.filteredPlaylistState(playlistId: InstantPageMediaPlaylistId(webpageId: webPage.webpageId), itemId: InstantPageMediaPlaylistItemId(index: self.media.index), type: self.playlistType)
|
self.scrubbingNode.status = account.telegramApplicationContext.mediaManager!.filteredPlaylistState(playlistId: InstantPageMediaPlaylistId(webpageId: webPage.webpageId), itemId: InstantPageMediaPlaylistItemId(index: self.media.index), type: self.playlistType)
|
||||||
|> map { playbackState -> MediaPlayerStatus in
|
|> map { playbackState -> MediaPlayerStatus in
|
||||||
return playbackState?.status ?? MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused)
|
return playbackState?.status ?? MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.playerStatusDisposable = (account.telegramApplicationContext.mediaManager.filteredPlaylistState(playlistId: InstantPageMediaPlaylistId(webpageId: webPage.webpageId), itemId: InstantPageMediaPlaylistItemId(index: self.media.index), type: playlistType)
|
self.playerStatusDisposable = (account.telegramApplicationContext.mediaManager!.filteredPlaylistState(playlistId: InstantPageMediaPlaylistId(webpageId: webPage.webpageId), itemId: InstantPageMediaPlaylistItemId(index: self.media.index), type: playlistType)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] playbackState in
|
|> deliverOnMainQueue).start(next: { [weak self] playbackState in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -246,7 +246,7 @@ final class InstantPageAudioNode: ASDisplayNode, InstantPageNode {
|
|||||||
|
|
||||||
@objc func buttonPressed() {
|
@objc func buttonPressed() {
|
||||||
if let _ = self.playbackState {
|
if let _ = self.playbackState {
|
||||||
self.account.telegramApplicationContext.mediaManager.playlistControl(.playback(.togglePlayPause), type: self.playlistType)
|
self.account.telegramApplicationContext.mediaManager?.playlistControl(.playback(.togglePlayPause), type: self.playlistType)
|
||||||
} else {
|
} else {
|
||||||
self.openMedia(self.media)
|
self.openMedia(self.media)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -783,7 +783,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.account.telegramApplicationContext.mediaManager.setPlaylist(InstantPageMediaPlaylist(webPage: webPage, items: medias, initialItemIndex: initialIndex), type: file.isVoice ? .voice : .music)
|
self.account.telegramApplicationContext.mediaManager?.setPlaylist(InstantPageMediaPlaylist(webPage: webPage, items: medias, initialItemIndex: initialIndex), type: file.isVoice ? .voice : .music)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,7 @@ final class InstantPagePlayableVideoNode: ASDisplayNode, InstantPageNode {
|
|||||||
self.interactive = interactive
|
self.interactive = interactive
|
||||||
self.openMedia = openMedia
|
self.openMedia = openMedia
|
||||||
|
|
||||||
self.videoNode = UniversalVideoNode(postbox: account.postbox, audioSession: account.telegramApplicationContext.mediaManager.audioSession, manager: account.telegramApplicationContext.mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: NativeVideoContent(id: .instantPage(webPage.webpageId, media.media.id!), fileReference: .webPage(webPage: WebpageReference(webPage), media: media.media as! TelegramMediaFile), loopVideo: true, enableSound: false, fetchAutomatically: true), priority: .embedded, autoplay: true)
|
self.videoNode = UniversalVideoNode(postbox: account.postbox, audioSession: account.telegramApplicationContext.mediaManager!.audioSession, manager: account.telegramApplicationContext.mediaManager!.universalVideoManager, decoration: GalleryVideoDecoration(), content: NativeVideoContent(id: .instantPage(webPage.webpageId, media.media.id!), fileReference: .webPage(webPage: WebpageReference(webPage), media: media.media as! TelegramMediaFile), loopVideo: true, enableSound: false, fetchAutomatically: true), priority: .embedded, autoplay: true)
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
|||||||
@ -96,7 +96,7 @@ class ItemListActivityTextItemNode: ListViewItemNode {
|
|||||||
titleString.addAttributes([NSAttributedStringKey.font: titleFont], range: NSMakeRange(0, titleString.length))
|
titleString.addAttributes([NSAttributedStringKey.font: titleFont], range: NSMakeRange(0, titleString.length))
|
||||||
}
|
}
|
||||||
|
|
||||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - 20.0 - 22.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: TextNodeCutout(position: .TopLeft, size: CGSize(width: activityWidth, height: 4.0)), insets: UIEdgeInsets()))
|
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - 20.0 - 22.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: TextNodeCutout(topLeft: CGSize(width: activityWidth, height: 4.0)), insets: UIEdgeInsets()))
|
||||||
|
|
||||||
let contentSize: CGSize
|
let contentSize: CGSize
|
||||||
let insets: UIEdgeInsets
|
let insets: UIEdgeInsets
|
||||||
|
|||||||
@ -777,7 +777,7 @@ final class ListMessageFileItemNode: ListMessageNode {
|
|||||||
}
|
}
|
||||||
case .playbackStatus:
|
case .playbackStatus:
|
||||||
if let account = self.account, let applicationContext = account.applicationContext as? TelegramApplicationContext {
|
if let account = self.account, let applicationContext = account.applicationContext as? TelegramApplicationContext {
|
||||||
applicationContext.mediaManager.playlistControl(.playback(.togglePlayPause))
|
applicationContext.mediaManager?.playlistControl(.playback(.togglePlayPause))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -286,7 +286,7 @@ final class ListMessageSnippetItemNode: ListMessageNode {
|
|||||||
|
|
||||||
let (descriptionNodeLayout, descriptionNodeApply) = descriptionNodeMakeLayout(TextNodeLayoutArguments(attributedString: descriptionText, backgroundColor: nil, maximumNumberOfLines: 3, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - params.rightInset - 12.0, height: CGFloat.infinity), alignment: .natural, lineSpacing: 0.3, cutout: nil, insets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)))
|
let (descriptionNodeLayout, descriptionNodeApply) = descriptionNodeMakeLayout(TextNodeLayoutArguments(attributedString: descriptionText, backgroundColor: nil, maximumNumberOfLines: 3, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - params.rightInset - 12.0, height: CGFloat.infinity), alignment: .natural, lineSpacing: 0.3, cutout: nil, insets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)))
|
||||||
|
|
||||||
let (linkNodeLayout, linkNodeApply) = linkNodeMakeLayout(TextNodeLayoutArguments(attributedString: linkText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - params.rightInset - 12.0, height: CGFloat.infinity), alignment: .natural, lineSpacing: 0.3, cutout: isInstantView ? TextNodeCutout(position: .TopLeft, size: CGSize(width: 14.0, height: 8.0)) : nil, insets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)))
|
let (linkNodeLayout, linkNodeApply) = linkNodeMakeLayout(TextNodeLayoutArguments(attributedString: linkText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - params.rightInset - 12.0, height: CGFloat.infinity), alignment: .natural, lineSpacing: 0.3, cutout: isInstantView ? TextNodeCutout(topLeft: CGSize(width: 14.0, height: 8.0)) : nil, insets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)))
|
||||||
var instantViewImage: UIImage?
|
var instantViewImage: UIImage?
|
||||||
if isInstantView {
|
if isInstantView {
|
||||||
instantViewImage = PresentationResourcesChat.sharedMediaInstantViewIcon(item.theme)
|
instantViewImage = PresentationResourcesChat.sharedMediaInstantViewIcon(item.theme)
|
||||||
|
|||||||
@ -625,7 +625,6 @@ public final class ManagedAudioSession {
|
|||||||
case .builtin:
|
case .builtin:
|
||||||
resetToBuiltin = true
|
resetToBuiltin = true
|
||||||
case .speaker:
|
case .speaker:
|
||||||
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker)
|
|
||||||
if type == .voiceCall {
|
if type == .voiceCall {
|
||||||
if let routes = AVAudioSession.sharedInstance().availableInputs {
|
if let routes = AVAudioSession.sharedInstance().availableInputs {
|
||||||
for route in routes {
|
for route in routes {
|
||||||
@ -636,6 +635,7 @@ public final class ManagedAudioSession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker)
|
||||||
case .headphones:
|
case .headphones:
|
||||||
break
|
break
|
||||||
case let .port(port):
|
case let .port(port):
|
||||||
|
|||||||
@ -131,7 +131,8 @@ public final class MediaManager: NSObject {
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
let combinedPlayersSignal: Signal<(SharedMediaPlayerItemPlaybackState, MediaManagerPlayerType)?, NoError> = combineLatest(self.voiceMediaPlayerState, self.musicMediaPlayerState) |> map { voice, music -> (SharedMediaPlayerItemPlaybackState, MediaManagerPlayerType)? in
|
let combinedPlayersSignal: Signal<(SharedMediaPlayerItemPlaybackState, MediaManagerPlayerType)?, NoError> = combineLatest(queue: Queue.mainQueue(), self.voiceMediaPlayerState, self.musicMediaPlayerState)
|
||||||
|
|> map { voice, music -> (SharedMediaPlayerItemPlaybackState, MediaManagerPlayerType)? in
|
||||||
if let voice = voice {
|
if let voice = voice {
|
||||||
return (voice, .voice)
|
return (voice, .voice)
|
||||||
} else if let music = music {
|
} else if let music = music {
|
||||||
@ -339,7 +340,7 @@ public final class MediaManager: NSObject {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
let shouldKeepAudioSession: Signal<Bool, NoError> = combineLatest(self.globalMediaPlayerState |> deliverOnMainQueue, inForeground |> deliverOnMainQueue)
|
let shouldKeepAudioSession: Signal<Bool, NoError> = combineLatest(queue: Queue.mainQueue(), self.globalMediaPlayerState, inForeground)
|
||||||
|> map { stateAndType, inForeground -> Bool in
|
|> map { stateAndType, inForeground -> Bool in
|
||||||
var isPlaying = false
|
var isPlaying = false
|
||||||
if let (state, _) = stateAndType {
|
if let (state, _) = stateAndType {
|
||||||
|
|||||||
@ -216,7 +216,7 @@ private func playSound(account: Account, sound: PeerMessageSound, defaultSound:
|
|||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
var currentPlayer: AudioPlayerWrapper?
|
var currentPlayer: AudioPlayerWrapper?
|
||||||
var deactivateImpl: (() -> Void)?
|
var deactivateImpl: (() -> Void)?
|
||||||
let session = account.telegramApplicationContext.mediaManager.audioSession.push(audioSessionType: .play, activate: { _ in
|
let session = account.telegramApplicationContext.mediaManager?.audioSession.push(audioSessionType: .play, activate: { _ in
|
||||||
if let url = Bundle.main.url(forResource: fileNameForNotificationSound(sound, defaultSound: defaultSound), withExtension: "m4a") {
|
if let url = Bundle.main.url(forResource: fileNameForNotificationSound(sound, defaultSound: defaultSound), withExtension: "m4a") {
|
||||||
currentPlayer = AudioPlayerWrapper(url: url, completed: {
|
currentPlayer = AudioPlayerWrapper(url: url, completed: {
|
||||||
deactivateImpl?()
|
deactivateImpl?()
|
||||||
@ -230,10 +230,10 @@ private func playSound(account: Account, sound: PeerMessageSound, defaultSound:
|
|||||||
return .complete()
|
return .complete()
|
||||||
})
|
})
|
||||||
deactivateImpl = {
|
deactivateImpl = {
|
||||||
session.dispose()
|
session?.dispose()
|
||||||
}
|
}
|
||||||
return ActionDisposable {
|
return ActionDisposable {
|
||||||
session.dispose()
|
session?.dispose()
|
||||||
currentPlayer?.stop()
|
currentPlayer?.stop()
|
||||||
currentPlayer = nil
|
currentPlayer = nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -217,8 +217,10 @@ func openChatMessage(account: Account, message: Message, standalone: Bool, rever
|
|||||||
dismissInput()
|
dismissInput()
|
||||||
present(controller, nil)
|
present(controller, nil)
|
||||||
return true
|
return true
|
||||||
case .document:
|
case let .document(file):
|
||||||
present(ShareController(account: account, subject: .messages([message]), showInChat: nil, externalShare: true, immediateExternalShare: true), nil)
|
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||||
|
navigationController?.view.window?.rootViewController?.present(DocumentPreviewController(theme: presentationData.theme, strings: presentationData.strings, postbox: account.postbox, file: file), animated: true, completion: nil)
|
||||||
|
//present(ShareController(account: account, subject: .messages([message]), showInChat: nil, externalShare: true, immediateExternalShare: true), nil)
|
||||||
return true
|
return true
|
||||||
case let .audio(file):
|
case let .audio(file):
|
||||||
let location: PeerMessagesPlaylistLocation
|
let location: PeerMessagesPlaylistLocation
|
||||||
@ -245,7 +247,7 @@ func openChatMessage(account: Account, message: Message, standalone: Bool, rever
|
|||||||
}
|
}
|
||||||
playerType = (file.isVoice || file.isInstantVideo) ? .voice : .music
|
playerType = (file.isVoice || file.isInstantVideo) ? .voice : .music
|
||||||
}
|
}
|
||||||
account.telegramApplicationContext.mediaManager.setPlaylist(PeerMessagesMediaPlaylist(postbox: account.postbox, network: account.network, location: location), type: playerType)
|
account.telegramApplicationContext.mediaManager?.setPlaylist(PeerMessagesMediaPlaylist(postbox: account.postbox, network: account.network, location: location), type: playerType)
|
||||||
return true
|
return true
|
||||||
case let .gallery(gallery):
|
case let .gallery(gallery):
|
||||||
dismissInput()
|
dismissInput()
|
||||||
|
|||||||
@ -440,10 +440,10 @@ public func openExternalUrl(account: Account, context: OpenURLContext = .generic
|
|||||||
navigationController.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
|
navigationController.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
|
||||||
navigateToChatController(navigationController: navigationController, account: account, chatLocation: .peer(peerId))
|
navigateToChatController(navigationController: navigationController, account: account, chatLocation: .peer(peerId))
|
||||||
}
|
}
|
||||||
case .withBotStartPayload:
|
case let .withBotStartPayload(payload):
|
||||||
if let navigationController = navigationController {
|
if let navigationController = navigationController {
|
||||||
navigationController.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
|
navigationController.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
|
||||||
navigateToChatController(navigationController: navigationController, account: account, chatLocation: .peer(peerId))
|
navigateToChatController(navigationController: navigationController, account: account, chatLocation: .peer(peerId), botStart: payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, present: { c, a in
|
}, present: { c, a in
|
||||||
|
|||||||
@ -75,7 +75,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec
|
|||||||
|
|
||||||
self.contentNode = ASDisplayNode()
|
self.contentNode = ASDisplayNode()
|
||||||
|
|
||||||
self.controlsNode = OverlayPlayerControlsNode(postbox: account.postbox, theme: self.presentationData.theme, status: account.telegramApplicationContext.mediaManager.musicMediaPlayerState)
|
self.controlsNode = OverlayPlayerControlsNode(postbox: account.postbox, theme: self.presentationData.theme, status: account.telegramApplicationContext.mediaManager!.musicMediaPlayerState)
|
||||||
|
|
||||||
self.historyBackgroundNode = ASDisplayNode()
|
self.historyBackgroundNode = ASDisplayNode()
|
||||||
self.historyBackgroundNode.isLayerBacked = true
|
self.historyBackgroundNode.isLayerBacked = true
|
||||||
@ -142,7 +142,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec
|
|||||||
|
|
||||||
self.controlsNode.control = { [weak self] action in
|
self.controlsNode.control = { [weak self] action in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.account.telegramApplicationContext.mediaManager.playlistControl(action, type: strongSelf.type)
|
strongSelf.account.telegramApplicationContext.mediaManager?.playlistControl(action, type: strongSelf.type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -462,30 +462,32 @@ public class PeerMediaCollectionController: TelegramController {
|
|||||||
self.displayNode = PeerMediaCollectionControllerNode(account: self.account, peerId: self.peerId, messageId: self.messageId, controllerInteraction: self.controllerInteraction!, interfaceInteraction: self.interfaceInteraction!, navigationBar: self.navigationBar, requestDeactivateSearch: { [weak self] in
|
self.displayNode = PeerMediaCollectionControllerNode(account: self.account, peerId: self.peerId, messageId: self.messageId, controllerInteraction: self.controllerInteraction!, interfaceInteraction: self.interfaceInteraction!, navigationBar: self.navigationBar, requestDeactivateSearch: { [weak self] in
|
||||||
self?.deactivateSearch()
|
self?.deactivateSearch()
|
||||||
})
|
})
|
||||||
|
|
||||||
self.galleryHiddenMesageAndMediaDisposable.set(self.account.telegramApplicationContext.mediaManager.galleryHiddenMediaManager.hiddenIds().start(next: { [weak self] ids in
|
if let mediaManager = self.account.telegramApplicationContext.mediaManager {
|
||||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
self.galleryHiddenMesageAndMediaDisposable.set(mediaManager.galleryHiddenMediaManager.hiddenIds().start(next: { [weak self] ids in
|
||||||
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
||||||
|
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
||||||
for id in ids {
|
|
||||||
if case let .chat(messageId, media) = id {
|
for id in ids {
|
||||||
messageIdAndMedia[messageId] = [media]
|
if case let .chat(messageId, media) = id {
|
||||||
|
messageIdAndMedia[messageId] = [media]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
//if controllerInteraction.hiddenMedia != messageIdAndMedia {
|
||||||
//if controllerInteraction.hiddenMedia != messageIdAndMedia {
|
controllerInteraction.hiddenMedia = messageIdAndMedia
|
||||||
controllerInteraction.hiddenMedia = messageIdAndMedia
|
|
||||||
|
strongSelf.mediaCollectionDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||||
strongSelf.mediaCollectionDisplayNode.historyNode.forEachItemNode { itemNode in
|
if let itemNode = itemNode as? GridMessageItemNode {
|
||||||
if let itemNode = itemNode as? GridMessageItemNode {
|
itemNode.updateHiddenMedia()
|
||||||
itemNode.updateHiddenMedia()
|
} else if let itemNode = itemNode as? ListMessageNode {
|
||||||
} else if let itemNode = itemNode as? ListMessageNode {
|
itemNode.updateHiddenMedia()
|
||||||
itemNode.updateHiddenMedia()
|
}
|
||||||
}
|
}
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
//}
|
}))
|
||||||
}
|
}
|
||||||
}))
|
|
||||||
|
|
||||||
self.ready.set(combineLatest(self.mediaCollectionDisplayNode.historyNode.historyState.get(), self._peerReady.get()) |> map { $1 })
|
self.ready.set(combineLatest(self.mediaCollectionDisplayNode.historyNode.historyState.get(), self._peerReady.get()) |> map { $1 })
|
||||||
|
|
||||||
|
|||||||
@ -540,17 +540,15 @@ func rawMessagePhoto(postbox: Postbox, photoReference: ImageMediaReference) -> S
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func chatMessagePhoto(postbox: Postbox, photoReference: ImageMediaReference) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
public func chatMessagePhoto(postbox: Postbox, photoReference: ImageMediaReference) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||||
return chatMessagePhotoInternal(postbox: postbox, photoReference: photoReference)
|
return chatMessagePhotoInternal(photoData: chatMessagePhotoDatas(postbox: postbox, photoReference: photoReference))
|
||||||
|> map { _, generate in
|
|> map { _, generate in
|
||||||
return generate
|
return generate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func chatMessagePhotoInternal(postbox: Postbox, photoReference: ImageMediaReference) -> Signal<(() -> CGSize?, (TransformImageArguments) -> DrawingContext?), NoError> {
|
public func chatMessagePhotoInternal(photoData: Signal<(Data?, Data?, Bool), NoError>) -> Signal<(() -> CGSize?, (TransformImageArguments) -> DrawingContext?), NoError> {
|
||||||
let signal = chatMessagePhotoDatas(postbox: postbox, photoReference: photoReference)
|
return photoData
|
||||||
|
|
||||||
return signal
|
|
||||||
|> map { (thumbnailData, fullSizeData, fullSizeComplete) in
|
|> map { (thumbnailData, fullSizeData, fullSizeComplete) in
|
||||||
return ({
|
return ({
|
||||||
return nil
|
return nil
|
||||||
@ -1432,7 +1430,7 @@ func chatMessagePhotoStatus(account: Account, messageId: MessageId, photoReferen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func chatMessagePhotoInteractiveFetched(account: Account, photoReference: ImageMediaReference) -> Signal<FetchResourceSourceType, NoError> {
|
public func chatMessagePhotoInteractiveFetched(account: Account, photoReference: ImageMediaReference) -> Signal<FetchResourceSourceType, NoError> {
|
||||||
if let largestRepresentation = largestRepresentationForPhoto(photoReference.media) {
|
if let largestRepresentation = largestRepresentationForPhoto(photoReference.media) {
|
||||||
return fetchedMediaResource(postbox: account.postbox, reference: photoReference.resourceReference(largestRepresentation.resource), statsCategory: .image)
|
return fetchedMediaResource(postbox: account.postbox, reference: photoReference.resourceReference(largestRepresentation.resource), statsCategory: .image)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
|
|
||||||
enum RadialStatusNodeState: Equatable {
|
public enum RadialStatusNodeState: Equatable {
|
||||||
case none
|
case none
|
||||||
case download(UIColor)
|
case download(UIColor)
|
||||||
case play(UIColor)
|
case play(UIColor)
|
||||||
@ -11,7 +11,7 @@ enum RadialStatusNodeState: Equatable {
|
|||||||
case customIcon(UIImage)
|
case customIcon(UIImage)
|
||||||
case secretTimeout(color: UIColor, icon: UIImage?, beginTime: Double, timeout: Double)
|
case secretTimeout(color: UIColor, icon: UIImage?, beginTime: Double, timeout: Double)
|
||||||
|
|
||||||
static func ==(lhs: RadialStatusNodeState, rhs: RadialStatusNodeState) -> Bool {
|
public static func ==(lhs: RadialStatusNodeState, rhs: RadialStatusNodeState) -> Bool {
|
||||||
switch lhs {
|
switch lhs {
|
||||||
case .none:
|
case .none:
|
||||||
if case .none = rhs {
|
if case .none = rhs {
|
||||||
@ -99,13 +99,13 @@ enum RadialStatusNodeState: Equatable {
|
|||||||
node.progress = value
|
node.progress = value
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
case let .secretTimeout(color, icon, beginTime, timeout):
|
case let .secretTimeout(color, icon, beginTime, timeout):
|
||||||
return RadialStatusSecretTimeoutContentNode(color: color, beginTime: beginTime, timeout: timeout, icon: icon)
|
return RadialStatusSecretTimeoutContentNode(color: color, beginTime: beginTime, timeout: timeout, icon: icon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class RadialStatusNode: ASControlNode {
|
public final class RadialStatusNode: ASControlNode {
|
||||||
private var backgroundNodeColor: UIColor
|
private var backgroundNodeColor: UIColor
|
||||||
|
|
||||||
private(set) var state: RadialStatusNodeState = .none
|
private(set) var state: RadialStatusNodeState = .none
|
||||||
@ -114,13 +114,13 @@ final class RadialStatusNode: ASControlNode {
|
|||||||
private var contentNode: RadialStatusContentNode?
|
private var contentNode: RadialStatusContentNode?
|
||||||
private var nextContentNode: RadialStatusContentNode?
|
private var nextContentNode: RadialStatusContentNode?
|
||||||
|
|
||||||
init(backgroundNodeColor: UIColor) {
|
public init(backgroundNodeColor: UIColor) {
|
||||||
self.backgroundNodeColor = backgroundNodeColor
|
self.backgroundNodeColor = backgroundNodeColor
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
func transitionToState(_ state: RadialStatusNodeState, animated: Bool = true, completion: @escaping () -> Void) {
|
public func transitionToState(_ state: RadialStatusNodeState, animated: Bool = true, completion: @escaping () -> Void) {
|
||||||
if self.state != state {
|
if self.state != state {
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ final class RadialStatusNode: ASControlNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func layout() {
|
override public func layout() {
|
||||||
self.backgroundNode?.frame = self.bounds
|
self.backgroundNode?.frame = self.bounds
|
||||||
if let contentNode = self.contentNode {
|
if let contentNode = self.contentNode {
|
||||||
contentNode.frame = self.bounds
|
contentNode.frame = self.bounds
|
||||||
|
|||||||
@ -13,8 +13,8 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
|
|
||||||
let closeButton: ASButtonNode
|
let closeButton: ASButtonNode
|
||||||
let lineNode: ASImageNode
|
let lineNode: ASImageNode
|
||||||
let titleNode: ASTextNode
|
let titleNode: ImmediateTextNode
|
||||||
let textNode: ASTextNode
|
let textNode: ImmediateTextNode
|
||||||
let imageNode: TransformImageNode
|
let imageNode: TransformImageNode
|
||||||
|
|
||||||
var theme: PresentationTheme
|
var theme: PresentationTheme
|
||||||
@ -34,13 +34,11 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
self.lineNode.displaysAsynchronously = false
|
self.lineNode.displaysAsynchronously = false
|
||||||
self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(theme)
|
self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(theme)
|
||||||
|
|
||||||
self.titleNode = ASTextNode()
|
self.titleNode = ImmediateTextNode()
|
||||||
self.titleNode.truncationMode = .byTruncatingTail
|
|
||||||
self.titleNode.maximumNumberOfLines = 1
|
self.titleNode.maximumNumberOfLines = 1
|
||||||
self.titleNode.displaysAsynchronously = false
|
self.titleNode.displaysAsynchronously = false
|
||||||
|
|
||||||
self.textNode = ASTextNode()
|
self.textNode = ImmediateTextNode()
|
||||||
self.textNode.truncationMode = .byTruncatingTail
|
|
||||||
self.textNode.maximumNumberOfLines = 1
|
self.textNode.maximumNumberOfLines = 1
|
||||||
self.textNode.displaysAsynchronously = false
|
self.textNode.displaysAsynchronously = false
|
||||||
|
|
||||||
@ -216,10 +214,10 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
}
|
}
|
||||||
self.imageNode.frame = CGRect(origin: CGPoint(x: leftInset + 9.0, y: 8.0), size: CGSize(width: 35.0, height: 35.0))
|
self.imageNode.frame = CGRect(origin: CGPoint(x: leftInset + 9.0, y: 8.0), size: CGSize(width: 35.0, height: 35.0))
|
||||||
|
|
||||||
let titleSize = self.titleNode.measure(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height))
|
let titleSize = self.titleNode.updateLayout(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height))
|
||||||
self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset, y: 7.0), size: titleSize)
|
self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset, y: 7.0), size: titleSize)
|
||||||
|
|
||||||
let textSize = self.textNode.measure(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height))
|
let textSize = self.textNode.updateLayout(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height))
|
||||||
self.textNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset, y: 25.0), size: textSize)
|
self.textNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset, y: 25.0), size: textSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -158,13 +158,16 @@ public final class SecretMediaPreviewController: ViewController {
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
self.hiddenMediaManagerIndex = account.telegramApplicationContext.mediaManager.galleryHiddenMediaManager.addSource(self._hiddenMedia.get() |> map { messageIdAndMedia in
|
if let mediaManager = account.telegramApplicationContext.mediaManager {
|
||||||
if let (messageId, media) = messageIdAndMedia {
|
self.hiddenMediaManagerIndex = mediaManager.galleryHiddenMediaManager.addSource(self._hiddenMedia.get()
|
||||||
return .chat(messageId, media)
|
|> map { messageIdAndMedia in
|
||||||
} else {
|
if let (messageId, media) = messageIdAndMedia {
|
||||||
return nil
|
return .chat(messageId, media)
|
||||||
}
|
} else {
|
||||||
})
|
return nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
self.screenCaptureEventsDisposable = (screenCaptureEvents()
|
self.screenCaptureEventsDisposable = (screenCaptureEvents()
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||||
@ -181,8 +184,8 @@ public final class SecretMediaPreviewController: ViewController {
|
|||||||
deinit {
|
deinit {
|
||||||
self.disposable.dispose()
|
self.disposable.dispose()
|
||||||
self.markMessageAsConsumedDisposable.dispose()
|
self.markMessageAsConsumedDisposable.dispose()
|
||||||
if let hiddenMediaManagerIndex = self.hiddenMediaManagerIndex {
|
if let hiddenMediaManagerIndex = self.hiddenMediaManagerIndex, let mediaManager = self.account.telegramApplicationContext.mediaManager {
|
||||||
self.account.telegramApplicationContext.mediaManager.galleryHiddenMediaManager.removeSource(hiddenMediaManagerIndex)
|
mediaManager.galleryHiddenMediaManager.removeSource(hiddenMediaManagerIndex)
|
||||||
}
|
}
|
||||||
self.screenCaptureEventsDisposable?.dispose()
|
self.screenCaptureEventsDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,7 +55,7 @@ public final class TelegramApplicationContext {
|
|||||||
let fetchManager: FetchManager
|
let fetchManager: FetchManager
|
||||||
public var callManager: PresentationCallManager?
|
public var callManager: PresentationCallManager?
|
||||||
|
|
||||||
public let mediaManager: MediaManager
|
public let mediaManager: MediaManager?
|
||||||
|
|
||||||
let locationManager: DeviceLocationManager?
|
let locationManager: DeviceLocationManager?
|
||||||
public let liveLocationManager: LiveLocationManager?
|
public let liveLocationManager: LiveLocationManager?
|
||||||
@ -106,7 +106,11 @@ public final class TelegramApplicationContext {
|
|||||||
private var storedPassword: (String, CFAbsoluteTime, SwiftSignalKit.Timer)?
|
private var storedPassword: (String, CFAbsoluteTime, SwiftSignalKit.Timer)?
|
||||||
|
|
||||||
public init(applicationBindings: TelegramApplicationBindings, accountManager: AccountManager, account: Account?, initialPresentationDataAndSettings: InitialPresentationDataAndSettings, postbox: Postbox) {
|
public init(applicationBindings: TelegramApplicationBindings, accountManager: AccountManager, account: Account?, initialPresentationDataAndSettings: InitialPresentationDataAndSettings, postbox: Postbox) {
|
||||||
self.mediaManager = MediaManager(postbox: postbox, inForeground: applicationBindings.applicationInForeground)
|
if account != nil {
|
||||||
|
self.mediaManager = MediaManager(postbox: postbox, inForeground: applicationBindings.applicationInForeground)
|
||||||
|
} else {
|
||||||
|
self.mediaManager = nil
|
||||||
|
}
|
||||||
|
|
||||||
if applicationBindings.isMainApp {
|
if applicationBindings.isMainApp {
|
||||||
self.locationManager = DeviceLocationManager(queue: Queue.mainQueue())
|
self.locationManager = DeviceLocationManager(queue: Queue.mainQueue())
|
||||||
@ -218,7 +222,7 @@ public final class TelegramApplicationContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func attachOverlayMediaController(_ controller: OverlayMediaController) {
|
public func attachOverlayMediaController(_ controller: OverlayMediaController) {
|
||||||
self.mediaManager.overlayMediaManager.attachOverlayMediaController(controller)
|
self.mediaManager?.overlayMediaManager.attachOverlayMediaController(controller)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func storeSecureIdPassword(password: String) {
|
public func storeSecureIdPassword(password: String) {
|
||||||
|
|||||||
@ -88,8 +88,8 @@ public class TelegramController: ViewController {
|
|||||||
|
|
||||||
super.init(navigationBarPresentationData: navigationBarPresentationData)
|
super.init(navigationBarPresentationData: navigationBarPresentationData)
|
||||||
|
|
||||||
if case .none = mediaAccessoryPanelVisibility {} else {
|
if case .none = mediaAccessoryPanelVisibility {} else if let mediaManager = account.telegramApplicationContext.mediaManager {
|
||||||
self.mediaStatusDisposable = (account.telegramApplicationContext.mediaManager.globalMediaPlayerState
|
self.mediaStatusDisposable = (mediaManager.globalMediaPlayerState
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] playlistStateAndType in
|
|> deliverOnMainQueue).start(next: { [weak self] playlistStateAndType in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if !arePlaylistItemsEqual(strongSelf.playlistStateAndType?.0, playlistStateAndType?.0.item) ||
|
if !arePlaylistItemsEqual(strongSelf.playlistStateAndType?.0, playlistStateAndType?.0.item) ||
|
||||||
@ -379,8 +379,12 @@ public class TelegramController: ViewController {
|
|||||||
transition.updateFrame(layer: mediaAccessoryPanel.layer, frame: panelFrame)
|
transition.updateFrame(layer: mediaAccessoryPanel.layer, frame: panelFrame)
|
||||||
mediaAccessoryPanel.updateLayout(size: panelFrame.size, transition: transition)
|
mediaAccessoryPanel.updateLayout(size: panelFrame.size, transition: transition)
|
||||||
mediaAccessoryPanel.containerNode.headerNode.playbackItem = item
|
mediaAccessoryPanel.containerNode.headerNode.playbackItem = item
|
||||||
mediaAccessoryPanel.containerNode.headerNode.playbackStatus = self.account.telegramApplicationContext.mediaManager.globalMediaPlayerState |> map { state in
|
|
||||||
return state?.0.status ?? MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused)
|
if let mediaManager = self.account.telegramApplicationContext.mediaManager {
|
||||||
|
mediaAccessoryPanel.containerNode.headerNode.playbackStatus = mediaManager.globalMediaPlayerState
|
||||||
|
|> map { state in
|
||||||
|
return state?.0.status ?? MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let (mediaAccessoryPanel, _) = self.mediaAccessoryPanel {
|
if let (mediaAccessoryPanel, _) = self.mediaAccessoryPanel {
|
||||||
@ -398,7 +402,7 @@ public class TelegramController: ViewController {
|
|||||||
mediaAccessoryPanel.containerNode.headerNode.displayScrubber = type != .voice
|
mediaAccessoryPanel.containerNode.headerNode.displayScrubber = type != .voice
|
||||||
mediaAccessoryPanel.close = { [weak self] in
|
mediaAccessoryPanel.close = { [weak self] in
|
||||||
if let strongSelf = self, let (_, _, type) = strongSelf.playlistStateAndType {
|
if let strongSelf = self, let (_, _, type) = strongSelf.playlistStateAndType {
|
||||||
strongSelf.account.telegramApplicationContext.mediaManager.setPlaylist(nil, type: type)
|
strongSelf.account.telegramApplicationContext.mediaManager?.setPlaylist(nil, type: type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mediaAccessoryPanel.toggleRate = {
|
mediaAccessoryPanel.toggleRate = {
|
||||||
@ -424,12 +428,12 @@ public class TelegramController: ViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.account.telegramApplicationContext.mediaManager.playlistControl(.setBaseRate(baseRate), type: type)
|
strongSelf.account.telegramApplicationContext.mediaManager?.playlistControl(.setBaseRate(baseRate), type: type)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
mediaAccessoryPanel.togglePlayPause = { [weak self] in
|
mediaAccessoryPanel.togglePlayPause = { [weak self] in
|
||||||
if let strongSelf = self, let (_, _, type) = strongSelf.playlistStateAndType {
|
if let strongSelf = self, let (_, _, type) = strongSelf.playlistStateAndType {
|
||||||
strongSelf.account.telegramApplicationContext.mediaManager.playlistControl(.playback(.togglePlayPause), type: type)
|
strongSelf.account.telegramApplicationContext.mediaManager?.playlistControl(.playback(.togglePlayPause), type: type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mediaAccessoryPanel.tapAction = { [weak self] in
|
mediaAccessoryPanel.tapAction = { [weak self] in
|
||||||
@ -456,8 +460,11 @@ public class TelegramController: ViewController {
|
|||||||
self.mediaAccessoryPanel = (mediaAccessoryPanel, type)
|
self.mediaAccessoryPanel = (mediaAccessoryPanel, type)
|
||||||
mediaAccessoryPanel.updateLayout(size: panelFrame.size, transition: .immediate)
|
mediaAccessoryPanel.updateLayout(size: panelFrame.size, transition: .immediate)
|
||||||
mediaAccessoryPanel.containerNode.headerNode.playbackItem = item
|
mediaAccessoryPanel.containerNode.headerNode.playbackItem = item
|
||||||
mediaAccessoryPanel.containerNode.headerNode.playbackStatus = self.account.telegramApplicationContext.mediaManager.globalMediaPlayerState |> map { state in
|
if let mediaManager = self.account.telegramApplicationContext.mediaManager {
|
||||||
return state?.0.status ?? MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused)
|
mediaAccessoryPanel.containerNode.headerNode.playbackStatus = mediaManager.globalMediaPlayerState
|
||||||
|
|> map { state in
|
||||||
|
return state?.0.status ?? MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mediaAccessoryPanel.animateIn(transition: transition)
|
mediaAccessoryPanel.animateIn(transition: transition)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -223,14 +223,14 @@ private final class LegacyComponentsGlobalsProviderImpl: NSObject, LegacyCompone
|
|||||||
default:
|
default:
|
||||||
convertedType = .play
|
convertedType = .play
|
||||||
}
|
}
|
||||||
let disposable = legacyAccount.telegramApplicationContext.mediaManager.audioSession.push(audioSessionType: convertedType, once: true, activate: { _ in
|
let disposable = legacyAccount.telegramApplicationContext.mediaManager?.audioSession.push(audioSessionType: convertedType, once: true, activate: { _ in
|
||||||
}, deactivate: {
|
}, deactivate: {
|
||||||
interrupted?()
|
interrupted?()
|
||||||
return .complete()
|
return .complete()
|
||||||
})
|
})
|
||||||
|
|
||||||
return SBlockDisposable(block: {
|
return SBlockDisposable(block: {
|
||||||
disposable.dispose()
|
disposable?.dispose()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -37,7 +37,7 @@ public class TransformImageNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setSignal(_ signal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>, dispatchOnDisplayLink: Bool = true) {
|
public func setSignal(_ signal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>, dispatchOnDisplayLink: Bool = true) {
|
||||||
let argumentsPromise = self.argumentsPromise
|
let argumentsPromise = self.argumentsPromise
|
||||||
|
|
||||||
let result = combineLatest(signal, argumentsPromise.get())
|
let result = combineLatest(signal, argumentsPromise.get())
|
||||||
|
|||||||
@ -255,7 +255,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
self.footerContentNode.scrubberView = nil
|
self.footerContentNode.scrubberView = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let videoNode = UniversalVideoNode(postbox: item.account.postbox, audioSession: item.account.telegramApplicationContext.mediaManager.audioSession, manager: item.account.telegramApplicationContext.mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: item.content, priority: .gallery)
|
guard let mediaManager = item.account.telegramApplicationContext.mediaManager else {
|
||||||
|
preconditionFailure()
|
||||||
|
}
|
||||||
|
|
||||||
|
let videoNode = UniversalVideoNode(postbox: item.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: item.content, priority: .gallery)
|
||||||
let videoSize = CGSize(width: item.content.dimensions.width * 2.0, height: item.content.dimensions.height * 2.0)
|
let videoSize = CGSize(width: item.content.dimensions.width * 2.0, height: item.content.dimensions.height * 2.0)
|
||||||
videoNode.updateLayout(size: videoSize, transition: .immediate)
|
videoNode.updateLayout(size: videoSize, transition: .immediate)
|
||||||
videoNode.ownsContentNodeUpdated = { [weak self] value in
|
videoNode.ownsContentNodeUpdated = { [weak self] value in
|
||||||
@ -439,7 +443,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
let transform = CATransform3DScale(videoNode.layer.transform, transformedFrame.size.width / videoNode.layer.bounds.size.width, transformedFrame.size.height / videoNode.layer.bounds.size.height, 1.0)
|
let transform = CATransform3DScale(videoNode.layer.transform, transformedFrame.size.width / videoNode.layer.bounds.size.width, transformedFrame.size.height / videoNode.layer.bounds.size.height, 1.0)
|
||||||
videoNode.layer.animate(from: NSValue(caTransform3D: transform), to: NSValue(caTransform3D: videoNode.layer.transform), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25)
|
videoNode.layer.animate(from: NSValue(caTransform3D: transform), to: NSValue(caTransform3D: videoNode.layer.transform), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25)
|
||||||
|
|
||||||
self.account.telegramApplicationContext.mediaManager.setOverlayVideoNode(nil)
|
self.account.telegramApplicationContext.mediaManager?.setOverlayVideoNode(nil)
|
||||||
} else {
|
} else {
|
||||||
var transformedFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view)
|
var transformedFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view)
|
||||||
let transformedSuperFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view.superview)
|
let transformedSuperFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view.superview)
|
||||||
@ -696,9 +700,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
if let item = self.item, let _ = self.videoNode {
|
if let item = self.item, let _ = self.videoNode {
|
||||||
let account = self.account
|
let account = self.account
|
||||||
let baseNavigationController = self.baseNavigationController()
|
let baseNavigationController = self.baseNavigationController()
|
||||||
let mediaManager = self.account.telegramApplicationContext.mediaManager
|
let mediaManager = self.account.telegramApplicationContext.mediaManager!
|
||||||
var expandImpl: (() -> Void)?
|
var expandImpl: (() -> Void)?
|
||||||
let overlayNode = OverlayUniversalVideoNode(account: self.account, audioSession: self.account.telegramApplicationContext.mediaManager.audioSession, manager: self.account.telegramApplicationContext.mediaManager.universalVideoManager, content: item.content, expand: {
|
let overlayNode = OverlayUniversalVideoNode(account: self.account, audioSession: self.account.telegramApplicationContext.mediaManager!.audioSession, manager: self.account.telegramApplicationContext.mediaManager!.universalVideoManager, content: item.content, expand: {
|
||||||
expandImpl?()
|
expandImpl?()
|
||||||
}, close: { [weak mediaManager] in
|
}, close: { [weak mediaManager] in
|
||||||
mediaManager?.setOverlayVideoNode(nil)
|
mediaManager?.setOverlayVideoNode(nil)
|
||||||
@ -734,7 +738,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
account.telegramApplicationContext.mediaManager.setOverlayVideoNode(overlayNode)
|
account.telegramApplicationContext.mediaManager?.setOverlayVideoNode(overlayNode)
|
||||||
if overlayNode.supernode != nil {
|
if overlayNode.supernode != nil {
|
||||||
self.beginCustomDismiss()
|
self.beginCustomDismiss()
|
||||||
self.animateOut(toOverlay: overlayNode, completion: { [weak self] in
|
self.animateOut(toOverlay: overlayNode, completion: { [weak self] in
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user