mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
0c2f2ec1b7
commit
9d41d0f110
BIN
Telegram/Telegram-iOS/Resources/Gift.tgs
Normal file
BIN
Telegram/Telegram-iOS/Resources/Gift.tgs
Normal file
Binary file not shown.
@ -7831,6 +7831,9 @@ Sorry for the inconvenience.";
|
|||||||
"Notification.PremiumGift.Sent" = "%1$@ sent you a gift for %2$@";
|
"Notification.PremiumGift.Sent" = "%1$@ sent you a gift for %2$@";
|
||||||
"Notification.PremiumGift.SentYou" = "You sent a gift for %@";
|
"Notification.PremiumGift.SentYou" = "You sent a gift for %@";
|
||||||
|
|
||||||
|
"Notification.PremiumGift.Months_1" = "%@ month";
|
||||||
|
"Notification.PremiumGift.Months_any" = "%@ months";
|
||||||
|
|
||||||
"Notification.PremiumGift.Title" = "Telegram Premium";
|
"Notification.PremiumGift.Title" = "Telegram Premium";
|
||||||
"Notification.PremiumGift.Subtitle" = "for %@";
|
"Notification.PremiumGift.Subtitle" = "for %@";
|
||||||
"Notification.PremiumGift.View" = "View";
|
"Notification.PremiumGift.View" = "View";
|
||||||
|
@ -147,10 +147,15 @@ private let moreButtonImage = generateTintedImage(image: UIImage(bundleImageName
|
|||||||
private let placeholderFont = Font.regular(16.0)
|
private let placeholderFont = Font.regular(16.0)
|
||||||
|
|
||||||
private final class UniversalVideoGalleryItemPictureInPictureNode: ASDisplayNode {
|
private final class UniversalVideoGalleryItemPictureInPictureNode: ASDisplayNode {
|
||||||
|
enum Mode {
|
||||||
|
case pictureInPicture
|
||||||
|
case airplay
|
||||||
|
}
|
||||||
|
|
||||||
private let iconNode: ASImageNode
|
private let iconNode: ASImageNode
|
||||||
private let textNode: ASTextNode
|
private let textNode: ASTextNode
|
||||||
|
|
||||||
init(strings: PresentationStrings) {
|
init(strings: PresentationStrings, mode: Mode) {
|
||||||
self.iconNode = ASImageNode()
|
self.iconNode = ASImageNode()
|
||||||
self.iconNode.isLayerBacked = true
|
self.iconNode.isLayerBacked = true
|
||||||
self.iconNode.displayWithoutProcessing = true
|
self.iconNode.displayWithoutProcessing = true
|
||||||
@ -160,10 +165,20 @@ private final class UniversalVideoGalleryItemPictureInPictureNode: ASDisplayNode
|
|||||||
self.textNode = ASTextNode()
|
self.textNode = ASTextNode()
|
||||||
self.textNode.isUserInteractionEnabled = false
|
self.textNode.isUserInteractionEnabled = false
|
||||||
self.textNode.displaysAsynchronously = false
|
self.textNode.displaysAsynchronously = false
|
||||||
self.textNode.attributedText = NSAttributedString(string: strings.Embed_PlayingInPIP, font: placeholderFont, textColor: UIColor(rgb: 0x8e8e93))
|
|
||||||
|
let text: String
|
||||||
|
switch mode {
|
||||||
|
case .pictureInPicture:
|
||||||
|
text = strings.Embed_PlayingInPIP
|
||||||
|
case .airplay:
|
||||||
|
text = strings.Gallery_AirPlayPlaceholder
|
||||||
|
}
|
||||||
|
self.textNode.attributedText = NSAttributedString(string: text, font: placeholderFont, textColor: UIColor(rgb: 0x8e8e93))
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
self.backgroundColor = UIColor(rgb: 0x333335)
|
||||||
|
|
||||||
self.addSubnode(self.iconNode)
|
self.addSubnode(self.iconNode)
|
||||||
self.addSubnode(self.textNode)
|
self.addSubnode(self.textNode)
|
||||||
}
|
}
|
||||||
@ -975,7 +990,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
|
|
||||||
if let pictureInPictureNode = self.pictureInPictureNode {
|
if let pictureInPictureNode = self.pictureInPictureNode {
|
||||||
if let item = self.item {
|
if let item = self.item {
|
||||||
let placeholderSize = item.content.dimensions.fitted(layout.size)
|
var placeholderSize = item.content.dimensions.fitted(layout.size)
|
||||||
|
placeholderSize.height += 2.0
|
||||||
transition.updateFrame(node: pictureInPictureNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - placeholderSize.width) / 2.0), y: floor((layout.size.height - placeholderSize.height) / 2.0)), size: placeholderSize))
|
transition.updateFrame(node: pictureInPictureNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - placeholderSize.width) / 2.0), y: floor((layout.size.height - placeholderSize.height) / 2.0)), size: placeholderSize))
|
||||||
pictureInPictureNode.updateLayout(placeholderSize, transition: transition)
|
pictureInPictureNode.updateLayout(placeholderSize, transition: transition)
|
||||||
}
|
}
|
||||||
@ -1144,11 +1160,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
self.videoNode = videoNode
|
self.videoNode = videoNode
|
||||||
self.videoNodeUserInteractionEnabled = disablePlayerControls || forceEnableUserInteraction
|
self.videoNodeUserInteractionEnabled = disablePlayerControls || forceEnableUserInteraction
|
||||||
videoNode.isUserInteractionEnabled = disablePlayerControls || forceEnableUserInteraction
|
videoNode.isUserInteractionEnabled = disablePlayerControls || forceEnableUserInteraction
|
||||||
videoNode.backgroundColor = videoNode.ownsContentNode ? UIColor.black : UIColor(rgb: 0x333335)
|
videoNode.backgroundColor = UIColor.black
|
||||||
if item.fromPlayingVideo {
|
if item.fromPlayingVideo {
|
||||||
videoNode.canAttachContent = false
|
videoNode.canAttachContent = false
|
||||||
} else {
|
} else {
|
||||||
self.updateDisplayPlaceholder(!videoNode.ownsContentNode)
|
self.updateDisplayPlaceholder()
|
||||||
}
|
}
|
||||||
|
|
||||||
scrubberView.setStatusSignal(videoNode.status |> map { value -> MediaPlayerStatus in
|
scrubberView.setStatusSignal(videoNode.status |> map { value -> MediaPlayerStatus in
|
||||||
@ -1492,21 +1508,25 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
self.videoNode?.notifyPlaybackControlsHidden(!isVisible)
|
self.videoNode?.notifyPlaybackControlsHidden(!isVisible)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updateDisplayPlaceholder() {
|
||||||
|
self.updateDisplayPlaceholder(!(self.videoNode?.ownsContentNode ?? true) || self.isAirPlayActive)
|
||||||
|
}
|
||||||
|
|
||||||
private func updateDisplayPlaceholder(_ displayPlaceholder: Bool) {
|
private func updateDisplayPlaceholder(_ displayPlaceholder: Bool) {
|
||||||
if displayPlaceholder && !self.disablePictureInPicturePlaceholder {
|
if displayPlaceholder && !self.disablePictureInPicturePlaceholder {
|
||||||
if self.pictureInPictureNode == nil {
|
if self.pictureInPictureNode == nil {
|
||||||
let pictureInPictureNode = UniversalVideoGalleryItemPictureInPictureNode(strings: self.presentationData.strings)
|
let pictureInPictureNode = UniversalVideoGalleryItemPictureInPictureNode(strings: self.presentationData.strings, mode: self.isAirPlayActive ? .airplay : .pictureInPicture)
|
||||||
pictureInPictureNode.isUserInteractionEnabled = false
|
pictureInPictureNode.isUserInteractionEnabled = false
|
||||||
self.pictureInPictureNode = pictureInPictureNode
|
self.pictureInPictureNode = pictureInPictureNode
|
||||||
self.insertSubnode(pictureInPictureNode, aboveSubnode: self.scrollNode)
|
self.insertSubnode(pictureInPictureNode, aboveSubnode: self.scrollNode)
|
||||||
if let validLayout = self.validLayout {
|
if let validLayout = self.validLayout {
|
||||||
if let item = self.item {
|
if let item = self.item {
|
||||||
let placeholderSize = item.content.dimensions.fitted(validLayout.0.size)
|
var placeholderSize = item.content.dimensions.fitted(validLayout.0.size)
|
||||||
pictureInPictureNode.frame = CGRect(origin: CGPoint(x: floor((validLayout.0.size.width - placeholderSize.width) / 2.0), y: floor((validLayout.0.size.height - placeholderSize.height) / 2.0)), size: placeholderSize)
|
placeholderSize.height += 2.0
|
||||||
|
pictureInPictureNode.frame = CGRect(origin: CGPoint(x: floor((validLayout.0.size.width - placeholderSize.width) / 2.0), y: floorToScreenPixels((validLayout.0.size.height - placeholderSize.height) / 2.0)), size: placeholderSize)
|
||||||
pictureInPictureNode.updateLayout(placeholderSize, transition: .immediate)
|
pictureInPictureNode.updateLayout(placeholderSize, transition: .immediate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.videoNode?.backgroundColor = UIColor(rgb: 0x333335)
|
|
||||||
}
|
}
|
||||||
} else if let pictureInPictureNode = self.pictureInPictureNode {
|
} else if let pictureInPictureNode = self.pictureInPictureNode {
|
||||||
self.pictureInPictureNode = nil
|
self.pictureInPictureNode = nil
|
||||||
@ -1602,10 +1622,10 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
} else {
|
} else {
|
||||||
videoNode.continuePlayingWithoutSound()
|
videoNode.continuePlayingWithoutSound()
|
||||||
}
|
}
|
||||||
self.updateDisplayPlaceholder(!videoNode.ownsContentNode)
|
self.updateDisplayPlaceholder()
|
||||||
} else if !item.fromPlayingVideo {
|
} else if !item.fromPlayingVideo {
|
||||||
videoNode.canAttachContent = isVisible
|
videoNode.canAttachContent = isVisible
|
||||||
self.updateDisplayPlaceholder(!videoNode.ownsContentNode)
|
self.updateDisplayPlaceholder()
|
||||||
}
|
}
|
||||||
if self.shouldAutoplayOnCentrality() {
|
if self.shouldAutoplayOnCentrality() {
|
||||||
self.hideStatusNodeUntilCentrality = true
|
self.hideStatusNodeUntilCentrality = true
|
||||||
@ -1690,7 +1710,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
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)
|
||||||
|
|
||||||
videoNode.canAttachContent = true
|
videoNode.canAttachContent = true
|
||||||
self.updateDisplayPlaceholder(!videoNode.ownsContentNode)
|
self.updateDisplayPlaceholder()
|
||||||
|
|
||||||
self.context.sharedContext.mediaManager.setOverlayVideoNode(nil)
|
self.context.sharedContext.mediaManager.setOverlayVideoNode(nil)
|
||||||
} else {
|
} else {
|
||||||
@ -1768,7 +1788,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
if self.item?.fromPlayingVideo ?? false {
|
if self.item?.fromPlayingVideo ?? false {
|
||||||
Queue.mainQueue().after(0.001) {
|
Queue.mainQueue().after(0.001) {
|
||||||
videoNode.canAttachContent = true
|
videoNode.canAttachContent = true
|
||||||
self.updateDisplayPlaceholder(!videoNode.ownsContentNode)
|
self.updateDisplayPlaceholder()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2457,6 +2477,16 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
c.setItems(strongSelf.contextMenuSpeedItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
|
c.setItems(strongSelf.contextMenuSpeedItems() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
|
||||||
})))
|
})))
|
||||||
|
|
||||||
|
if #available(iOS 11.0, *) {
|
||||||
|
items.append(.action(ContextMenuActionItem(text: "AirPlay", textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/AirPlay"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||||
|
f(.default)
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.beginAirPlaySetup()
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
if let (message, _, _) = strongSelf.contentInfo() {
|
if let (message, _, _) = strongSelf.contentInfo() {
|
||||||
for media in message.media {
|
for media in message.media {
|
||||||
if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
|
if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
|
||||||
@ -2578,63 +2608,85 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var isAirPlayActive = false
|
||||||
|
private var externalVideoPlayer: ExternalVideoPlayer?
|
||||||
|
func beginAirPlaySetup() {
|
||||||
|
guard let content = self.item?.content as? NativeVideoContent else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if #available(iOS 11.0, *) {
|
||||||
|
self.externalVideoPlayer = ExternalVideoPlayer(context: self.context, content: content)
|
||||||
|
self.externalVideoPlayer?.openRouteSelection()
|
||||||
|
self.externalVideoPlayer?.isActiveUpdated = { [weak self] isActive in
|
||||||
|
if let strongSelf = self {
|
||||||
|
if strongSelf.isAirPlayActive && !isActive {
|
||||||
|
strongSelf.externalVideoPlayer = nil
|
||||||
|
}
|
||||||
|
strongSelf.isAirPlayActive = isActive
|
||||||
|
strongSelf.updateDisplayPlaceholder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objc func openStickersButtonPressed() {
|
@objc func openStickersButtonPressed() {
|
||||||
if let content = self.item?.content as? NativeVideoContent {
|
guard let content = self.item?.content as? NativeVideoContent else {
|
||||||
let context = self.context
|
return
|
||||||
let media = content.fileReference.abstract
|
|
||||||
|
|
||||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
|
||||||
let topController = (self.baseNavigationController()?.topViewController as? ViewController)
|
|
||||||
let progressSignal = Signal<Never, NoError> { subscriber in
|
|
||||||
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
|
|
||||||
topController?.present(controller, in: .window(.root), with: nil)
|
|
||||||
return ActionDisposable { [weak controller] in
|
|
||||||
Queue.mainQueue().async() {
|
|
||||||
controller?.dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|> runOn(Queue.mainQueue())
|
|
||||||
|> delay(0.15, queue: Queue.mainQueue())
|
|
||||||
let progressDisposable = progressSignal.start()
|
|
||||||
|
|
||||||
self.isInteractingPromise.set(true)
|
|
||||||
|
|
||||||
let signal = self.context.engine.stickers.stickerPacksAttachedToMedia(media: media)
|
|
||||||
|> afterDisposed {
|
|
||||||
Queue.mainQueue().async {
|
|
||||||
progressDisposable.dispose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let _ = (signal
|
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] packs in
|
|
||||||
guard let strongSelf = self, !packs.isEmpty else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let baseNavigationController = strongSelf.baseNavigationController()
|
|
||||||
baseNavigationController?.view.endEditing(true)
|
|
||||||
let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packs[0], stickerPacks: packs, sendSticker: nil, actionPerformed: { info, items, action in
|
|
||||||
let animateInAsReplacement = false
|
|
||||||
switch action {
|
|
||||||
case .add:
|
|
||||||
topController?.present(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).string, undo: false, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { _ in
|
|
||||||
return true
|
|
||||||
}), in: .window(.root))
|
|
||||||
case let .remove(positionInList):
|
|
||||||
topController?.present(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).string, undo: true, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in
|
|
||||||
if case .undo = action {
|
|
||||||
let _ = context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start()
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}), in: .window(.root))
|
|
||||||
}
|
|
||||||
}, dismissed: { [weak self] in
|
|
||||||
self?.isInteractingPromise.set(false)
|
|
||||||
})
|
|
||||||
(baseNavigationController?.topViewController as? ViewController)?.present(controller, in: .window(.root), with: nil)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
let context = self.context
|
||||||
|
let media = content.fileReference.abstract
|
||||||
|
|
||||||
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let topController = (self.baseNavigationController()?.topViewController as? ViewController)
|
||||||
|
let progressSignal = Signal<Never, NoError> { subscriber in
|
||||||
|
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
|
||||||
|
topController?.present(controller, in: .window(.root), with: nil)
|
||||||
|
return ActionDisposable { [weak controller] in
|
||||||
|
Queue.mainQueue().async() {
|
||||||
|
controller?.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> runOn(Queue.mainQueue())
|
||||||
|
|> delay(0.15, queue: Queue.mainQueue())
|
||||||
|
let progressDisposable = progressSignal.start()
|
||||||
|
|
||||||
|
self.isInteractingPromise.set(true)
|
||||||
|
|
||||||
|
let signal = self.context.engine.stickers.stickerPacksAttachedToMedia(media: media)
|
||||||
|
|> afterDisposed {
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
progressDisposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = (signal
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] packs in
|
||||||
|
guard let strongSelf = self, !packs.isEmpty else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let baseNavigationController = strongSelf.baseNavigationController()
|
||||||
|
baseNavigationController?.view.endEditing(true)
|
||||||
|
let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packs[0], stickerPacks: packs, sendSticker: nil, actionPerformed: { info, items, action in
|
||||||
|
let animateInAsReplacement = false
|
||||||
|
switch action {
|
||||||
|
case .add:
|
||||||
|
topController?.present(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).string, undo: false, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { _ in
|
||||||
|
return true
|
||||||
|
}), in: .window(.root))
|
||||||
|
case let .remove(positionInList):
|
||||||
|
topController?.present(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).string, undo: true, info: info, topItem: items.first, context: context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in
|
||||||
|
if case .undo = action {
|
||||||
|
let _ = context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}), in: .window(.root))
|
||||||
|
}
|
||||||
|
}, dismissed: { [weak self] in
|
||||||
|
self?.isInteractingPromise.set(false)
|
||||||
|
})
|
||||||
|
(baseNavigationController?.topViewController as? ViewController)?.present(controller, in: .window(.root), with: nil)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override func adjustForPreviewing() {
|
override func adjustForPreviewing() {
|
||||||
|
@ -799,11 +799,11 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|
|||||||
let duration: Int32
|
let duration: Int32
|
||||||
switch product.id {
|
switch product.id {
|
||||||
case "org.telegram.telegramPremium.twelveMonths":
|
case "org.telegram.telegramPremium.twelveMonths":
|
||||||
duration = 86400 * 365
|
duration = 12
|
||||||
case "org.telegram.telegramPremium.sixMonths":
|
case "org.telegram.telegramPremium.sixMonths":
|
||||||
duration = 86400 * 180
|
duration = 6
|
||||||
case "org.telegram.telegramPremium.threeMonths":
|
case "org.telegram.telegramPremium.threeMonths":
|
||||||
duration = 86400 * 90
|
duration = 3
|
||||||
default:
|
default:
|
||||||
duration = 0
|
duration = 0
|
||||||
}
|
}
|
||||||
@ -1259,13 +1259,16 @@ public final class PremiumGiftScreen: ViewControllerComponentContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
completionImpl = { [weak self] duration in
|
completionImpl = { [weak self] duration in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController {
|
||||||
let navigationController = strongSelf.navigationController
|
// let introController = PremiumIntroScreen(context: context, source: .gift(from: context.account.peerId, to: peerId, duration: duration))
|
||||||
strongSelf.dismiss()
|
var controllers = navigationController.viewControllers
|
||||||
let introController = PremiumIntroScreen(context: context, source: .gift(from: context.account.peerId, to: peerId, duration: duration))
|
controllers = controllers.filter { !($0 is PeerInfoScreen) && !($0 is PremiumGiftScreen) }
|
||||||
navigationController?.pushViewController(introController, animated: true)
|
navigationController.setViewControllers(controllers, animated: true)
|
||||||
|
|
||||||
Queue.mainQueue().after(0.1, {
|
Queue.mainQueue().after(0.1, {
|
||||||
introController.view.addSubview(ConfettiView(frame: introController.view.bounds))
|
if let topController = navigationController.viewControllers.first {
|
||||||
|
topController.view.addSubview(ConfettiView(frame: topController.view.bounds))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1487,21 +1487,21 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
secondaryTitleText = environment.strings.Premium_PersonalTitle(otherPeerName).string
|
secondaryTitleText = environment.strings.Premium_PersonalTitle(otherPeerName).string
|
||||||
} else if case let .gift(fromPeerId, _, duration) = context.component.source {
|
} else if case let .gift(fromPeerId, _, duration) = context.component.source {
|
||||||
if fromPeerId == context.component.context.account.peerId {
|
if fromPeerId == context.component.context.account.peerId {
|
||||||
if duration >= 86400 * 365 {
|
if duration == 12 {
|
||||||
secondaryTitleText = environment.strings.Premium_GiftedTitleYou_12Month(otherPeerName).string
|
secondaryTitleText = environment.strings.Premium_GiftedTitleYou_12Month(otherPeerName).string
|
||||||
} else if duration >= 86400 * 180 {
|
} else if duration == 6 {
|
||||||
secondaryTitleText = environment.strings.Premium_GiftedTitleYou_6Month(otherPeerName).string
|
secondaryTitleText = environment.strings.Premium_GiftedTitleYou_6Month(otherPeerName).string
|
||||||
} else if duration >= 86400 * 90 {
|
} else if duration == 3 {
|
||||||
secondaryTitleText = environment.strings.Premium_GiftedTitleYou_3Month(otherPeerName).string
|
secondaryTitleText = environment.strings.Premium_GiftedTitleYou_3Month(otherPeerName).string
|
||||||
} else {
|
} else {
|
||||||
secondaryTitleText = ""
|
secondaryTitleText = ""
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if duration >= 86400 * 365 {
|
if duration == 12 {
|
||||||
secondaryTitleText = environment.strings.Premium_GiftedTitle_12Month(otherPeerName).string
|
secondaryTitleText = environment.strings.Premium_GiftedTitle_12Month(otherPeerName).string
|
||||||
} else if duration >= 86400 * 180 {
|
} else if duration == 6 {
|
||||||
secondaryTitleText = environment.strings.Premium_GiftedTitle_6Month(otherPeerName).string
|
secondaryTitleText = environment.strings.Premium_GiftedTitle_6Month(otherPeerName).string
|
||||||
} else if duration >= 86400 * 90 {
|
} else if duration == 3 {
|
||||||
secondaryTitleText = environment.strings.Premium_GiftedTitle_3Month(otherPeerName).string
|
secondaryTitleText = environment.strings.Premium_GiftedTitle_3Month(otherPeerName).string
|
||||||
} else {
|
} else {
|
||||||
secondaryTitleText = ""
|
secondaryTitleText = ""
|
||||||
|
@ -1190,8 +1190,21 @@ public final class MediaStreamComponentController: ViewControllerComponentContai
|
|||||||
view.expandFromPictureInPicture()
|
view.expandFromPictureInPicture()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let validLayout = self.validLayout {
|
||||||
|
self.view.clipsToBounds = true
|
||||||
|
self.view.layer.cornerRadius = validLayout.deviceMetrics.screenCornerRadius
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
self.view.layer.cornerCurve = .continuous
|
||||||
|
}
|
||||||
|
|
||||||
|
self.view.layer.animatePosition(from: CGPoint(x: 353.0, y: 117.0), to: self.view.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak self] _ in
|
||||||
|
self?.view.layer.cornerRadius = 0.0
|
||||||
|
})
|
||||||
|
self.view.layer.animateScale(from: 0.001, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
}
|
||||||
|
|
||||||
self.view.layer.allowsGroupOpacity = true
|
self.view.layer.allowsGroupOpacity = true
|
||||||
self.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, completion: { [weak self] _ in
|
self.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, completion: { [weak self] _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1226,6 +1239,18 @@ public final class MediaStreamComponentController: ViewControllerComponentContai
|
|||||||
strongSelf.view.layer.allowsGroupOpacity = false
|
strongSelf.view.layer.allowsGroupOpacity = false
|
||||||
strongSelf.dismissImpl(completion: completion)
|
strongSelf.dismissImpl(completion: completion)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if let validLayout = self.validLayout {
|
||||||
|
self.view.clipsToBounds = true
|
||||||
|
self.view.layer.cornerRadius = validLayout.deviceMetrics.screenCornerRadius
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
self.view.layer.cornerCurve = .continuous
|
||||||
|
}
|
||||||
|
|
||||||
|
self.view.layer.animatePosition(from: self.view.center, to: CGPoint(x: 353.0, y: 117.0), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
|
||||||
|
})
|
||||||
|
self.view.layer.animateScale(from: 1.0, to: 0.001, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func dismissImpl(completion: (() -> Void)? = nil) {
|
private func dismissImpl(completion: (() -> Void)? = nil) {
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "gift.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 47 KiB |
188
submodules/TelegramUI/Images.xcassets/Media Gallery/AirPlay.imageset/Airplay.pdf
vendored
Normal file
188
submodules/TelegramUI/Images.xcassets/Media Gallery/AirPlay.imageset/Airplay.pdf
vendored
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 2.335022 2.334961 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
3.865000 17.330078 m
|
||||||
|
3.837526 17.330078 l
|
||||||
|
3.300828 17.330086 2.857980 17.330093 2.497252 17.300621 c
|
||||||
|
2.122623 17.270012 1.778399 17.204330 1.455116 17.039610 c
|
||||||
|
0.953664 16.784107 0.545970 16.376415 0.290468 15.874963 c
|
||||||
|
0.125747 15.551680 0.060066 15.207455 0.029457 14.832827 c
|
||||||
|
-0.000016 14.472098 -0.000009 14.029249 0.000000 13.492552 c
|
||||||
|
0.000000 13.465077 l
|
||||||
|
0.000000 7.865078 l
|
||||||
|
0.000000 7.837605 l
|
||||||
|
-0.000009 7.300907 -0.000016 6.858058 0.029457 6.497330 c
|
||||||
|
0.060066 6.122702 0.125747 5.778477 0.290468 5.455194 c
|
||||||
|
0.545970 4.953741 0.953664 4.546048 1.455116 4.290545 c
|
||||||
|
1.778399 4.125825 2.122623 4.060143 2.497252 4.029535 c
|
||||||
|
2.857977 4.000063 3.300821 4.000070 3.837511 4.000078 c
|
||||||
|
3.837541 4.000078 l
|
||||||
|
3.865000 4.000078 l
|
||||||
|
4.665000 4.000078 l
|
||||||
|
5.032270 4.000078 5.330000 4.297809 5.330000 4.665078 c
|
||||||
|
5.330000 5.032348 5.032270 5.330078 4.665000 5.330078 c
|
||||||
|
3.865000 5.330078 l
|
||||||
|
3.293975 5.330078 2.905699 5.330595 2.605556 5.355118 c
|
||||||
|
2.313176 5.379006 2.163463 5.422318 2.058923 5.475584 c
|
||||||
|
1.807727 5.603576 1.603498 5.807804 1.475507 6.059001 c
|
||||||
|
1.422241 6.163542 1.378929 6.313254 1.355040 6.605635 c
|
||||||
|
1.330518 6.905777 1.330000 7.294052 1.330000 7.865078 c
|
||||||
|
1.330000 13.465077 l
|
||||||
|
1.330000 14.036103 1.330518 14.424378 1.355040 14.724522 c
|
||||||
|
1.378929 15.016902 1.422241 15.166615 1.475507 15.271155 c
|
||||||
|
1.603498 15.522351 1.807727 15.726581 2.058923 15.854571 c
|
||||||
|
2.163463 15.907838 2.313176 15.951150 2.605556 15.975038 c
|
||||||
|
2.905699 15.999560 3.293974 16.000078 3.865000 16.000078 c
|
||||||
|
15.465000 16.000078 l
|
||||||
|
16.036026 16.000078 16.424301 15.999560 16.724445 15.975038 c
|
||||||
|
17.016823 15.951150 17.166538 15.907838 17.271076 15.854571 c
|
||||||
|
17.522274 15.726581 17.726501 15.522351 17.854492 15.271155 c
|
||||||
|
17.907761 15.166615 17.951073 15.016902 17.974960 14.724522 c
|
||||||
|
17.999481 14.424378 18.000000 14.036104 18.000000 13.465078 c
|
||||||
|
18.000000 7.865078 l
|
||||||
|
18.000000 7.294053 17.999481 6.905778 17.974960 6.605635 c
|
||||||
|
17.951073 6.313254 17.907761 6.163542 17.854492 6.059001 c
|
||||||
|
17.726501 5.807804 17.522274 5.603576 17.271076 5.475584 c
|
||||||
|
17.166538 5.422318 17.016823 5.379006 16.724445 5.355118 c
|
||||||
|
16.424301 5.330595 16.036026 5.330078 15.465000 5.330078 c
|
||||||
|
14.665000 5.330078 l
|
||||||
|
14.297730 5.330078 14.000000 5.032348 14.000000 4.665078 c
|
||||||
|
14.000000 4.297809 14.297730 4.000078 14.665000 4.000078 c
|
||||||
|
15.465000 4.000078 l
|
||||||
|
15.492459 4.000078 l
|
||||||
|
15.492488 4.000078 l
|
||||||
|
16.029179 4.000070 16.472023 4.000063 16.832748 4.029535 c
|
||||||
|
17.207378 4.060143 17.551601 4.125825 17.874886 4.290545 c
|
||||||
|
18.376335 4.546048 18.784031 4.953741 19.039532 5.455194 c
|
||||||
|
19.204254 5.778477 19.269936 6.122702 19.300545 6.497330 c
|
||||||
|
19.330015 6.858045 19.330009 7.300875 19.330002 7.837547 c
|
||||||
|
19.330002 7.837619 l
|
||||||
|
19.330002 7.865078 l
|
||||||
|
19.330002 13.465078 l
|
||||||
|
19.330002 13.492537 l
|
||||||
|
19.330002 13.492610 l
|
||||||
|
19.330009 14.029282 19.330015 14.472111 19.300545 14.832827 c
|
||||||
|
19.269936 15.207455 19.204254 15.551680 19.039532 15.874963 c
|
||||||
|
18.784031 16.376415 18.376335 16.784107 17.874886 17.039610 c
|
||||||
|
17.551601 17.204330 17.207378 17.270012 16.832748 17.300621 c
|
||||||
|
16.472019 17.330093 16.029171 17.330086 15.492474 17.330078 c
|
||||||
|
15.465000 17.330078 l
|
||||||
|
3.865000 17.330078 l
|
||||||
|
h
|
||||||
|
9.549997 5.417668 m
|
||||||
|
9.624264 5.444814 9.705738 5.444814 9.780005 5.417668 c
|
||||||
|
9.779352 5.417907 9.779119 5.418036 9.779340 5.417960 c
|
||||||
|
9.779449 5.417922 9.779672 5.417834 9.780010 5.417685 c
|
||||||
|
9.785703 5.415166 9.824282 5.395164 9.915651 5.303084 c
|
||||||
|
10.027624 5.190243 10.164604 5.026791 10.383287 4.764371 c
|
||||||
|
11.967221 2.863649 l
|
||||||
|
12.324518 2.434894 12.562548 2.148232 12.716402 1.925475 c
|
||||||
|
12.838650 1.748481 12.861221 1.674737 12.864748 1.663215 c
|
||||||
|
12.864949 1.662560 l
|
||||||
|
12.864219 1.564913 12.820898 1.472416 12.746351 1.409344 c
|
||||||
|
12.745722 1.409081 l
|
||||||
|
12.734627 1.404418 12.663531 1.374542 12.449274 1.355145 c
|
||||||
|
12.179653 1.330734 11.807049 1.330078 11.248934 1.330078 c
|
||||||
|
8.081068 1.330078 l
|
||||||
|
7.522953 1.330078 7.150349 1.330734 6.880728 1.355145 c
|
||||||
|
6.666473 1.374543 6.595378 1.404416 6.584280 1.409080 c
|
||||||
|
6.583652 1.409343 l
|
||||||
|
6.509105 1.472416 6.465783 1.564913 6.465053 1.662560 c
|
||||||
|
6.465254 1.663213 l
|
||||||
|
6.468780 1.674733 6.491349 1.748475 6.613600 1.925474 c
|
||||||
|
6.767454 2.148230 7.005483 2.434893 7.362779 2.863647 c
|
||||||
|
8.946714 4.764370 l
|
||||||
|
9.165398 5.026790 9.302377 5.190243 9.414351 5.303084 c
|
||||||
|
9.505718 5.395163 9.544298 5.415166 9.549992 5.417685 c
|
||||||
|
9.551012 5.418137 9.550975 5.418026 9.549997 5.417668 c
|
||||||
|
h
|
||||||
|
10.236588 6.666842 m
|
||||||
|
9.867472 6.801756 9.462530 6.801756 9.093414 6.666842 c
|
||||||
|
8.834289 6.572128 8.636753 6.407670 8.470269 6.239893 c
|
||||||
|
8.312561 6.080961 8.138463 5.872022 7.942029 5.636275 c
|
||||||
|
7.924980 5.615815 l
|
||||||
|
6.341045 3.715093 l
|
||||||
|
6.320368 3.690281 l
|
||||||
|
5.989172 3.292866 5.711553 2.959741 5.519254 2.681324 c
|
||||||
|
5.330824 2.408508 5.141049 2.075929 5.135232 1.690181 c
|
||||||
|
5.127517 1.178505 5.355523 0.691704 5.753542 0.370064 c
|
||||||
|
6.053606 0.127583 6.430592 0.060459 6.760806 0.030563 c
|
||||||
|
7.097775 0.000055 7.531381 0.000065 8.048665 0.000076 c
|
||||||
|
8.048759 0.000076 l
|
||||||
|
8.081068 0.000076 l
|
||||||
|
11.248934 0.000076 l
|
||||||
|
11.281242 0.000076 l
|
||||||
|
11.281337 0.000076 l
|
||||||
|
11.798622 0.000065 12.232226 0.000055 12.569197 0.030563 c
|
||||||
|
12.899410 0.060459 13.276397 0.127583 13.576460 0.370064 c
|
||||||
|
13.974480 0.691704 14.202486 1.178505 14.194770 1.690181 c
|
||||||
|
14.188953 2.075929 13.999178 2.408508 13.810747 2.681325 c
|
||||||
|
13.618445 2.959745 13.340822 3.292877 13.009618 3.690300 c
|
||||||
|
12.988955 3.715096 l
|
||||||
|
11.405022 5.615816 l
|
||||||
|
11.387974 5.636274 l
|
||||||
|
11.191540 5.872021 11.017441 6.080961 10.859733 6.239894 c
|
||||||
|
10.693249 6.407670 10.495712 6.572128 10.236588 6.666842 c
|
||||||
|
h
|
||||||
|
f*
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
5505
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Pages 5 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000005595 00000 n
|
||||||
|
0000005618 00000 n
|
||||||
|
0000005791 00000 n
|
||||||
|
0000005865 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
5924
|
||||||
|
%%EOF
|
12
submodules/TelegramUI/Images.xcassets/Media Gallery/AirPlay.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Media Gallery/AirPlay.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Airplay.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,8 @@ import UrlEscaping
|
|||||||
import TelegramStringFormatting
|
import TelegramStringFormatting
|
||||||
import WallpaperBackgroundNode
|
import WallpaperBackgroundNode
|
||||||
import ReactionSelectionNode
|
import ReactionSelectionNode
|
||||||
|
import AnimatedStickerNode
|
||||||
|
import TelegramAnimatedStickerNode
|
||||||
|
|
||||||
private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, message: Message, accountPeerId: PeerId) -> NSAttributedString? {
|
private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, message: Message, accountPeerId: PeerId) -> NSAttributedString? {
|
||||||
return universalServiceMessageString(presentationData: (theme.theme, theme.wallpaper), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: EngineMessage(message), accountPeerId: accountPeerId, forChatList: false)
|
return universalServiceMessageString(presentationData: (theme.theme, theme.wallpaper), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: EngineMessage(message), accountPeerId: accountPeerId, forChatList: false)
|
||||||
@ -29,8 +31,7 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
private let mediaBackgroundNode: NavigationBackgroundNode
|
private let mediaBackgroundNode: NavigationBackgroundNode
|
||||||
private let titleNode: TextNode
|
private let titleNode: TextNode
|
||||||
private let subtitleNode: TextNode
|
private let subtitleNode: TextNode
|
||||||
|
private let animationNode: AnimatedStickerNode
|
||||||
private let giftNode: ASImageNode
|
|
||||||
|
|
||||||
private let buttonNode: HighlightTrackingButtonNode
|
private let buttonNode: HighlightTrackingButtonNode
|
||||||
private let buttonStarsNode: PremiumStarsNode
|
private let buttonStarsNode: PremiumStarsNode
|
||||||
@ -62,10 +63,10 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
self.buttonNode = HighlightTrackingButtonNode()
|
self.buttonNode = HighlightTrackingButtonNode()
|
||||||
self.buttonNode.clipsToBounds = true
|
self.buttonNode.clipsToBounds = true
|
||||||
self.buttonNode.cornerRadius = 17.0
|
self.buttonNode.cornerRadius = 17.0
|
||||||
|
|
||||||
self.giftNode = ASImageNode()
|
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||||
self.giftNode.isUserInteractionEnabled = false
|
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "Gift"), width: 384, height: 384, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||||
self.giftNode.displaysAsynchronously = false
|
self.animationNode.visibility = true
|
||||||
|
|
||||||
self.buttonStarsNode = PremiumStarsNode()
|
self.buttonStarsNode = PremiumStarsNode()
|
||||||
|
|
||||||
@ -80,7 +81,7 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
self.addSubnode(self.mediaBackgroundNode)
|
self.addSubnode(self.mediaBackgroundNode)
|
||||||
self.addSubnode(self.titleNode)
|
self.addSubnode(self.titleNode)
|
||||||
self.addSubnode(self.subtitleNode)
|
self.addSubnode(self.subtitleNode)
|
||||||
self.addSubnode(self.giftNode)
|
self.addSubnode(self.animationNode)
|
||||||
|
|
||||||
self.addSubnode(self.buttonNode)
|
self.addSubnode(self.buttonNode)
|
||||||
self.buttonNode.addSubnode(self.buttonStarsNode)
|
self.buttonNode.addSubnode(self.buttonStarsNode)
|
||||||
@ -128,7 +129,7 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: true, headerSpacing: 0.0, hidesBackground: .always, forceFullCorners: false, forceAlignment: .center)
|
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: true, headerSpacing: 0.0, hidesBackground: .always, forceFullCorners: false, forceAlignment: .center)
|
||||||
|
|
||||||
return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in
|
return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in
|
||||||
let imageSize = CGSize(width: 220.0, height: 210.0)
|
let giftSize = CGSize(width: 220.0, height: 240.0)
|
||||||
|
|
||||||
let attributedString = attributedServiceMessageString(theme: item.presentationData.theme, strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, message: item.message, accountPeerId: item.context.account.peerId)
|
let attributedString = attributedServiceMessageString(theme: item.presentationData.theme, strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, message: item.message, accountPeerId: item.context.account.peerId)
|
||||||
|
|
||||||
@ -139,7 +140,7 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
if let action = media as? TelegramMediaAction {
|
if let action = media as? TelegramMediaAction {
|
||||||
switch action.action {
|
switch action.action {
|
||||||
case let .giftPremium(_, _, durationValue):
|
case let .giftPremium(_, _, durationValue):
|
||||||
duration = item.presentationData.strings.Notification_PremiumGift_Subtitle(timeIntervalString(strings: item.presentationData.strings, value: durationValue)).string
|
duration = item.presentationData.strings.Notification_PremiumGift_Subtitle(item.presentationData.strings.Notification_PremiumGift_Months(durationValue)).string
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -148,11 +149,11 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
|
|
||||||
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: attributedString, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: attributedString, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Notification_PremiumGift_Title, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: imageSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Notification_PremiumGift_Title, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: giftSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: duration, font: Font.regular(13.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: imageSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: duration, font: Font.regular(13.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: giftSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
let (buttonTitleLayout, buttonTitleApply) = makeButtonTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Notification_PremiumGift_View, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: imageSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
let (buttonTitleLayout, buttonTitleApply) = makeButtonTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Notification_PremiumGift_View, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: giftSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
var labelRects = labelLayout.linesRects()
|
var labelRects = labelLayout.linesRects()
|
||||||
if labelRects.count > 1 {
|
if labelRects.count > 1 {
|
||||||
@ -184,7 +185,7 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
backgroundMaskUpdated = true
|
backgroundMaskUpdated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
let backgroundSize = CGSize(width: labelLayout.size.width + 8.0 + 8.0, height: labelLayout.size.height + imageSize.height + 18.0)
|
let backgroundSize = CGSize(width: labelLayout.size.width + 8.0 + 8.0, height: labelLayout.size.height + giftSize.height + 18.0)
|
||||||
|
|
||||||
return (backgroundSize.width, { boundingWidth in
|
return (backgroundSize.width, { boundingWidth in
|
||||||
return (backgroundSize, { [weak self] animation, synchronousLoads, _ in
|
return (backgroundSize, { [weak self] animation, synchronousLoads, _ in
|
||||||
@ -193,7 +194,7 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
|
|
||||||
strongSelf.backgroundColorNode.backgroundColor = selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper)
|
strongSelf.backgroundColorNode.backgroundColor = selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper)
|
||||||
|
|
||||||
let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - imageSize.width) / 2.0), y: labelLayout.size.height + 16.0), size: imageSize)
|
let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - giftSize.width) / 2.0), y: labelLayout.size.height + 16.0), size: giftSize)
|
||||||
let mediaBackgroundFrame = imageFrame.insetBy(dx: -2.0, dy: -2.0)
|
let mediaBackgroundFrame = imageFrame.insetBy(dx: -2.0, dy: -2.0)
|
||||||
strongSelf.mediaBackgroundNode.frame = mediaBackgroundFrame
|
strongSelf.mediaBackgroundNode.frame = mediaBackgroundFrame
|
||||||
|
|
||||||
@ -201,10 +202,9 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
strongSelf.mediaBackgroundNode.update(size: mediaBackgroundFrame.size, transition: .immediate)
|
strongSelf.mediaBackgroundNode.update(size: mediaBackgroundFrame.size, transition: .immediate)
|
||||||
strongSelf.buttonNode.backgroundColor = item.presentationData.theme.theme.overallDarkAppearance ? UIColor(rgb: 0xffffff, alpha: 0.12) : UIColor(rgb: 0x000000, alpha: 0.12)
|
strongSelf.buttonNode.backgroundColor = item.presentationData.theme.theme.overallDarkAppearance ? UIColor(rgb: 0xffffff, alpha: 0.12) : UIColor(rgb: 0x000000, alpha: 0.12)
|
||||||
|
|
||||||
strongSelf.giftNode.image = UIImage(bundleImageName: "Components/Gift")
|
let iconSize = CGSize(width: 160.0, height: 160.0)
|
||||||
if let image = strongSelf.giftNode.image {
|
strongSelf.animationNode.frame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - iconSize.width) / 2.0), y: mediaBackgroundFrame.minY - 16.0), size: iconSize)
|
||||||
strongSelf.giftNode.frame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - image.size.width) / 2.0), y: mediaBackgroundFrame.minY + 14.0), size: image.size)
|
strongSelf.animationNode.updateLayout(size: iconSize)
|
||||||
}
|
|
||||||
|
|
||||||
let _ = labelApply()
|
let _ = labelApply()
|
||||||
let _ = titleApply()
|
let _ = titleApply()
|
||||||
@ -214,7 +214,7 @@ class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
let labelFrame = CGRect(origin: CGPoint(x: 8.0, y: 2.0), size: labelLayout.size)
|
let labelFrame = CGRect(origin: CGPoint(x: 8.0, y: 2.0), size: labelLayout.size)
|
||||||
strongSelf.labelNode.frame = labelFrame
|
strongSelf.labelNode.frame = labelFrame
|
||||||
|
|
||||||
let titleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - titleLayout.size.width) / 2.0) , y: mediaBackgroundFrame.minY + 121.0), size: titleLayout.size)
|
let titleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - titleLayout.size.width) / 2.0) , y: mediaBackgroundFrame.minY + 151.0), size: titleLayout.size)
|
||||||
strongSelf.titleNode.frame = titleFrame
|
strongSelf.titleNode.frame = titleFrame
|
||||||
|
|
||||||
let subtitleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0) , y: titleFrame.maxY - 1.0), size: subtitleLayout.size)
|
let subtitleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0) , y: titleFrame.maxY - 1.0), size: subtitleLayout.size)
|
||||||
|
@ -0,0 +1,121 @@
|
|||||||
|
import Foundation
|
||||||
|
import AVFoundation
|
||||||
|
import SwiftSignalKit
|
||||||
|
import UniversalMediaPlayer
|
||||||
|
import AccountContext
|
||||||
|
import AVKit
|
||||||
|
|
||||||
|
public class ExternalVideoPlayer: NSObject, AVRoutePickerViewDelegate {
|
||||||
|
private let context: AccountContext
|
||||||
|
let content: NativeVideoContent
|
||||||
|
|
||||||
|
let player: AVPlayer?
|
||||||
|
private var didPlayToEndTimeObserver: NSObjectProtocol?
|
||||||
|
private var timeObserver: Any?
|
||||||
|
|
||||||
|
private var statusValue = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: false, progress: 0.0, display: true), soundEnabled: true)
|
||||||
|
private let _status = ValuePromise<MediaPlayerStatus>()
|
||||||
|
var status: Signal<MediaPlayerStatus, NoError> {
|
||||||
|
return self._status.get()
|
||||||
|
}
|
||||||
|
private var seekId: Int = 0
|
||||||
|
|
||||||
|
private weak var routePickerView: UIView?
|
||||||
|
|
||||||
|
public var isActiveUpdated: (Bool) -> Void = { _ in }
|
||||||
|
|
||||||
|
public init(context: AccountContext, content: NativeVideoContent) {
|
||||||
|
self.context = context
|
||||||
|
self.content = content
|
||||||
|
|
||||||
|
if let path = context.account.postbox.mediaBox.completedResourcePath(content.fileReference.media.resource, pathExtension: "mp4") {
|
||||||
|
let player = AVPlayer(url: URL(fileURLWithPath: path))
|
||||||
|
self.player = player
|
||||||
|
} else {
|
||||||
|
self.player = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.startObservingForAirPlayStatusChanges()
|
||||||
|
self.isActiveUpdated(self.player?.isExternalPlaybackActive ?? false)
|
||||||
|
|
||||||
|
if let player = self.player {
|
||||||
|
self.didPlayToEndTimeObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem, queue: nil, using: { [weak self] notification in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.player?.seek(to: CMTime(seconds: 0.0, preferredTimescale: 30))
|
||||||
|
strongSelf.play()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
self.timeObserver = player.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1, timescale: 10), queue: DispatchQueue.main) { [weak self] time in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.statusValue = MediaPlayerStatus(generationTimestamp: 0.0, duration: strongSelf.statusValue.duration, dimensions: CGSize(), timestamp: CMTimeGetSeconds(time), baseRate: 1.0, seekId: strongSelf.seekId, status: strongSelf.statusValue.status, soundEnabled: true)
|
||||||
|
strongSelf._status.set(strongSelf.statusValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self._status.set(self.statusValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
if let timeObserver = self.timeObserver {
|
||||||
|
self.player?.removeTimeObserver(timeObserver)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let didPlayToEndTimeObserver = self.didPlayToEndTimeObserver {
|
||||||
|
NotificationCenter.default.removeObserver(didPlayToEndTimeObserver)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.stopObservingForAirPlayStatusChanges()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func play() {
|
||||||
|
self.player?.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func openRouteSelection() {
|
||||||
|
if #available(iOS 11.0, *) {
|
||||||
|
let routePickerView = AVRoutePickerView()
|
||||||
|
routePickerView.delegate = self
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
routePickerView.prioritizesVideoDevices = true
|
||||||
|
}
|
||||||
|
self.context.sharedContext.mainWindow?.viewController?.view.addSubview(routePickerView)
|
||||||
|
|
||||||
|
if let routePickerButton = routePickerView.subviews.first(where: { $0 is UIButton }) as? UIButton {
|
||||||
|
routePickerButton.sendActions(for: .touchUpInside)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 11.0, *)
|
||||||
|
public func routePickerViewDidEndPresentingRoutes(_ routePickerView: AVRoutePickerView) {
|
||||||
|
routePickerView.removeFromSuperview()
|
||||||
|
|
||||||
|
self.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var observerContextAirplay = 1
|
||||||
|
|
||||||
|
func startObservingForAirPlayStatusChanges()
|
||||||
|
{
|
||||||
|
self.player?.addObserver(self, forKeyPath: #keyPath(AVPlayer.isExternalPlaybackActive), options: .new, context: &observerContextAirplay)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopObservingForAirPlayStatusChanges()
|
||||||
|
{
|
||||||
|
self.player?.removeObserver(self, forKeyPath: #keyPath(AVPlayer.isExternalPlaybackActive))
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||||
|
if context == &observerContextAirplay {
|
||||||
|
self.isActiveUpdated(self.player?.isExternalPlaybackActive ?? false)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -777,6 +777,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
}
|
}
|
||||||
self.controller?.present(alertController, in: .window(.root))
|
self.controller?.present(alertController, in: .window(.root))
|
||||||
}
|
}
|
||||||
|
case "web_app_setup_closing_behavior":
|
||||||
|
if let json = json, let _ = json["need_confirmation"] as? String {
|
||||||
|
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user