mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-08 08:31:13 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
00a149d06e
@ -7057,6 +7057,11 @@ Sorry for the inconvenience.";
|
|||||||
"Conversation.CopyProtectionInfoGroup" = "Admins restricted members to copy or forward content from this group.";
|
"Conversation.CopyProtectionInfoGroup" = "Admins restricted members to copy or forward content from this group.";
|
||||||
"Conversation.CopyProtectionInfoChannel" = "Admins restricted members to copy or forward content from this channel.";
|
"Conversation.CopyProtectionInfoChannel" = "Admins restricted members to copy or forward content from this channel.";
|
||||||
|
|
||||||
|
"Conversation.CopyProtectionForwardingDisabledGroup" = "Forwards from this group are restricted";
|
||||||
|
"Conversation.CopyProtectionForwardingDisabledChannel" = "Forwards from this channel are restricted";
|
||||||
|
"Conversation.CopyProtectionSavingDisabledGroup" = "Saving from this group is restricted";
|
||||||
|
"Conversation.CopyProtectionSavingDisabledChannel" = "Saving from this channel is restricted";
|
||||||
|
|
||||||
"Channel.AdminLog.MessageToggleNoForwardsOn" = "%@ restricted message forwarding";
|
"Channel.AdminLog.MessageToggleNoForwardsOn" = "%@ restricted message forwarding";
|
||||||
"Channel.AdminLog.MessageToggleNoForwardsOff" = "%@ allowed message forwarding";
|
"Channel.AdminLog.MessageToggleNoForwardsOff" = "%@ allowed message forwarding";
|
||||||
|
|
||||||
|
@ -2,11 +2,14 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
import LottieMeshSwift
|
import LottieMeshSwift
|
||||||
|
import Postbox
|
||||||
|
|
||||||
public final class ViewController: UIViewController {
|
public final class ViewController: UIViewController {
|
||||||
override public func viewDidLoad() {
|
override public func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
TempBox.initializeShared(basePath: NSTemporaryDirectory(), processType: "test", launchSpecificId: Int64.random(in: Int64.min ..< Int64.max))
|
||||||
|
|
||||||
self.view.backgroundColor = .black
|
self.view.backgroundColor = .black
|
||||||
|
|
||||||
let path = Bundle.main.path(forResource: "Fireworks", ofType: "json")!
|
let path = Bundle.main.path(forResource: "Fireworks", ofType: "json")!
|
||||||
@ -16,7 +19,12 @@ public final class ViewController: UIViewController {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
if #available(iOS 13.0, *) {
|
if #available(iOS 13.0, *) {
|
||||||
let animation = generateMeshAnimation(data: try! Data(contentsOf: URL(fileURLWithPath: path)))!
|
let startTime = CFAbsoluteTimeGetCurrent()
|
||||||
|
let animationFile = generateMeshAnimation(data: try! Data(contentsOf: URL(fileURLWithPath: path)))!
|
||||||
|
print("Time: \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0)")
|
||||||
|
let buffer = MeshReadBuffer(data: try! Data(contentsOf: URL(fileURLWithPath: animationFile.path)))
|
||||||
|
let animation = MeshAnimation.read(buffer: buffer)
|
||||||
|
|
||||||
let renderer = MeshRenderer(wireframe: true)!
|
let renderer = MeshRenderer(wireframe: true)!
|
||||||
|
|
||||||
renderer.frame = CGRect(origin: CGPoint(x: 0.0, y: 50.0), size: CGSize(width: 300.0, height: 300.0))
|
renderer.frame = CGRect(origin: CGPoint(x: 0.0, y: 50.0), size: CGSize(width: 300.0, height: 300.0))
|
||||||
|
@ -13,15 +13,18 @@ public enum SharedMediaPlaybackDataType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum SharedMediaPlaybackDataSource: Equatable {
|
public enum SharedMediaPlaybackDataSource: Equatable {
|
||||||
case telegramFile(FileMediaReference)
|
case telegramFile(reference: FileMediaReference, isCopyProtected: Bool)
|
||||||
|
|
||||||
public static func ==(lhs: SharedMediaPlaybackDataSource, rhs: SharedMediaPlaybackDataSource) -> Bool {
|
public static func ==(lhs: SharedMediaPlaybackDataSource, rhs: SharedMediaPlaybackDataSource) -> Bool {
|
||||||
switch lhs {
|
switch lhs {
|
||||||
case let .telegramFile(lhsFileReference):
|
case let .telegramFile(lhsFileReference, lhsIsCopyProtected):
|
||||||
if case let .telegramFile(rhsFileReference) = rhs {
|
if case let .telegramFile(rhsFileReference, rhsIsCopyProtected) = rhs {
|
||||||
if !lhsFileReference.media.isEqual(to: rhsFileReference.media) {
|
if !lhsFileReference.media.isEqual(to: rhsFileReference.media) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhsIsCopyProtected != rhsIsCopyProtected {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
|
@ -190,6 +190,11 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
|||||||
private let highlightedBackgroundNode: ASDisplayNode
|
private let highlightedBackgroundNode: ASDisplayNode
|
||||||
private let maskNode: ASImageNode
|
private let maskNode: ASImageNode
|
||||||
|
|
||||||
|
private let containerNode: ASDisplayNode
|
||||||
|
override var controlsContainer: ASDisplayNode {
|
||||||
|
return self.containerNode
|
||||||
|
}
|
||||||
|
|
||||||
private let avatarNode: AvatarNode
|
private let avatarNode: AvatarNode
|
||||||
private let titleNode: TextNode
|
private let titleNode: TextNode
|
||||||
private let statusNode: TextNode
|
private let statusNode: TextNode
|
||||||
@ -207,6 +212,8 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
|||||||
self.backgroundNode = ASDisplayNode()
|
self.backgroundNode = ASDisplayNode()
|
||||||
self.backgroundNode.isLayerBacked = true
|
self.backgroundNode.isLayerBacked = true
|
||||||
|
|
||||||
|
self.containerNode = ASDisplayNode()
|
||||||
|
|
||||||
self.maskNode = ASImageNode()
|
self.maskNode = ASImageNode()
|
||||||
self.maskNode.isUserInteractionEnabled = false
|
self.maskNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
@ -239,12 +246,13 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
|||||||
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||||
|
|
||||||
self.addSubnode(self.backgroundNode)
|
self.addSubnode(self.backgroundNode)
|
||||||
self.addSubnode(self.avatarNode)
|
self.addSubnode(self.containerNode)
|
||||||
self.addSubnode(self.typeIconNode)
|
self.containerNode.addSubnode(self.avatarNode)
|
||||||
self.addSubnode(self.titleNode)
|
self.containerNode.addSubnode(self.typeIconNode)
|
||||||
self.addSubnode(self.statusNode)
|
self.containerNode.addSubnode(self.titleNode)
|
||||||
self.addSubnode(self.dateNode)
|
self.containerNode.addSubnode(self.statusNode)
|
||||||
self.addSubnode(self.infoButtonNode)
|
self.containerNode.addSubnode(self.dateNode)
|
||||||
|
self.containerNode.addSubnode(self.infoButtonNode)
|
||||||
self.addSubnode(self.accessibilityArea)
|
self.addSubnode(self.accessibilityArea)
|
||||||
|
|
||||||
self.infoButtonNode.addTarget(self, action: #selector(self.infoPressed), forControlEvents: .touchUpInside)
|
self.infoButtonNode.addTarget(self, action: #selector(self.infoPressed), forControlEvents: .touchUpInside)
|
||||||
@ -631,6 +639,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
let topHighlightInset: CGFloat = (first || !nodeLayout.insets.top.isZero) ? 0.0 : separatorHeight
|
let topHighlightInset: CGFloat = (first || !nodeLayout.insets.top.isZero) ? 0.0 : separatorHeight
|
||||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: nodeLayout.contentSize.width, height: nodeLayout.contentSize.height))
|
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: nodeLayout.contentSize.width, height: nodeLayout.contentSize.height))
|
||||||
|
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: strongSelf.backgroundNode.frame.size)
|
||||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top - topHighlightInset), size: CGSize(width: nodeLayout.size.width, height: nodeLayout.size.height + topHighlightInset))
|
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top - topHighlightInset), size: CGSize(width: nodeLayout.size.width, height: nodeLayout.size.height + topHighlightInset))
|
||||||
|
|
||||||
strongSelf.updateLayout(size: nodeLayout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset)
|
strongSelf.updateLayout(size: nodeLayout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset)
|
||||||
|
@ -109,6 +109,11 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN
|
|||||||
private let highlightedBackgroundNode: ASDisplayNode
|
private let highlightedBackgroundNode: ASDisplayNode
|
||||||
private let maskNode: ASImageNode
|
private let maskNode: ASImageNode
|
||||||
|
|
||||||
|
private let containerNode: ASDisplayNode
|
||||||
|
override var controlsContainer: ASDisplayNode {
|
||||||
|
return self.containerNode
|
||||||
|
}
|
||||||
|
|
||||||
private let titleNode: TextNode
|
private let titleNode: TextNode
|
||||||
private let labelNode: TextNode
|
private let labelNode: TextNode
|
||||||
private let arrowNode: ASImageNode
|
private let arrowNode: ASImageNode
|
||||||
@ -138,6 +143,8 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN
|
|||||||
self.bottomStripeNode = ASDisplayNode()
|
self.bottomStripeNode = ASDisplayNode()
|
||||||
self.bottomStripeNode.isLayerBacked = true
|
self.bottomStripeNode.isLayerBacked = true
|
||||||
|
|
||||||
|
self.containerNode = ASDisplayNode()
|
||||||
|
|
||||||
self.maskNode = ASImageNode()
|
self.maskNode = ASImageNode()
|
||||||
self.maskNode.isUserInteractionEnabled = false
|
self.maskNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
@ -161,9 +168,10 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN
|
|||||||
|
|
||||||
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||||
|
|
||||||
self.addSubnode(self.titleNode)
|
self.addSubnode(self.containerNode)
|
||||||
self.addSubnode(self.labelNode)
|
self.containerNode.addSubnode(self.titleNode)
|
||||||
self.addSubnode(self.arrowNode)
|
self.containerNode.addSubnode(self.labelNode)
|
||||||
|
self.containerNode.addSubnode(self.arrowNode)
|
||||||
self.addSubnode(self.activateArea)
|
self.addSubnode(self.activateArea)
|
||||||
|
|
||||||
self.activateArea.activate = { [weak self] in
|
self.activateArea.activate = { [weak self] in
|
||||||
@ -345,6 +353,7 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN
|
|||||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||||
|
|
||||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||||
|
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: strongSelf.backgroundNode.frame.size)
|
||||||
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||||
transition.updateFrame(node: strongSelf.topStripeNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)))
|
transition.updateFrame(node: strongSelf.topStripeNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)))
|
||||||
transition.updateFrame(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)))
|
transition.updateFrame(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)))
|
||||||
|
@ -843,8 +843,13 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
updatedContentAreaInScreenSpace.size.width = self.bounds.width
|
updatedContentAreaInScreenSpace.size.width = self.bounds.width
|
||||||
|
|
||||||
self.clippingNode.view.mask = putBackInfo.maskView
|
self.clippingNode.view.mask = putBackInfo.maskView
|
||||||
self.clippingNode.layer.animateFrame(from: self.clippingNode.frame, to: updatedContentAreaInScreenSpace, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
let previousFrame = self.clippingNode.frame
|
||||||
self.clippingNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: updatedContentAreaInScreenSpace.minY, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
self.clippingNode.position = updatedContentAreaInScreenSpace.center
|
||||||
|
self.clippingNode.bounds = CGRect(origin: CGPoint(), size: updatedContentAreaInScreenSpace.size)
|
||||||
|
self.clippingNode.layer.animatePosition(from: previousFrame.center, to: updatedContentAreaInScreenSpace.center, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: true)
|
||||||
|
self.clippingNode.layer.animateBounds(from: CGRect(origin: CGPoint(), size: previousFrame.size), to: CGRect(origin: CGPoint(), size: updatedContentAreaInScreenSpace.size), duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: true)
|
||||||
|
//self.clippingNode.layer.animateFrame(from: previousFrame, to: updatedContentAreaInScreenSpace, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
||||||
|
//self.clippingNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: updatedContentAreaInScreenSpace.minY, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
let intermediateCompletion: () -> Void = { [weak self, weak contentParentNode] in
|
let intermediateCompletion: () -> Void = { [weak self, weak contentParentNode] in
|
||||||
@ -1135,6 +1140,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.reactionContextNodeIsAnimatingOut = true
|
self.reactionContextNodeIsAnimatingOut = true
|
||||||
|
reactionContextNode.willAnimateOutToReaction(value: value)
|
||||||
reactionContextNode.animateOutToReaction(value: value, targetEmptyNode: targetEmptyNode, targetFilledNode: targetFilledNode, hideNode: hideNode, completion: { [weak self] in
|
reactionContextNode.animateOutToReaction(value: value, targetEmptyNode: targetEmptyNode, targetFilledNode: targetFilledNode, hideNode: hideNode, completion: { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
|
import SwiftSignalKit
|
||||||
|
|
||||||
private let containerInsets = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0)
|
private let containerInsets = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0)
|
||||||
|
|
||||||
@ -59,6 +60,7 @@ final class ActionSheetControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.bottomDimView.isUserInteractionEnabled = false
|
self.bottomDimView.isUserInteractionEnabled = false
|
||||||
|
|
||||||
self.itemGroupsContainerNode = ActionSheetItemGroupsContainerNode(theme: self.theme)
|
self.itemGroupsContainerNode = ActionSheetItemGroupsContainerNode(theme: self.theme)
|
||||||
|
self.itemGroupsContainerNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
@ -128,6 +130,7 @@ final class ActionSheetControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.updateScrollDimViews(size: layout.size, insets: insets, transition: transition)
|
self.updateScrollDimViews(size: layout.size, insets: insets, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func animateIn(completion: @escaping () -> Void) {
|
func animateIn(completion: @escaping () -> Void) {
|
||||||
let tempDimView = UIView()
|
let tempDimView = UIView()
|
||||||
tempDimView.backgroundColor = self.theme.dimColor
|
tempDimView.backgroundColor = self.theme.dimColor
|
||||||
@ -144,6 +147,10 @@ final class ActionSheetControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
tempDimView?.removeFromSuperview()
|
tempDimView?.removeFromSuperview()
|
||||||
completion()
|
completion()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Queue.mainQueue().after(0.3, {
|
||||||
|
self.itemGroupsContainerNode.isUserInteractionEnabled = true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateOut(cancelled: Bool) {
|
func animateOut(cancelled: Bool) {
|
||||||
@ -170,7 +177,7 @@ final class ActionSheetControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func dimNodeTap(_ recognizer: UITapGestureRecognizer) {
|
@objc func dimNodeTap(_ recognizer: UITapGestureRecognizer) {
|
||||||
if case .ended = recognizer.state {
|
if case .ended = recognizer.state, self.itemGroupsContainerNode.isUserInteractionEnabled {
|
||||||
self.view.window?.endEditing(true)
|
self.view.window?.endEditing(true)
|
||||||
self.animateOut(cancelled: true)
|
self.animateOut(cancelled: true)
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,9 @@ final class ActionSheetItemGroupsContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
|
guard self.isUserInteractionEnabled else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
for groupNode in self.groupNodes {
|
for groupNode in self.groupNodes {
|
||||||
if groupNode.frame.contains(point) {
|
if groupNode.frame.contains(point) {
|
||||||
return groupNode.hitTest(self.convert(point, to: groupNode), with: event)
|
return groupNode.hitTest(self.convert(point, to: groupNode), with: event)
|
||||||
|
@ -151,7 +151,7 @@ public extension CALayer {
|
|||||||
let animationGroup = CAAnimationGroup()
|
let animationGroup = CAAnimationGroup()
|
||||||
var timeOffset = 0.0
|
var timeOffset = 0.0
|
||||||
for animation in animations {
|
for animation in animations {
|
||||||
animation.beginTime = animation.beginTime + timeOffset
|
animation.beginTime = self.convertTime(animation.beginTime, from: nil) + timeOffset
|
||||||
timeOffset += animation.duration / Double(animation.speed)
|
timeOffset += animation.duration / Double(animation.speed)
|
||||||
}
|
}
|
||||||
animationGroup.animations = animations
|
animationGroup.animations = animations
|
||||||
@ -217,7 +217,7 @@ public extension CALayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !delay.isZero {
|
if !delay.isZero {
|
||||||
animation.beginTime = CACurrentMediaTime() + delay * UIView.animationDurationFactor()
|
animation.beginTime = self.convertTime(CACurrentMediaTime(), from: nil) + delay * UIView.animationDurationFactor()
|
||||||
animation.fillMode = .both
|
animation.fillMode = .both
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1192,7 +1192,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
|||||||
shareController.actionCompleted = { [weak self] in
|
shareController.actionCompleted = { [weak self] in
|
||||||
if let strongSelf = self, let actionCompletionText = actionCompletionText {
|
if let strongSelf = self, let actionCompletionText = actionCompletionText {
|
||||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
strongSelf.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .mediaSaved(text: actionCompletionText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
strongSelf.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .mediaSaved(text: actionCompletionText), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return true }), nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
shareController.completed = { [weak self] peerIds in
|
shareController.completed = { [weak self] peerIds in
|
||||||
|
@ -2423,8 +2423,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
let url = content.url
|
let url = content.url
|
||||||
|
|
||||||
let item = OpenInItem.url(url: url)
|
let item = OpenInItem.url(url: url)
|
||||||
let canOpenIn = availableOpenInOptions(context: strongSelf.context, item: item).count > 1
|
let openText = strongSelf.presentationData.strings.Conversation_FileOpenIn
|
||||||
let openText = canOpenIn ? strongSelf.presentationData.strings.Conversation_FileOpenIn : strongSelf.presentationData.strings.Conversation_LinkDialogOpen
|
|
||||||
items.append(.action(ContextMenuActionItem(text: openText, textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
items.append(.action(ContextMenuActionItem(text: openText, textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||||
f(.default)
|
f(.default)
|
||||||
|
|
||||||
@ -2446,7 +2445,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (message, maybeFile, _) = strongSelf.contentInfo(), let file = maybeFile {
|
if let (message, maybeFile, _) = strongSelf.contentInfo(), let file = maybeFile, !message.isCopyProtected() {
|
||||||
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Gallery_SaveVideo, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Download"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in
|
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Gallery_SaveVideo, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Download"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in
|
||||||
f(.default)
|
f(.default)
|
||||||
|
|
||||||
|
@ -46,13 +46,13 @@ final class InstantPageMediaPlaylistItem: SharedMediaPlaylistItem {
|
|||||||
switch attribute {
|
switch attribute {
|
||||||
case let .Audio(isVoice, _, _, _, _):
|
case let .Audio(isVoice, _, _, _, _):
|
||||||
if isVoice {
|
if isVoice {
|
||||||
return SharedMediaPlaybackData(type: .voice, source: .telegramFile(.webPage(webPage: WebpageReference(self.webPage), media: file)))
|
return SharedMediaPlaybackData(type: .voice, source: .telegramFile(reference: .webPage(webPage: WebpageReference(self.webPage), media: file), isCopyProtected: false))
|
||||||
} else {
|
} else {
|
||||||
return SharedMediaPlaybackData(type: .music, source: .telegramFile(.webPage(webPage: WebpageReference(self.webPage), media: file)))
|
return SharedMediaPlaybackData(type: .music, source: .telegramFile(reference: .webPage(webPage: WebpageReference(self.webPage), media: file), isCopyProtected: false))
|
||||||
}
|
}
|
||||||
case let .Video(_, _, flags):
|
case let .Video(_, _, flags):
|
||||||
if flags.contains(.instantRoundVideo) {
|
if flags.contains(.instantRoundVideo) {
|
||||||
return SharedMediaPlaybackData(type: .instantVideo, source: .telegramFile(.webPage(webPage: WebpageReference(self.webPage), media: file)))
|
return SharedMediaPlaybackData(type: .instantVideo, source: .telegramFile(reference: .webPage(webPage: WebpageReference(self.webPage), media: file), isCopyProtected: false))
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -61,12 +61,12 @@ final class InstantPageMediaPlaylistItem: SharedMediaPlaylistItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if file.mimeType.hasPrefix("audio/") {
|
if file.mimeType.hasPrefix("audio/") {
|
||||||
return SharedMediaPlaybackData(type: .music, source: .telegramFile(.webPage(webPage: WebpageReference(self.webPage), media: file)))
|
return SharedMediaPlaybackData(type: .music, source: .telegramFile(reference: .webPage(webPage: WebpageReference(self.webPage), media: file), isCopyProtected: false))
|
||||||
}
|
}
|
||||||
if let fileName = file.fileName {
|
if let fileName = file.fileName {
|
||||||
let ext = (fileName as NSString).pathExtension.lowercased()
|
let ext = (fileName as NSString).pathExtension.lowercased()
|
||||||
if ext == "wav" || ext == "opus" {
|
if ext == "wav" || ext == "opus" {
|
||||||
return SharedMediaPlaybackData(type: .music, source: .telegramFile(.webPage(webPage: WebpageReference(self.webPage), media: file)))
|
return SharedMediaPlaybackData(type: .music, source: .telegramFile(reference: .webPage(webPage: WebpageReference(self.webPage), media: file), isCopyProtected: false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -449,6 +449,9 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
private let maskNode: ASImageNode
|
private let maskNode: ASImageNode
|
||||||
|
|
||||||
private let containerNode: ContextControllerSourceNode
|
private let containerNode: ContextControllerSourceNode
|
||||||
|
public override var controlsContainer: ASDisplayNode {
|
||||||
|
return self.containerNode
|
||||||
|
}
|
||||||
|
|
||||||
fileprivate let avatarNode: AvatarNode
|
fileprivate let avatarNode: AvatarNode
|
||||||
private let titleNode: TextNode
|
private let titleNode: TextNode
|
||||||
|
@ -151,6 +151,11 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
|||||||
private var disabledOverlayNode: ASDisplayNode?
|
private var disabledOverlayNode: ASDisplayNode?
|
||||||
private let maskNode: ASImageNode
|
private let maskNode: ASImageNode
|
||||||
|
|
||||||
|
private let containerNode: ASDisplayNode
|
||||||
|
override var controlsContainer: ASDisplayNode {
|
||||||
|
return self.containerNode
|
||||||
|
}
|
||||||
|
|
||||||
fileprivate let imageNode: TransformImageNode
|
fileprivate let imageNode: TransformImageNode
|
||||||
private var animationNode: AnimatedStickerNode?
|
private var animationNode: AnimatedStickerNode?
|
||||||
private var placeholderNode: StickerShimmerEffectNode?
|
private var placeholderNode: StickerShimmerEffectNode?
|
||||||
@ -203,6 +208,8 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
|||||||
self.bottomStripeNode = ASDisplayNode()
|
self.bottomStripeNode = ASDisplayNode()
|
||||||
self.bottomStripeNode.isLayerBacked = true
|
self.bottomStripeNode.isLayerBacked = true
|
||||||
|
|
||||||
|
self.containerNode = ASDisplayNode()
|
||||||
|
|
||||||
self.maskNode = ASImageNode()
|
self.maskNode = ASImageNode()
|
||||||
self.maskNode.isUserInteractionEnabled = false
|
self.maskNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
@ -245,17 +252,19 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||||
|
|
||||||
if let placeholderNode = self.placeholderNode {
|
self.addSubnode(self.containerNode)
|
||||||
self.addSubnode(placeholderNode)
|
|
||||||
}
|
|
||||||
self.addSubnode(self.imageNode)
|
|
||||||
|
|
||||||
self.addSubnode(self.titleNode)
|
if let placeholderNode = self.placeholderNode {
|
||||||
self.addSubnode(self.statusNode)
|
self.containerNode.addSubnode(placeholderNode)
|
||||||
self.addSubnode(self.unreadNode)
|
}
|
||||||
self.addSubnode(self.installationActionImageNode)
|
|
||||||
self.addSubnode(self.installationActionNode)
|
self.containerNode.addSubnode(self.imageNode)
|
||||||
self.addSubnode(self.selectionIconNode)
|
self.containerNode.addSubnode(self.titleNode)
|
||||||
|
self.containerNode.addSubnode(self.statusNode)
|
||||||
|
self.containerNode.addSubnode(self.unreadNode)
|
||||||
|
self.containerNode.addSubnode(self.installationActionImageNode)
|
||||||
|
self.containerNode.addSubnode(self.installationActionNode)
|
||||||
|
self.containerNode.addSubnode(self.selectionIconNode)
|
||||||
self.addSubnode(self.activateArea)
|
self.addSubnode(self.activateArea)
|
||||||
|
|
||||||
self.installationActionNode.addTarget(self, action: #selector(self.installationActionPressed), forControlEvents: .touchUpInside)
|
self.installationActionNode.addTarget(self, action: #selector(self.installationActionPressed), forControlEvents: .touchUpInside)
|
||||||
@ -689,6 +698,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
|||||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||||
|
|
||||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||||
|
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: strongSelf.backgroundNode.frame.size)
|
||||||
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||||
transition.updateFrame(node: strongSelf.topStripeNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)))
|
transition.updateFrame(node: strongSelf.topStripeNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)))
|
||||||
transition.updateFrame(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)))
|
transition.updateFrame(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)))
|
||||||
|
@ -301,7 +301,7 @@ public class ItemListVenueItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
strongSelf.topStripeNode.removeFromSupernode()
|
strongSelf.topStripeNode.removeFromSupernode()
|
||||||
}
|
}
|
||||||
if strongSelf.bottomStripeNode.supernode == nil {
|
if strongSelf.bottomStripeNode.supernode == nil {
|
||||||
strongSelf.addSubnode(strongSelf.bottomStripeNode)
|
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0)
|
||||||
}
|
}
|
||||||
if strongSelf.maskNode.supernode != nil {
|
if strongSelf.maskNode.supernode != nil {
|
||||||
strongSelf.maskNode.removeFromSupernode()
|
strongSelf.maskNode.removeFromSupernode()
|
||||||
|
@ -81,6 +81,7 @@
|
|||||||
- (void)animateOut:(BOOL)toSmallSize;
|
- (void)animateOut:(BOOL)toSmallSize;
|
||||||
- (void)addMicLevel:(CGFloat)level;
|
- (void)addMicLevel:(CGFloat)level;
|
||||||
- (void)dismiss;
|
- (void)dismiss;
|
||||||
|
- (void)reset;
|
||||||
|
|
||||||
- (void)updateOverlay;
|
- (void)updateOverlay;
|
||||||
|
|
||||||
|
@ -869,6 +869,11 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)reset {
|
||||||
|
_targetTranslation = 0.0;
|
||||||
|
[self updateOverlay];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)addMicLevel:(CGFloat)level {
|
- (void)addMicLevel:(CGFloat)level {
|
||||||
_inputLevel = level;
|
_inputLevel = level;
|
||||||
[_decoration updateLevel:level];
|
[_decoration updateLevel:level];
|
||||||
|
@ -34,7 +34,7 @@ typedef NS_CLOSED_ENUM(NSInteger, LottieMeshFillRule) {
|
|||||||
- (void)getVertexAt:(NSInteger)index x:(float * _Nullable)x y:(float * _Nullable)y;
|
- (void)getVertexAt:(NSInteger)index x:(float * _Nullable)x y:(float * _Nullable)y;
|
||||||
|
|
||||||
- (NSInteger)triangleCount;
|
- (NSInteger)triangleCount;
|
||||||
- (void)getTriangleAt:(NSInteger)index v0:(NSInteger * _Nullable)v0 v1:(NSInteger * _Nullable)v1 v2:(NSInteger * _Nullable)v2;
|
- (void * _Nonnull)getTriangles;
|
||||||
|
|
||||||
+ (LottieMeshData * _Nullable)generateWithPath:(UIBezierPath * _Nonnull)path fill:(LottieMeshFill * _Nullable)fill stroke:(LottieMeshStroke * _Nullable)stroke;
|
+ (LottieMeshData * _Nullable)generateWithPath:(UIBezierPath * _Nonnull)path fill:(LottieMeshFill * _Nullable)fill stroke:(LottieMeshStroke * _Nullable)stroke;
|
||||||
|
|
||||||
|
@ -10,12 +10,6 @@ MeshGenerator::Point bezierQuadraticPointAt(MeshGenerator::Point const &p0, Mesh
|
|||||||
return MeshGenerator::Point(x, y);
|
return MeshGenerator::Point(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
MeshGenerator::Point bezierQubicPointAt(MeshGenerator::Point const &p0, MeshGenerator::Point const &p1, MeshGenerator::Point const &p2, MeshGenerator::Point const &p3, float t) {
|
|
||||||
float x = powf((1.0 - t), 3.0) * p0.x + 3.0 * powf((1.0 - t), 2.0) * t * p1.x + 3.0 * (1.0 - t) * powf(t, 2.0) * p2.x + powf(t, 3.0) * p3.x;
|
|
||||||
float y = powf((1.0 - t), 3.0) * p0.y + 3.0 * powf((1.0 - t), 2.0) * t * p1.y + 3.0 * (1.0 - t) * powf(t, 2.0) * p2.y + powf(t, 3.0) * p3.y;
|
|
||||||
return MeshGenerator::Point(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
float approximateBezierQuadraticLength(MeshGenerator::Point const &p0, MeshGenerator::Point const &p1, MeshGenerator::Point const &p2) {
|
float approximateBezierQuadraticLength(MeshGenerator::Point const &p0, MeshGenerator::Point const &p1, MeshGenerator::Point const &p2) {
|
||||||
float length = 0.0f;
|
float length = 0.0f;
|
||||||
float t = 0.1;
|
float t = 0.1;
|
||||||
@ -29,17 +23,51 @@ float approximateBezierQuadraticLength(MeshGenerator::Point const &p0, MeshGener
|
|||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
float approximateBezierQubicLength(MeshGenerator::Point const &p0, MeshGenerator::Point const &p1, MeshGenerator::Point const &p2, MeshGenerator::Point const &p3) {
|
void tesselateBezier(MeshGenerator::Path &path, MeshGenerator::Point const &p1, MeshGenerator::Point const &p2, MeshGenerator::Point const &p3, MeshGenerator::Point const &p4, int level) {
|
||||||
float length = 0.0f;
|
const float tessTol = 0.25f / 0.5f;
|
||||||
float t = 0.1;
|
|
||||||
MeshGenerator::Point last = p0;
|
float x1 = p1.x;
|
||||||
while (t < 1.01) {
|
float y1 = p1.y;
|
||||||
auto point = bezierQubicPointAt(p0, p1, p2, p3, t);
|
float x2 = p2.x;
|
||||||
length += last.distance(point);
|
float y2 = p2.y;
|
||||||
last = point;
|
float x3 = p3.x;
|
||||||
t += 0.1;
|
float y3 = p3.y;
|
||||||
|
float x4 = p4.x;
|
||||||
|
float y4 = p4.y;
|
||||||
|
|
||||||
|
float x12, y12, x23, y23, x34, y34, x123, y123, x234, y234, x1234, y1234;
|
||||||
|
float dx, dy, d2, d3;
|
||||||
|
|
||||||
|
if (level > 10) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return length;
|
|
||||||
|
x12 = (x1 + x2) * 0.5f;
|
||||||
|
y12 = (y1 + y2) * 0.5f;
|
||||||
|
x23 = (x2 + x3) * 0.5f;
|
||||||
|
y23 = (y2 + y3) * 0.5f;
|
||||||
|
x34 = (x3 + x4) * 0.5f;
|
||||||
|
y34 = (y3 + y4) * 0.5f;
|
||||||
|
x123 = (x12 + x23) * 0.5f;
|
||||||
|
y123 = (y12 + y23) * 0.5f;
|
||||||
|
|
||||||
|
dx = x4 - x1;
|
||||||
|
dy = y4 - y1;
|
||||||
|
d2 = std::abs(((x2 - x4) * dy - (y2 - y4) * dx));
|
||||||
|
d3 = std::abs(((x3 - x4) * dy - (y3 - y4) * dx));
|
||||||
|
|
||||||
|
if ((d2 + d3) * (d2 + d3) < tessTol * (dx * dx + dy * dy)) {
|
||||||
|
path.points.emplace_back(x4, y4);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
x234 = (x23+x34) * 0.5f;
|
||||||
|
y234 = (y23+y34) * 0.5f;
|
||||||
|
x1234 = (x123 + x234) * 0.5f;
|
||||||
|
y1234 = (y123 + y234) * 0.5f;
|
||||||
|
|
||||||
|
tesselateBezier(path, MeshGenerator::Point(x1, y1), MeshGenerator::Point(x12, y12), MeshGenerator::Point(x123, y123), MeshGenerator::Point(x1234, y1234), level + 1);
|
||||||
|
tesselateBezier(path, MeshGenerator::Point(x1234, y1234), MeshGenerator::Point(x234, y234), MeshGenerator::Point(x34, y34), MeshGenerator::Point(x4, y4), level + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -80,7 +108,11 @@ float approximateBezierQubicLength(MeshGenerator::Point const &p0, MeshGenerator
|
|||||||
return (NSInteger)(_mesh->triangles.size() / 3);
|
return (NSInteger)(_mesh->triangles.size() / 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)getTriangleAt:(NSInteger)index v0:(NSInteger * _Nullable)v0 v1:(NSInteger * _Nullable)v1 v2:(NSInteger * _Nullable)v2 {
|
- (void * _Nonnull)getTriangles {
|
||||||
|
return _mesh->triangles.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*- (void)getTriangleAt:(NSInteger)index v0:(NSInteger * _Nullable)v0 v1:(NSInteger * _Nullable)v1 v2:(NSInteger * _Nullable)v2 {
|
||||||
if (v0) {
|
if (v0) {
|
||||||
*v0 = (NSInteger)_mesh->triangles[index * 3 + 0];
|
*v0 = (NSInteger)_mesh->triangles[index * 3 + 0];
|
||||||
}
|
}
|
||||||
@ -90,7 +122,7 @@ float approximateBezierQubicLength(MeshGenerator::Point const &p0, MeshGenerator
|
|||||||
if (v2) {
|
if (v2) {
|
||||||
*v2 = (NSInteger)_mesh->triangles[index * 3 + 2];
|
*v2 = (NSInteger)_mesh->triangles[index * 3 + 2];
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
+ (LottieMeshData * _Nullable)generateWithPath:(UIBezierPath * _Nonnull)path fill: (LottieMeshFill * _Nullable)fill stroke:(LottieMeshStroke * _Nullable)stroke {
|
+ (LottieMeshData * _Nullable)generateWithPath:(UIBezierPath * _Nonnull)path fill: (LottieMeshFill * _Nullable)fill stroke:(LottieMeshStroke * _Nullable)stroke {
|
||||||
float scale = 1.0f;
|
float scale = 1.0f;
|
||||||
@ -173,14 +205,8 @@ float approximateBezierQubicLength(MeshGenerator::Point const &p0, MeshGenerator
|
|||||||
MeshGenerator::Point p1(element->points[0].x * scale, element->points[0].y * scale);
|
MeshGenerator::Point p1(element->points[0].x * scale, element->points[0].y * scale);
|
||||||
MeshGenerator::Point p2(element->points[1].x * scale, element->points[1].y * scale);
|
MeshGenerator::Point p2(element->points[1].x * scale, element->points[1].y * scale);
|
||||||
MeshGenerator::Point p3(element->points[2].x * scale, element->points[2].y * scale);
|
MeshGenerator::Point p3(element->points[2].x * scale, element->points[2].y * scale);
|
||||||
|
|
||||||
float step = 10.0f * flatness / approximateBezierQubicLength(p0, p1, p2, p3);
|
tesselateBezier(paths[paths.size() - 1], p0, p1, p2, p3, 0);
|
||||||
while (t < 1.0f) {
|
|
||||||
auto point = bezierQubicPointAt(p0, p1, p2, p3, t);
|
|
||||||
paths[paths.size() - 1].points.push_back(point);
|
|
||||||
t += step;
|
|
||||||
}
|
|
||||||
paths[paths.size() - 1].points.push_back(p3);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -573,13 +573,13 @@ public final class MeshRenderer: MTKView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func generateSegments(geometry: CapturedGeometryNode, superAlpha: CGFloat, superTransform: CGAffineTransform, writeSegment: (MeshAnimation.Frame.Segment) -> Void) {
|
private func generateSegments(writeBuffer: MeshWriteBuffer, segmentCount: inout Int, geometry: CapturedGeometryNode, superAlpha: CGFloat, superTransform: CGAffineTransform) {
|
||||||
if geometry.isHidden || geometry.alpha.isZero {
|
if geometry.isHidden || geometry.alpha.isZero {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0 ..< geometry.subnodes.count {
|
for i in 0 ..< geometry.subnodes.count {
|
||||||
generateSegments(geometry: geometry.subnodes[i], superAlpha: superAlpha * geometry.alpha, superTransform: CATransform3DGetAffineTransform(geometry.transform).concatenating(superTransform), writeSegment: writeSegment)
|
generateSegments(writeBuffer: writeBuffer, segmentCount: &segmentCount, geometry: geometry.subnodes[i], superAlpha: superAlpha * geometry.alpha, superTransform: CATransform3DGetAffineTransform(geometry.transform).concatenating(superTransform))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let displayItem = geometry.displayItem {
|
if let displayItem = geometry.displayItem {
|
||||||
@ -617,17 +617,6 @@ private func generateSegments(geometry: CapturedGeometryNode, superAlpha: CGFloa
|
|||||||
meshData = LottieMeshData.generate(with: UIBezierPath(cgPath: displayItem.path), fill: nil, stroke: LottieMeshStroke(lineWidth: stroke.lineWidth, lineJoin: stroke.lineJoin, lineCap: stroke.lineCap, miterLimit: stroke.miterLimit))
|
meshData = LottieMeshData.generate(with: UIBezierPath(cgPath: displayItem.path), fill: nil, stroke: LottieMeshStroke(lineWidth: stroke.lineWidth, lineJoin: stroke.lineJoin, lineCap: stroke.lineCap, miterLimit: stroke.miterLimit))
|
||||||
}
|
}
|
||||||
if let meshData = meshData, meshData.triangleCount() != 0 {
|
if let meshData = meshData, meshData.triangleCount() != 0 {
|
||||||
let mappedTriangles = WriteBuffer()
|
|
||||||
for i in 0 ..< meshData.triangleCount() {
|
|
||||||
var v0: Int = 0
|
|
||||||
var v1: Int = 0
|
|
||||||
var v2: Int = 0
|
|
||||||
meshData.getTriangleAt(i, v0: &v0, v1: &v1, v2: &v2)
|
|
||||||
mappedTriangles.writeInt32(Int32(v0))
|
|
||||||
mappedTriangles.writeInt32(Int32(v1))
|
|
||||||
mappedTriangles.writeInt32(Int32(v2))
|
|
||||||
}
|
|
||||||
|
|
||||||
let mappedVertices = WriteBuffer()
|
let mappedVertices = WriteBuffer()
|
||||||
for i in 0 ..< meshData.vertexCount() {
|
for i in 0 ..< meshData.vertexCount() {
|
||||||
var x: Float = 0.0
|
var x: Float = 0.0
|
||||||
@ -636,13 +625,15 @@ private func generateSegments(geometry: CapturedGeometryNode, superAlpha: CGFloa
|
|||||||
mappedVertices.writeFloat(x)
|
mappedVertices.writeFloat(x)
|
||||||
mappedVertices.writeFloat(y)
|
mappedVertices.writeFloat(y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let trianglesData = Data(bytes: meshData.getTriangles(), count: meshData.triangleCount() * 3 * 4)
|
||||||
|
|
||||||
let verticesData = mappedVertices.makeData()
|
let verticesData = mappedVertices.makeData()
|
||||||
let trianglesData = mappedTriangles.makeData()
|
|
||||||
|
|
||||||
let segment = MeshAnimation.Frame.Segment(vertices: DataRange(data: verticesData, range: 0 ..< verticesData.count), triangles: DataRange(data: trianglesData, range: 0 ..< trianglesData.count), fill: triangleFill, transform: CATransform3DGetAffineTransform(geometry.transform).concatenating(superTransform))
|
let segment = MeshAnimation.Frame.Segment(vertices: DataRange(data: verticesData, range: 0 ..< verticesData.count), triangles: DataRange(data: trianglesData, range: 0 ..< trianglesData.count), fill: triangleFill, transform: CATransform3DGetAffineTransform(geometry.transform).concatenating(superTransform))
|
||||||
|
|
||||||
writeSegment(segment)
|
segment.write(buffer: writeBuffer)
|
||||||
|
segmentCount += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -666,9 +657,9 @@ public func generateMeshAnimation(data: Data) -> TempBoxFile? {
|
|||||||
|
|
||||||
for i in 0 ..< Int(animation.endFrame) {
|
for i in 0 ..< Int(animation.endFrame) {
|
||||||
container.setFrame(frame: CGFloat(i))
|
container.setFrame(frame: CGFloat(i))
|
||||||
#if DEBUG
|
//#if DEBUG
|
||||||
print("Frame \(i) / \(Int(animation.endFrame))")
|
print("Frame \(i) / \(Int(animation.endFrame))")
|
||||||
#endif
|
//#endif
|
||||||
|
|
||||||
let segmentCountOffset = writeBuffer.offset
|
let segmentCountOffset = writeBuffer.offset
|
||||||
writeBuffer.writeInt32(0)
|
writeBuffer.writeInt32(0)
|
||||||
@ -677,11 +668,7 @@ public func generateMeshAnimation(data: Data) -> TempBoxFile? {
|
|||||||
let geometry = container.captureGeometry()
|
let geometry = container.captureGeometry()
|
||||||
geometry.transform = CATransform3DMakeTranslation(256.0, 256.0, 0.0)
|
geometry.transform = CATransform3DMakeTranslation(256.0, 256.0, 0.0)
|
||||||
|
|
||||||
generateSegments(geometry: geometry, superAlpha: 1.0, superTransform: .identity, writeSegment: { segment in
|
generateSegments(writeBuffer: writeBuffer, segmentCount: &segmentCount, geometry: geometry, superAlpha: 1.0, superTransform: .identity)
|
||||||
segment.write(buffer: writeBuffer)
|
|
||||||
|
|
||||||
segmentCount += 1
|
|
||||||
})
|
|
||||||
|
|
||||||
let currentOffset = writeBuffer.offset
|
let currentOffset = writeBuffer.offset
|
||||||
writeBuffer.seek(offset: segmentCountOffset)
|
writeBuffer.seek(offset: segmentCountOffset)
|
||||||
|
@ -27,6 +27,7 @@ swift_library(
|
|||||||
"//submodules/LegacyComponents:LegacyComponents",
|
"//submodules/LegacyComponents:LegacyComponents",
|
||||||
"//submodules/LegacyMediaPickerUI:LegacyMediaPickerUI",
|
"//submodules/LegacyMediaPickerUI:LegacyMediaPickerUI",
|
||||||
"//submodules/SaveToCameraRoll:SaveToCameraRoll",
|
"//submodules/SaveToCameraRoll:SaveToCameraRoll",
|
||||||
|
"//submodules/UndoUI:UndoUI",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -105,6 +105,7 @@ final class AvatarGalleryItemFooterContentNode: GalleryFooterContentNode {
|
|||||||
var dateText: String?
|
var dateText: String?
|
||||||
var typeText: String?
|
var typeText: String?
|
||||||
var buttonText: String?
|
var buttonText: String?
|
||||||
|
var canShare = true
|
||||||
switch entry {
|
switch entry {
|
||||||
case let .image(_, _, _, videoRepresentations, peer, date, _, _, _, _):
|
case let .image(_, _, _, videoRepresentations, peer, date, _, _, _, _):
|
||||||
nameText = peer.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""
|
nameText = peer.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) ?? ""
|
||||||
@ -119,6 +120,10 @@ final class AvatarGalleryItemFooterContentNode: GalleryFooterContentNode {
|
|||||||
typeText = self.strings.ProfilePhoto_MainPhoto
|
typeText = self.strings.ProfilePhoto_MainPhoto
|
||||||
buttonText = self.strings.ProfilePhoto_SetMainPhoto
|
buttonText = self.strings.ProfilePhoto_SetMainPhoto
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let peer = peer {
|
||||||
|
canShare = !peer.isCopyProtectionEnabled
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -151,6 +156,8 @@ final class AvatarGalleryItemFooterContentNode: GalleryFooterContentNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.actionButton.isHidden = !canShare
|
||||||
|
|
||||||
switch content {
|
switch content {
|
||||||
case .info:
|
case .info:
|
||||||
self.nameNode.isHidden = false
|
self.nameNode.isHidden = false
|
||||||
|
@ -12,6 +12,7 @@ import ShareController
|
|||||||
import PhotoResources
|
import PhotoResources
|
||||||
import GalleryUI
|
import GalleryUI
|
||||||
import TelegramUniversalVideoContent
|
import TelegramUniversalVideoContent
|
||||||
|
import UndoUI
|
||||||
|
|
||||||
private struct PeerAvatarImageGalleryThumbnailItem: GalleryThumbnailItem {
|
private struct PeerAvatarImageGalleryThumbnailItem: GalleryThumbnailItem {
|
||||||
let account: Account
|
let account: Account
|
||||||
@ -185,13 +186,22 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
self.footerContentNode.share = { [weak self] interaction in
|
self.footerContentNode.share = { [weak self] interaction in
|
||||||
if let strongSelf = self, let entry = strongSelf.entry, !entry.representations.isEmpty {
|
if let strongSelf = self, let entry = strongSelf.entry, !entry.representations.isEmpty {
|
||||||
let subject: ShareControllerSubject
|
let subject: ShareControllerSubject
|
||||||
|
var actionCompletionText: String?
|
||||||
if let video = entry.videoRepresentations.last, let peerReference = PeerReference(peer) {
|
if let video = entry.videoRepresentations.last, let peerReference = PeerReference(peer) {
|
||||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])]))
|
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])]))
|
||||||
subject = .media(videoFileReference.abstract)
|
subject = .media(videoFileReference.abstract)
|
||||||
|
actionCompletionText = strongSelf.presentationData.strings.Gallery_VideoSaved
|
||||||
} else {
|
} else {
|
||||||
subject = .image(entry.representations)
|
subject = .image(entry.representations)
|
||||||
|
actionCompletionText = strongSelf.presentationData.strings.Gallery_ImageSaved
|
||||||
}
|
}
|
||||||
let shareController = ShareController(context: strongSelf.context, subject: subject, preferredAction: .saveToCameraRoll)
|
let shareController = ShareController(context: strongSelf.context, subject: subject, preferredAction: .saveToCameraRoll)
|
||||||
|
shareController.actionCompleted = { [weak self] in
|
||||||
|
if let strongSelf = self, let actionCompletionText = actionCompletionText {
|
||||||
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
interaction.presentController(UndoOverlayController(presentationData: presentationData, content: .mediaSaved(text: actionCompletionText), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return true }), nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
interaction.presentController(shareController, nil)
|
interaction.presentController(shareController, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -379,7 +379,9 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
} else if let animateOutToAnchorRect = animateOutToAnchorRect {
|
} else if let animateOutToAnchorRect = animateOutToAnchorRect {
|
||||||
let targetBackgroundFrame = self.calculateBackgroundFrame(containerSize: size, insets: insets, anchorRect: animateOutToAnchorRect, contentSize: CGSize(width: visibleContentWidth, height: contentHeight)).0
|
let targetBackgroundFrame = self.calculateBackgroundFrame(containerSize: size, insets: insets, anchorRect: animateOutToAnchorRect, contentSize: CGSize(width: visibleContentWidth, height: contentHeight)).0
|
||||||
|
|
||||||
self.layer.animatePosition(from: CGPoint(), to: CGPoint(x: targetBackgroundFrame.minX - backgroundFrame.minX, y: targetBackgroundFrame.minY - backgroundFrame.minY), duration: 0.2, removeOnCompletion: false, additive: true)
|
let offset = CGPoint(x: -(targetBackgroundFrame.minX - backgroundFrame.minX), y: -(targetBackgroundFrame.minY - backgroundFrame.minY))
|
||||||
|
self.position = CGPoint(x: self.position.x - offset.x, y: self.position.y - offset.y)
|
||||||
|
self.layer.animatePosition(from: offset, to: CGPoint(), duration: 0.2, removeOnCompletion: true, additive: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,6 +530,15 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
itemNode.layer.animateScale(from: 1.0, to: (targetSnapshotView.bounds.width * 0.5) / itemNode.bounds.width, duration: duration, removeOnCompletion: false)
|
itemNode.layer.animateScale(from: 1.0, to: (targetSnapshotView.bounds.width * 0.5) / itemNode.bounds.width, duration: duration, removeOnCompletion: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func willAnimateOutToReaction(value: String) {
|
||||||
|
for itemNode in self.itemNodes {
|
||||||
|
if itemNode.item.reaction.rawValue != value {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
itemNode.isExtracted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func animateOutToReaction(value: String, targetEmptyNode: ASDisplayNode, targetFilledNode: ASDisplayNode, hideNode: Bool, completion: @escaping () -> Void) {
|
public func animateOutToReaction(value: String, targetEmptyNode: ASDisplayNode, targetFilledNode: ASDisplayNode, hideNode: Bool, completion: @escaping () -> Void) {
|
||||||
for itemNode in self.itemNodes {
|
for itemNode in self.itemNodes {
|
||||||
if itemNode.item.reaction.rawValue != value {
|
if itemNode.item.reaction.rawValue != value {
|
||||||
|
@ -120,6 +120,11 @@ class LocalizationListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
private let activateArea: AccessibilityAreaNode
|
private let activateArea: AccessibilityAreaNode
|
||||||
|
|
||||||
|
private let containerNode: ASDisplayNode
|
||||||
|
override var controlsContainer: ASDisplayNode {
|
||||||
|
return self.containerNode
|
||||||
|
}
|
||||||
|
|
||||||
override var canBeSelected: Bool {
|
override var canBeSelected: Bool {
|
||||||
if self.editableControlNode != nil {
|
if self.editableControlNode != nil {
|
||||||
return false
|
return false
|
||||||
@ -144,6 +149,8 @@ class LocalizationListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
self.maskNode = ASImageNode()
|
self.maskNode = ASImageNode()
|
||||||
self.maskNode.isUserInteractionEnabled = false
|
self.maskNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
self.containerNode = ASDisplayNode()
|
||||||
|
|
||||||
self.iconNode = ASImageNode()
|
self.iconNode = ASImageNode()
|
||||||
self.iconNode.isLayerBacked = true
|
self.iconNode.isLayerBacked = true
|
||||||
self.iconNode.displayWithoutProcessing = true
|
self.iconNode.displayWithoutProcessing = true
|
||||||
@ -169,10 +176,12 @@ class LocalizationListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||||
|
|
||||||
self.addSubnode(self.iconNode)
|
self.addSubnode(self.containerNode)
|
||||||
self.addSubnode(self.activityNode)
|
|
||||||
self.addSubnode(self.titleNode)
|
self.containerNode.addSubnode(self.iconNode)
|
||||||
self.addSubnode(self.subtitleNode)
|
self.containerNode.addSubnode(self.activityNode)
|
||||||
|
self.containerNode.addSubnode(self.titleNode)
|
||||||
|
self.containerNode.addSubnode(self.subtitleNode)
|
||||||
|
|
||||||
self.addSubnode(self.activateArea)
|
self.addSubnode(self.activateArea)
|
||||||
}
|
}
|
||||||
@ -282,7 +291,7 @@ class LocalizationListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
|
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
|
||||||
}
|
}
|
||||||
if strongSelf.maskNode.supernode == nil {
|
if strongSelf.maskNode.supernode == nil {
|
||||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
strongSelf.addSubnode(strongSelf.maskNode)
|
||||||
}
|
}
|
||||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||||
var hasTopCorners = false
|
var hasTopCorners = false
|
||||||
@ -307,6 +316,7 @@ class LocalizationListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||||
|
|
||||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||||
|
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: strongSelf.backgroundNode.frame.size)
|
||||||
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight))
|
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight))
|
||||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight))
|
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight))
|
||||||
|
@ -757,7 +757,7 @@ public func recentSessionsController(context: AccountContext, activeSessionsCont
|
|||||||
pushControllerImpl?(AuthTransferScanScreen(context: context, activeSessionsContext: activeSessionsContext))
|
pushControllerImpl?(AuthTransferScanScreen(context: context, activeSessionsContext: activeSessionsContext))
|
||||||
})
|
})
|
||||||
}, openOtherAppsUrl: {
|
}, openOtherAppsUrl: {
|
||||||
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: "https://desktop.telegram.org", forceExternal: true, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {})
|
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: "https://getdesktop.telegram.org", forceExternal: true, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {})
|
||||||
}, setupAuthorizationTTL: {
|
}, setupAuthorizationTTL: {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let controller = ActionSheetController(presentationData: presentationData)
|
let controller = ActionSheetController(presentationData: presentationData)
|
||||||
@ -829,12 +829,12 @@ public func recentSessionsController(context: AccountContext, activeSessionsCont
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let emptyStateItem: ItemListControllerEmptyStateItem?
|
let emptyStateItem: ItemListControllerEmptyStateItem? = nil
|
||||||
if sessionsState.sessions.count == 1 && mode == .sessions {
|
// if sessionsState.sessions.count == 1 && mode == .sessions {
|
||||||
emptyStateItem = RecentSessionsEmptyStateItem(theme: presentationData.theme, strings: presentationData.strings)
|
// emptyStateItem = RecentSessionsEmptyStateItem(theme: presentationData.theme, strings: presentationData.strings)
|
||||||
} else {
|
// } else {
|
||||||
emptyStateItem = nil
|
// emptyStateItem = nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
let title: ItemListControllerTitle
|
let title: ItemListControllerTitle
|
||||||
let entries: [RecentSessionsEntry]
|
let entries: [RecentSessionsEntry]
|
||||||
|
@ -131,7 +131,7 @@ class RecentSessionsHeaderItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
let contentSize = CGSize(width: params.width, height: topInset + titleLayout.size.height + 85.0)
|
let contentSize = CGSize(width: params.width, height: topInset + titleLayout.size.height + 69.0)
|
||||||
let insets = itemListNeighborsGroupedInsets(neighbors, params)
|
let insets = itemListNeighborsGroupedInsets(neighbors, params)
|
||||||
|
|
||||||
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||||
@ -150,9 +150,9 @@ class RecentSessionsHeaderItemNode: ListViewItemNode {
|
|||||||
strongSelf.buttonNode.updateTheme(SolidRoundedButtonTheme(theme: item.theme))
|
strongSelf.buttonNode.updateTheme(SolidRoundedButtonTheme(theme: item.theme))
|
||||||
}
|
}
|
||||||
|
|
||||||
let buttonWidth = contentSize.width - 32.0
|
let buttonWidth = min(375, contentSize.width - params.leftInset - params.rightInset)
|
||||||
let buttonHeight = strongSelf.buttonNode.updateLayout(width: buttonWidth, transition: .immediate)
|
let buttonHeight = strongSelf.buttonNode.updateLayout(width: buttonWidth, transition: .immediate)
|
||||||
let buttonFrame = CGRect(x: 16.0, y: contentSize.height - buttonHeight - 12.0, width: buttonWidth, height: buttonHeight)
|
let buttonFrame = CGRect(x: floorToScreenPixels((params.width - buttonWidth) / 2.0), y: contentSize.height - buttonHeight + 4.0, width: buttonWidth, height: buttonHeight)
|
||||||
strongSelf.buttonNode.frame = buttonFrame
|
strongSelf.buttonNode.frame = buttonFrame
|
||||||
|
|
||||||
strongSelf.accessibilityLabel = attributedText.string
|
strongSelf.accessibilityLabel = attributedText.string
|
||||||
|
@ -1883,6 +1883,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}, presentController: { [weak self] controller, arguments in
|
}, presentController: { [weak self] controller, arguments in
|
||||||
self?.present(controller, in: .window(.root), with: arguments)
|
self?.present(controller, in: .window(.root), with: arguments)
|
||||||
|
}, presentControllerInCurrent: { [weak self] controller, arguments in
|
||||||
|
self?.present(controller, in: .current, with: arguments)
|
||||||
}, navigationController: { [weak self] in
|
}, navigationController: { [weak self] in
|
||||||
return self?.navigationController as? NavigationController
|
return self?.navigationController as? NavigationController
|
||||||
}, chatControllerNode: { [weak self] in
|
}, chatControllerNode: { [weak self] in
|
||||||
|
@ -79,6 +79,7 @@ public final class ChatControllerInteraction {
|
|||||||
let updateInputMode: ((ChatInputMode) -> ChatInputMode) -> Void
|
let updateInputMode: ((ChatInputMode) -> ChatInputMode) -> Void
|
||||||
let openMessageShareMenu: (MessageId) -> Void
|
let openMessageShareMenu: (MessageId) -> Void
|
||||||
let presentController: (ViewController, Any?) -> Void
|
let presentController: (ViewController, Any?) -> Void
|
||||||
|
let presentControllerInCurrent: (ViewController, Any?) -> Void
|
||||||
let navigationController: () -> NavigationController?
|
let navigationController: () -> NavigationController?
|
||||||
let chatControllerNode: () -> ASDisplayNode?
|
let chatControllerNode: () -> ASDisplayNode?
|
||||||
let presentGlobalOverlayController: (ViewController, Any?) -> Void
|
let presentGlobalOverlayController: (ViewController, Any?) -> Void
|
||||||
@ -174,6 +175,7 @@ public final class ChatControllerInteraction {
|
|||||||
updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void,
|
updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void,
|
||||||
openMessageShareMenu: @escaping (MessageId) -> Void,
|
openMessageShareMenu: @escaping (MessageId) -> Void,
|
||||||
presentController: @escaping (ViewController, Any?) -> Void,
|
presentController: @escaping (ViewController, Any?) -> Void,
|
||||||
|
presentControllerInCurrent: @escaping (ViewController, Any?) -> Void,
|
||||||
navigationController: @escaping () -> NavigationController?,
|
navigationController: @escaping () -> NavigationController?,
|
||||||
chatControllerNode: @escaping () -> ASDisplayNode?,
|
chatControllerNode: @escaping () -> ASDisplayNode?,
|
||||||
presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void,
|
presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void,
|
||||||
@ -255,6 +257,7 @@ public final class ChatControllerInteraction {
|
|||||||
self.updateInputMode = updateInputMode
|
self.updateInputMode = updateInputMode
|
||||||
self.openMessageShareMenu = openMessageShareMenu
|
self.openMessageShareMenu = openMessageShareMenu
|
||||||
self.presentController = presentController
|
self.presentController = presentController
|
||||||
|
self.presentControllerInCurrent = presentControllerInCurrent
|
||||||
self.navigationController = navigationController
|
self.navigationController = navigationController
|
||||||
self.chatControllerNode = chatControllerNode
|
self.chatControllerNode = chatControllerNode
|
||||||
self.presentGlobalOverlayController = presentGlobalOverlayController
|
self.presentGlobalOverlayController = presentGlobalOverlayController
|
||||||
@ -311,7 +314,7 @@ public final class ChatControllerInteraction {
|
|||||||
static var `default`: ChatControllerInteraction {
|
static var `default`: ChatControllerInteraction {
|
||||||
return ChatControllerInteraction(openMessage: { _, _ in
|
return ChatControllerInteraction(openMessage: { _, _ in
|
||||||
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, updateMessageReaction: { _ in }, activateMessagePinch: { _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _ in return false }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, updateMessageReaction: { _ in }, activateMessagePinch: { _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _ in return false }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||||
}, presentController: { _, _ in }, navigationController: {
|
}, presentController: { _, _ in }, presentControllerInCurrent: { _, _ in }, navigationController: {
|
||||||
return nil
|
return nil
|
||||||
}, chatControllerNode: {
|
}, chatControllerNode: {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1167,9 +1167,16 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var scrollAnimationCurve: ListViewAnimationCurve? = nil
|
var scrollAnimationCurve: ListViewAnimationCurve? = nil
|
||||||
if let strongSelf = self, strongSelf.appliedPlayingMessageId != currentlyPlayingMessageId, let currentlyPlayingMessageId = currentlyPlayingMessageId {
|
if let strongSelf = self, case .default = source {
|
||||||
updatedScrollPosition = .index(index: .message(currentlyPlayingMessageId), position: .center(.bottom), directionHint: .Down, animated: true, highlight: true)
|
if strongSelf.appliedPlayingMessageId != currentlyPlayingMessageId, let currentlyPlayingMessageId = currentlyPlayingMessageId {
|
||||||
scrollAnimationCurve = .Spring(duration: 0.4)
|
if isFirstTime {
|
||||||
|
} else if case let .peer(peerId) = chatLocation, currentlyPlayingMessageId.id.peerId != peerId {
|
||||||
|
} else {
|
||||||
|
updatedScrollPosition = .index(index: .message(currentlyPlayingMessageId), position: .center(.bottom), directionHint: .Up, animated: true, highlight: true)
|
||||||
|
scrollAnimationCurve = .Spring(duration: 0.4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isFirstTime = false
|
||||||
}
|
}
|
||||||
|
|
||||||
var disableAnimations = false
|
var disableAnimations = false
|
||||||
|
@ -786,7 +786,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
let _ = (saveToCameraRoll(context: context, postbox: context.account.postbox, mediaReference: mediaReference)
|
let _ = (saveToCameraRoll(context: context, postbox: context.account.postbox, mediaReference: mediaReference)
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
controllerInteraction.presentController(UndoOverlayController(presentationData: presentationData, content: .mediaSaved(text: isVideo ? presentationData.strings.Gallery_VideoSaved : presentationData.strings.Gallery_ImageSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
controllerInteraction.presentControllerInCurrent(UndoOverlayController(presentationData: presentationData, content: .mediaSaved(text: isVideo ? presentationData.strings.Gallery_VideoSaved : presentationData.strings.Gallery_ImageSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return true }), nil)
|
||||||
})
|
})
|
||||||
f(.default)
|
f(.default)
|
||||||
})))
|
})))
|
||||||
|
@ -1339,7 +1339,7 @@ final class ChatMediaInputNode: ChatInputNode {
|
|||||||
self.panelFocusTimer?.invalidate()
|
self.panelFocusTimer?.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateIsExpanded(_ isExpanded: Bool) {
|
private func updateIsFocused(_ isExpanded: Bool) {
|
||||||
guard self.panelIsFocused != isExpanded else {
|
guard self.panelIsFocused != isExpanded else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -2054,7 +2054,7 @@ final class ChatMediaInputNode: ChatInputNode {
|
|||||||
panelHeight = standardInputHeight
|
panelHeight = standardInputHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updateIsExpanded(isFocused)
|
self.updateIsFocused(isFocused)
|
||||||
|
|
||||||
if displaySearch {
|
if displaySearch {
|
||||||
if let searchContainerNode = self.searchContainerNode {
|
if let searchContainerNode = self.searchContainerNode {
|
||||||
@ -2490,12 +2490,14 @@ final class ChatMediaInputNode: ChatInputNode {
|
|||||||
|
|
||||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
if self.panelIsFocused {
|
if self.panelIsFocused {
|
||||||
let convertedPoint = CGPoint(x: max(0.0, point.y), y: point.x)
|
if point.y > -41.0 && point.y < 38.0 {
|
||||||
if let result = self.listView.hitTest(convertedPoint, with: event) {
|
let convertedPoint = CGPoint(x: max(0.0, point.y), y: point.x)
|
||||||
return result
|
if let result = self.listView.hitTest(convertedPoint, with: event) {
|
||||||
}
|
return result
|
||||||
if let result = self.gifListView.hitTest(convertedPoint, with: event) {
|
}
|
||||||
return result
|
if let result = self.gifListView.hitTest(convertedPoint, with: event) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let searchContainerNode = self.searchContainerNode {
|
if let searchContainerNode = self.searchContainerNode {
|
||||||
|
@ -782,15 +782,15 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
|
|
||||||
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
||||||
|
|
||||||
var needShareButton = false
|
var needsShareButton = false
|
||||||
if case .pinnedMessages = item.associatedData.subject {
|
if case .pinnedMessages = item.associatedData.subject {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
} else if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
|
} else if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
|
||||||
needShareButton = false
|
needsShareButton = false
|
||||||
} else if item.message.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) {
|
} else if item.message.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) {
|
||||||
for attribute in item.content.firstMessage.attributes {
|
for attribute in item.content.firstMessage.attributes {
|
||||||
if let _ = attribute as? SourceReferenceMessageAttribute {
|
if let _ = attribute as? SourceReferenceMessageAttribute {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -798,31 +798,35 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
if let peer = item.message.peers[item.message.id.peerId] {
|
if let peer = item.message.peers[item.message.id.peerId] {
|
||||||
if let channel = peer as? TelegramChannel {
|
if let channel = peer as? TelegramChannel {
|
||||||
if case .broadcast = channel.info {
|
if case .broadcast = channel.info {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !needShareButton, let author = item.message.author as? TelegramUser, let _ = author.botInfo, !item.message.media.isEmpty {
|
if !needsShareButton, let author = item.message.author as? TelegramUser, let _ = author.botInfo, !item.message.media.isEmpty {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
}
|
}
|
||||||
if !needShareButton {
|
if !needsShareButton {
|
||||||
loop: for media in item.message.media {
|
loop: for media in item.message.media {
|
||||||
if media is TelegramMediaGame || media is TelegramMediaInvoice {
|
if media is TelegramMediaGame || media is TelegramMediaInvoice {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
break loop
|
break loop
|
||||||
} else if let media = media as? TelegramMediaWebpage, case .Loaded = media.content {
|
} else if let media = media as? TelegramMediaWebpage, case .Loaded = media.content {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
loop: for media in item.message.media {
|
loop: for media in item.message.media {
|
||||||
if media is TelegramMediaAction {
|
if media is TelegramMediaAction {
|
||||||
needShareButton = false
|
needsShareButton = false
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if item.associatedData.isCopyProtectionEnabled {
|
||||||
|
needsShareButton = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var layoutInsets = UIEdgeInsets(top: mergedTop.merged ? layoutConstants.bubble.mergedSpacing : layoutConstants.bubble.defaultSpacing, left: 0.0, bottom: mergedBottom.merged ? layoutConstants.bubble.mergedSpacing : layoutConstants.bubble.defaultSpacing, right: 0.0)
|
var layoutInsets = UIEdgeInsets(top: mergedTop.merged ? layoutConstants.bubble.mergedSpacing : layoutConstants.bubble.defaultSpacing, left: 0.0, bottom: mergedBottom.merged ? layoutConstants.bubble.mergedSpacing : layoutConstants.bubble.defaultSpacing, right: 0.0)
|
||||||
@ -955,7 +959,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var updatedShareButtonNode: ChatMessageShareButton?
|
var updatedShareButtonNode: ChatMessageShareButton?
|
||||||
if needShareButton {
|
if needsShareButton {
|
||||||
if let currentShareButtonNode = currentShareButtonNode {
|
if let currentShareButtonNode = currentShareButtonNode {
|
||||||
updatedShareButtonNode = currentShareButtonNode
|
updatedShareButtonNode = currentShareButtonNode
|
||||||
} else {
|
} else {
|
||||||
|
@ -1118,6 +1118,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
tmpWidth = baseWidth
|
tmpWidth = baseWidth
|
||||||
if needsShareButton || isAd {
|
if needsShareButton || isAd {
|
||||||
tmpWidth -= 38.0
|
tmpWidth -= 38.0
|
||||||
|
} else {
|
||||||
|
tmpWidth -= 4.0
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tmpWidth = layoutConstants.bubble.maximumWidthFill.widthFor(baseWidth)
|
tmpWidth = layoutConstants.bubble.maximumWidthFill.widthFor(baseWidth)
|
||||||
|
@ -501,7 +501,11 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
|
|||||||
if self.peerId.namespace == Namespaces.Peer.Empty, case let .message(_, id, _, _, _) = self.messageReference?.content {
|
if self.peerId.namespace == Namespaces.Peer.Empty, case let .message(_, id, _, _, _) = self.messageReference?.content {
|
||||||
self.controllerInteraction.displayMessageTooltip(id, self.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, self.avatarNode.frame)
|
self.controllerInteraction.displayMessageTooltip(id, self.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, self.avatarNode.frame)
|
||||||
} else {
|
} else {
|
||||||
self.controllerInteraction.openPeer(self.peerId, .info, nil)
|
if let channel = self.peer as? TelegramChannel, case .broadcast = channel.info {
|
||||||
|
self.controllerInteraction.openPeer(self.peerId, .chat(textInputState: nil, subject: nil, peekData: nil), nil)
|
||||||
|
} else {
|
||||||
|
self.controllerInteraction.openPeer(self.peerId, .info, nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -306,16 +306,16 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
|
|
||||||
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
||||||
|
|
||||||
var needShareButton = false
|
var needsShareButton = false
|
||||||
if case .pinnedMessages = item.associatedData.subject {
|
if case .pinnedMessages = item.associatedData.subject {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
} else if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
|
} else if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
|
||||||
needShareButton = false
|
needsShareButton = false
|
||||||
}
|
}
|
||||||
else if item.message.id.peerId == item.context.account.peerId {
|
else if item.message.id.peerId == item.context.account.peerId {
|
||||||
for attribute in item.content.firstMessage.attributes {
|
for attribute in item.content.firstMessage.attributes {
|
||||||
if let _ = attribute as? SourceReferenceMessageAttribute {
|
if let _ = attribute as? SourceReferenceMessageAttribute {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -323,31 +323,35 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
if let peer = item.message.peers[item.message.id.peerId] {
|
if let peer = item.message.peers[item.message.id.peerId] {
|
||||||
if let channel = peer as? TelegramChannel {
|
if let channel = peer as? TelegramChannel {
|
||||||
if case .broadcast = channel.info {
|
if case .broadcast = channel.info {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !needShareButton, let author = item.message.author as? TelegramUser, let _ = author.botInfo, !item.message.media.isEmpty {
|
if !needsShareButton, let author = item.message.author as? TelegramUser, let _ = author.botInfo, !item.message.media.isEmpty {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
}
|
}
|
||||||
if !needShareButton {
|
if !needsShareButton {
|
||||||
loop: for media in item.message.media {
|
loop: for media in item.message.media {
|
||||||
if media is TelegramMediaGame || media is TelegramMediaInvoice {
|
if media is TelegramMediaGame || media is TelegramMediaInvoice {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
break loop
|
break loop
|
||||||
} else if let media = media as? TelegramMediaWebpage, case .Loaded = media.content {
|
} else if let media = media as? TelegramMediaWebpage, case .Loaded = media.content {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
loop: for media in item.message.media {
|
loop: for media in item.message.media {
|
||||||
if media is TelegramMediaAction {
|
if media is TelegramMediaAction {
|
||||||
needShareButton = false
|
needsShareButton = false
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if item.associatedData.isCopyProtectionEnabled {
|
||||||
|
needsShareButton = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var layoutInsets = layoutConstants.instantVideo.insets
|
var layoutInsets = layoutConstants.instantVideo.insets
|
||||||
@ -472,7 +476,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
}
|
}
|
||||||
|
|
||||||
var updatedShareButtonNode: ChatMessageShareButton?
|
var updatedShareButtonNode: ChatMessageShareButton?
|
||||||
if needShareButton {
|
if needsShareButton {
|
||||||
if currentShareButtonNode != nil {
|
if currentShareButtonNode != nil {
|
||||||
updatedShareButtonNode = currentShareButtonNode
|
updatedShareButtonNode = currentShareButtonNode
|
||||||
} else {
|
} else {
|
||||||
|
@ -970,7 +970,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let updateImageSignal = updateImageSignal {
|
if let updateImageSignal = updateImageSignal {
|
||||||
strongSelf.imageNode.captureProtected = message.id.peerId.namespace == Namespaces.Peer.SecretChat
|
strongSelf.imageNode.captureProtected = message.id.peerId.namespace == Namespaces.Peer.SecretChat || message.isCopyProtected()
|
||||||
strongSelf.imageNode.setSignal(updateImageSignal(synchronousLoads, false), attemptSynchronously: synchronousLoads)
|
strongSelf.imageNode.setSignal(updateImageSignal(synchronousLoads, false), attemptSynchronously: synchronousLoads)
|
||||||
|
|
||||||
var imageDimensions: CGSize?
|
var imageDimensions: CGSize?
|
||||||
|
@ -86,7 +86,7 @@ struct ChatMessageItemLayoutConstants {
|
|||||||
fileprivate static var compact: ChatMessageItemLayoutConstants {
|
fileprivate static var compact: ChatMessageItemLayoutConstants {
|
||||||
let bubble = ChatMessageItemBubbleLayoutConstants(edgeInset: 4.0, defaultSpacing: 2.0 + UIScreenPixel, mergedSpacing: 1.0, maximumWidthFill: ChatMessageItemWidthFill(compactInset: 36.0, compactWidthBoundary: 500.0, freeMaximumFillFactor: 0.85), minimumSize: CGSize(width: 40.0, height: 35.0), contentInsets: UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 0.0), borderInset: UIScreenPixel, strokeInsets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0))
|
let bubble = ChatMessageItemBubbleLayoutConstants(edgeInset: 4.0, defaultSpacing: 2.0 + UIScreenPixel, mergedSpacing: 1.0, maximumWidthFill: ChatMessageItemWidthFill(compactInset: 36.0, compactWidthBoundary: 500.0, freeMaximumFillFactor: 0.85), minimumSize: CGSize(width: 40.0, height: 35.0), contentInsets: UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 0.0), borderInset: UIScreenPixel, strokeInsets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0))
|
||||||
let text = ChatMessageItemTextLayoutConstants(bubbleInsets: UIEdgeInsets(top: 6.0 + UIScreenPixel, left: 12.0, bottom: 6.0 - UIScreenPixel, right: 12.0))
|
let text = ChatMessageItemTextLayoutConstants(bubbleInsets: UIEdgeInsets(top: 6.0 + UIScreenPixel, left: 12.0, bottom: 6.0 - UIScreenPixel, right: 12.0))
|
||||||
let image = ChatMessageItemImageLayoutConstants(bubbleInsets: UIEdgeInsets(top: 2.0, left: 2.0, bottom: 2.0, right: 2.0), statusInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 6.0, right: 6.0), defaultCornerRadius: 16.0, mergedCornerRadius: 8.0, contentMergedCornerRadius: 0.0, maxDimensions: CGSize(width: 300.0, height: 300.0), minDimensions: CGSize(width: 170.0, height: 74.0))
|
let image = ChatMessageItemImageLayoutConstants(bubbleInsets: UIEdgeInsets(top: 2.0, left: 2.0, bottom: 2.0, right: 2.0), statusInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 6.0, right: 6.0), defaultCornerRadius: 16.0, mergedCornerRadius: 8.0, contentMergedCornerRadius: 0.0, maxDimensions: CGSize(width: 300.0, height: 380.0), minDimensions: CGSize(width: 170.0, height: 74.0))
|
||||||
let video = ChatMessageItemVideoLayoutConstants(maxHorizontalHeight: 250.0, maxVerticalHeight: 360.0)
|
let video = ChatMessageItemVideoLayoutConstants(maxHorizontalHeight: 250.0, maxVerticalHeight: 360.0)
|
||||||
let file = ChatMessageItemFileLayoutConstants(bubbleInsets: UIEdgeInsets(top: 15.0, left: 9.0, bottom: 15.0, right: 12.0))
|
let file = ChatMessageItemFileLayoutConstants(bubbleInsets: UIEdgeInsets(top: 15.0, left: 9.0, bottom: 15.0, right: 12.0))
|
||||||
let instantVideo = ChatMessageItemInstantVideoConstants(insets: UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0), dimensions: CGSize(width: 212.0, height: 212.0))
|
let instantVideo = ChatMessageItemInstantVideoConstants(insets: UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0), dimensions: CGSize(width: 212.0, height: 212.0))
|
||||||
|
@ -380,15 +380,15 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
|
|
||||||
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
||||||
|
|
||||||
var needShareButton = false
|
var needsShareButton = false
|
||||||
if case .pinnedMessages = item.associatedData.subject {
|
if case .pinnedMessages = item.associatedData.subject {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
} else if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
|
} else if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
|
||||||
needShareButton = false
|
needsShareButton = false
|
||||||
} else if item.message.id.peerId == item.context.account.peerId {
|
} else if item.message.id.peerId == item.context.account.peerId {
|
||||||
for attribute in item.content.firstMessage.attributes {
|
for attribute in item.content.firstMessage.attributes {
|
||||||
if let _ = attribute as? SourceReferenceMessageAttribute {
|
if let _ = attribute as? SourceReferenceMessageAttribute {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -396,31 +396,35 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
if let peer = item.message.peers[item.message.id.peerId] {
|
if let peer = item.message.peers[item.message.id.peerId] {
|
||||||
if let channel = peer as? TelegramChannel {
|
if let channel = peer as? TelegramChannel {
|
||||||
if case .broadcast = channel.info {
|
if case .broadcast = channel.info {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !needShareButton, let author = item.message.author as? TelegramUser, let _ = author.botInfo, !item.message.media.isEmpty {
|
if !needsShareButton, let author = item.message.author as? TelegramUser, let _ = author.botInfo, !item.message.media.isEmpty {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
}
|
}
|
||||||
if !needShareButton {
|
if !needsShareButton {
|
||||||
loop: for media in item.message.media {
|
loop: for media in item.message.media {
|
||||||
if media is TelegramMediaGame || media is TelegramMediaInvoice {
|
if media is TelegramMediaGame || media is TelegramMediaInvoice {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
break loop
|
break loop
|
||||||
} else if let media = media as? TelegramMediaWebpage, case .Loaded = media.content {
|
} else if let media = media as? TelegramMediaWebpage, case .Loaded = media.content {
|
||||||
needShareButton = true
|
needsShareButton = true
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
loop: for media in item.message.media {
|
loop: for media in item.message.media {
|
||||||
if media is TelegramMediaAction {
|
if media is TelegramMediaAction {
|
||||||
needShareButton = false
|
needsShareButton = false
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if item.associatedData.isCopyProtectionEnabled {
|
||||||
|
needsShareButton = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var layoutInsets = UIEdgeInsets(top: mergedTop.merged ? layoutConstants.bubble.mergedSpacing : layoutConstants.bubble.defaultSpacing, left: 0.0, bottom: mergedBottom.merged ? layoutConstants.bubble.mergedSpacing : layoutConstants.bubble.defaultSpacing, right: 0.0)
|
var layoutInsets = UIEdgeInsets(top: mergedTop.merged ? layoutConstants.bubble.mergedSpacing : layoutConstants.bubble.defaultSpacing, left: 0.0, bottom: mergedBottom.merged ? layoutConstants.bubble.mergedSpacing : layoutConstants.bubble.defaultSpacing, right: 0.0)
|
||||||
@ -553,7 +557,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var updatedShareButtonNode: ChatMessageShareButton?
|
var updatedShareButtonNode: ChatMessageShareButton?
|
||||||
if needShareButton {
|
if needsShareButton {
|
||||||
if let currentShareButtonNode = currentShareButtonNode {
|
if let currentShareButtonNode = currentShareButtonNode {
|
||||||
updatedShareButtonNode = currentShareButtonNode
|
updatedShareButtonNode = currentShareButtonNode
|
||||||
} else {
|
} else {
|
||||||
|
@ -295,6 +295,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
}))
|
}))
|
||||||
}, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
}, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||||
}, presentController: { _, _ in
|
}, presentController: { _, _ in
|
||||||
|
}, presentControllerInCurrent: { _, _ in
|
||||||
}, navigationController: { [weak self] in
|
}, navigationController: { [weak self] in
|
||||||
return self?.getNavigationController()
|
return self?.getNavigationController()
|
||||||
}, chatControllerNode: { [weak self] in
|
}, chatControllerNode: { [weak self] in
|
||||||
|
@ -98,6 +98,8 @@ private final class ChatTextInputMediaRecordingButtonPresenter : NSObject, TGMod
|
|||||||
private let presentController: (ViewController) -> Void
|
private let presentController: (ViewController) -> Void
|
||||||
let container: ChatTextInputMediaRecordingButtonPresenterContainer
|
let container: ChatTextInputMediaRecordingButtonPresenterContainer
|
||||||
private var presentationController: ChatTextInputMediaRecordingButtonPresenterController?
|
private var presentationController: ChatTextInputMediaRecordingButtonPresenterController?
|
||||||
|
private var timer: SwiftSignalKit.Timer?
|
||||||
|
fileprivate weak var button: ChatTextInputMediaRecordingButton?
|
||||||
|
|
||||||
init(account: Account, presentController: @escaping (ViewController) -> Void) {
|
init(account: Account, presentController: @escaping (ViewController) -> Void) {
|
||||||
self.account = account
|
self.account = account
|
||||||
@ -111,6 +113,7 @@ private final class ChatTextInputMediaRecordingButtonPresenter : NSObject, TGMod
|
|||||||
presentationController.presentingViewController?.dismiss(animated: false, completion: {})
|
presentationController.presentingViewController?.dismiss(animated: false, completion: {})
|
||||||
self.presentationController = nil
|
self.presentationController = nil
|
||||||
}
|
}
|
||||||
|
self.timer?.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func view() -> UIView! {
|
func view() -> UIView! {
|
||||||
@ -124,6 +127,14 @@ private final class ChatTextInputMediaRecordingButtonPresenter : NSObject, TGMod
|
|||||||
func present() {
|
func present() {
|
||||||
if let keyboardWindow = LegacyComponentsGlobals.provider().applicationKeyboardWindow(), !keyboardWindow.isHidden {
|
if let keyboardWindow = LegacyComponentsGlobals.provider().applicationKeyboardWindow(), !keyboardWindow.isHidden {
|
||||||
keyboardWindow.addSubview(self.container)
|
keyboardWindow.addSubview(self.container)
|
||||||
|
|
||||||
|
self.timer = SwiftSignalKit.Timer(timeout: 0.05, repeat: true, completion: { [weak self] in
|
||||||
|
if let keyboardWindow = LegacyComponentsGlobals.provider().applicationKeyboardWindow(), !keyboardWindow.isHidden {
|
||||||
|
} else {
|
||||||
|
self?.present()
|
||||||
|
}
|
||||||
|
}, queue: Queue.mainQueue())
|
||||||
|
self.timer?.start()
|
||||||
} else {
|
} else {
|
||||||
var presentNow = false
|
var presentNow = false
|
||||||
if self.presentationController == nil {
|
if self.presentationController == nil {
|
||||||
@ -137,10 +148,16 @@ private final class ChatTextInputMediaRecordingButtonPresenter : NSObject, TGMod
|
|||||||
if let presentationController = self.presentationController, presentNow {
|
if let presentationController = self.presentationController, presentNow {
|
||||||
self.presentController(presentationController)
|
self.presentController(presentationController)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let timer = self.timer {
|
||||||
|
self.button?.reset()
|
||||||
|
timer.invalidate()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func dismiss() {
|
func dismiss() {
|
||||||
|
self.timer?.invalidate()
|
||||||
self.container.removeFromSuperview()
|
self.container.removeFromSuperview()
|
||||||
if let presentationController = self.presentationController {
|
if let presentationController = self.presentationController {
|
||||||
presentationController.presentingViewController?.dismiss(animated: false, completion: {})
|
presentationController.presentingViewController?.dismiss(animated: false, completion: {})
|
||||||
@ -171,7 +188,7 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
|
|||||||
|
|
||||||
private var recordingOverlay: ChatTextInputAudioRecordingOverlay?
|
private var recordingOverlay: ChatTextInputAudioRecordingOverlay?
|
||||||
private var startTouchLocation: CGPoint?
|
private var startTouchLocation: CGPoint?
|
||||||
private(set) var controlsOffset: CGFloat = 0.0
|
fileprivate var controlsOffset: CGFloat = 0.0
|
||||||
private(set) var cancelTranslation: CGFloat = 0.0
|
private(set) var cancelTranslation: CGFloat = 0.0
|
||||||
|
|
||||||
private var micLevelDisposable: MetaDisposable?
|
private var micLevelDisposable: MetaDisposable?
|
||||||
@ -426,6 +443,7 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto
|
|||||||
|
|
||||||
func micButtonPresenter() -> TGModernConversationInputMicButtonPresentation! {
|
func micButtonPresenter() -> TGModernConversationInputMicButtonPresentation! {
|
||||||
let presenter = ChatTextInputMediaRecordingButtonPresenter(account: self.account!, presentController: self.presentController)
|
let presenter = ChatTextInputMediaRecordingButtonPresenter(account: self.account!, presentController: self.presentController)
|
||||||
|
presenter.button = self
|
||||||
self.currentPresenter = presenter.view()
|
self.currentPresenter = presenter.view()
|
||||||
return presenter
|
return presenter
|
||||||
}
|
}
|
||||||
|
@ -1115,7 +1115,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
|||||||
var hasMenuButton = false
|
var hasMenuButton = false
|
||||||
var menuButtonExpanded = false
|
var menuButtonExpanded = false
|
||||||
var isSendAsButton = false
|
var isSendAsButton = false
|
||||||
if let sendAsPeers = interfaceState.sendAsPeers, !sendAsPeers.isEmpty {
|
if let sendAsPeers = interfaceState.sendAsPeers, !sendAsPeers.isEmpty && interfaceState.editMessageState == nil {
|
||||||
hasMenuButton = true
|
hasMenuButton = true
|
||||||
menuButtonExpanded = false
|
menuButtonExpanded = false
|
||||||
isSendAsButton = true
|
isSendAsButton = true
|
||||||
|
@ -111,14 +111,16 @@ final class DocumentPreviewController: UINavigationController, QLPreviewControll
|
|||||||
final class CompactDocumentPreviewController: QLPreviewController, QLPreviewControllerDelegate, QLPreviewControllerDataSource {
|
final class CompactDocumentPreviewController: QLPreviewController, QLPreviewControllerDelegate, QLPreviewControllerDataSource {
|
||||||
private let postbox: Postbox
|
private let postbox: Postbox
|
||||||
private let file: TelegramMediaFile
|
private let file: TelegramMediaFile
|
||||||
|
private let canShare: Bool
|
||||||
|
|
||||||
private var item: DocumentPreviewItem?
|
private var item: DocumentPreviewItem?
|
||||||
|
|
||||||
private var tempFile: TempBoxFile?
|
private var tempFile: TempBoxFile?
|
||||||
|
|
||||||
init(theme: PresentationTheme, strings: PresentationStrings, postbox: Postbox, file: TelegramMediaFile) {
|
init(theme: PresentationTheme, strings: PresentationStrings, postbox: Postbox, file: TelegramMediaFile, canShare: Bool = true) {
|
||||||
self.postbox = postbox
|
self.postbox = postbox
|
||||||
self.file = file
|
self.file = file
|
||||||
|
self.canShare = canShare
|
||||||
|
|
||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
|
||||||
@ -144,6 +146,7 @@ final class CompactDocumentPreviewController: QLPreviewController, QLPreviewCont
|
|||||||
if let tempFile = self.tempFile {
|
if let tempFile = self.tempFile {
|
||||||
TempBox.shared.dispose(tempFile)
|
TempBox.shared.dispose(tempFile)
|
||||||
}
|
}
|
||||||
|
self.timer?.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func cancelPressed() {
|
@objc private func cancelPressed() {
|
||||||
@ -174,9 +177,51 @@ final class CompactDocumentPreviewController: QLPreviewController, QLPreviewCont
|
|||||||
func previewControllerDidDismiss(_ controller: QLPreviewController) {
|
func previewControllerDidDismiss(_ controller: QLPreviewController) {
|
||||||
//self.cancelPressed()
|
//self.cancelPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var toolbars: [UIView] = []
|
||||||
|
private var observations : [NSKeyValueObservation] = []
|
||||||
|
|
||||||
|
private var initialized = false
|
||||||
|
private var timer: SwiftSignalKit.Timer?
|
||||||
|
override func viewDidLayoutSubviews() {
|
||||||
|
super.viewDidLayoutSubviews()
|
||||||
|
|
||||||
|
if !self.canShare && !self.initialized {
|
||||||
|
self.initialized = true
|
||||||
|
|
||||||
|
self.timer = SwiftSignalKit.Timer(timeout: 0.01, repeat: true, completion: { [weak self] in
|
||||||
|
self?.tick()
|
||||||
|
}, queue: Queue.mainQueue())
|
||||||
|
self.timer?.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func tick() {
|
||||||
|
self.navigationItem.rightBarButtonItems?[0] = UIBarButtonItem()
|
||||||
|
self.navigationItem.setRightBarButton(UIBarButtonItem(), animated: false)
|
||||||
|
|
||||||
|
self.navigationController?.toolbar.isHidden = true
|
||||||
|
|
||||||
|
self.toolbars = self.toolbarsInSubviews(forView: self.view)
|
||||||
|
|
||||||
|
for toolbar in self.toolbars {
|
||||||
|
toolbar.isHidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func toolbarsInSubviews(forView view: UIView) -> [UIView] {
|
||||||
|
var toolbars: [UIView] = []
|
||||||
|
for subview in view.subviews {
|
||||||
|
if subview is UIToolbar {
|
||||||
|
toolbars.append(subview)
|
||||||
|
}
|
||||||
|
toolbars.append(contentsOf: toolbarsInSubviews(forView: subview))
|
||||||
|
}
|
||||||
|
return toolbars
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func presentDocumentPreviewController(rootController: UIViewController, theme: PresentationTheme, strings: PresentationStrings, postbox: Postbox, file: TelegramMediaFile) {
|
func presentDocumentPreviewController(rootController: UIViewController, theme: PresentationTheme, strings: PresentationStrings, postbox: Postbox, file: TelegramMediaFile, canShare: Bool) {
|
||||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
||||||
let navigationBar = UINavigationBar.appearance(whenContainedInInstancesOf: [QLPreviewController.self])
|
let navigationBar = UINavigationBar.appearance(whenContainedInInstancesOf: [QLPreviewController.self])
|
||||||
navigationBar.barTintColor = theme.rootController.navigationBar.opaqueBackgroundColor
|
navigationBar.barTintColor = theme.rootController.navigationBar.opaqueBackgroundColor
|
||||||
@ -194,5 +239,5 @@ func presentDocumentPreviewController(rootController: UIViewController, theme: P
|
|||||||
navigationBar.titleTextAttributes = [NSAttributedString.Key.font: Font.semibold(17.0), NSAttributedString.Key.foregroundColor: theme.rootController.navigationBar.primaryTextColor]
|
navigationBar.titleTextAttributes = [NSAttributedString.Key.font: Font.semibold(17.0), NSAttributedString.Key.foregroundColor: theme.rootController.navigationBar.primaryTextColor]
|
||||||
}
|
}
|
||||||
|
|
||||||
rootController.present(CompactDocumentPreviewController(theme: theme, strings: strings, postbox: postbox, file: file), animated: true, completion: nil)
|
rootController.present(CompactDocumentPreviewController(theme: theme, strings: strings, postbox: postbox, file: file, canShare: canShare), animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,9 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
|||||||
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, updateMessageReaction: { _ in }, activateMessagePinch: { _ in
|
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, updateMessageReaction: { _ in }, activateMessagePinch: { _ in
|
||||||
}, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
|
}, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
|
||||||
}, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { fileReference, _, _, _, _, node, rect in return selectStickerImpl?(fileReference, node, rect) ?? false }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
}, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { fileReference, _, _, _, _, node, rect in return selectStickerImpl?(fileReference, node, rect) ?? false }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||||
}, presentController: { _, _ in }, navigationController: {
|
}, presentController: { _, _ in
|
||||||
|
}, presentControllerInCurrent: { _, _ in
|
||||||
|
}, navigationController: {
|
||||||
return nil
|
return nil
|
||||||
}, chatControllerNode: {
|
}, chatControllerNode: {
|
||||||
return nil
|
return nil
|
||||||
|
@ -114,7 +114,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
|||||||
params.present(controller, nil)
|
params.present(controller, nil)
|
||||||
} else if let rootController = params.navigationController?.view.window?.rootViewController {
|
} else if let rootController = params.navigationController?.view.window?.rootViewController {
|
||||||
let proceed = {
|
let proceed = {
|
||||||
presentDocumentPreviewController(rootController: rootController, theme: presentationData.theme, strings: presentationData.strings, postbox: params.context.account.postbox, file: file)
|
presentDocumentPreviewController(rootController: rootController, theme: presentationData.theme, strings: presentationData.strings, postbox: params.context.account.postbox, file: file, canShare: !params.message.isCopyProtected())
|
||||||
}
|
}
|
||||||
if file.mimeType.contains("image/svg") {
|
if file.mimeType.contains("image/svg") {
|
||||||
let presentationData = params.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = params.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
@ -99,6 +99,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
|||||||
}, updateInputMode: { _ in
|
}, updateInputMode: { _ in
|
||||||
}, openMessageShareMenu: { _ in
|
}, openMessageShareMenu: { _ in
|
||||||
}, presentController: { _, _ in
|
}, presentController: { _, _ in
|
||||||
|
}, presentControllerInCurrent: { _, _ in
|
||||||
}, navigationController: {
|
}, navigationController: {
|
||||||
return nil
|
return nil
|
||||||
}, chatControllerNode: {
|
}, chatControllerNode: {
|
||||||
|
@ -314,7 +314,6 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var rateButtonIsHidden = true
|
var rateButtonIsHidden = true
|
||||||
strongSelf.shareNode.isHidden = false
|
|
||||||
var displayData: SharedMediaPlaybackDisplayData?
|
var displayData: SharedMediaPlaybackDisplayData?
|
||||||
if let (_, valueOrLoading, _) = value, case let .state(value) = valueOrLoading {
|
if let (_, valueOrLoading, _) = value, case let .state(value) = valueOrLoading {
|
||||||
var isPaused: Bool
|
var isPaused: Bool
|
||||||
@ -393,10 +392,12 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||||||
|
|
||||||
if strongSelf.displayData != displayData {
|
if strongSelf.displayData != displayData {
|
||||||
strongSelf.displayData = displayData
|
strongSelf.displayData = displayData
|
||||||
|
|
||||||
|
var canShare = true
|
||||||
if let (_, valueOrLoading, _) = value, case let .state(value) = valueOrLoading, let source = value.item.playbackData?.source {
|
if let (_, valueOrLoading, _) = value, case let .state(value) = valueOrLoading, let source = value.item.playbackData?.source {
|
||||||
switch source {
|
switch source {
|
||||||
case let .telegramFile(fileReference):
|
case let .telegramFile(fileReference, isCopyProtected):
|
||||||
|
canShare = !isCopyProtected
|
||||||
strongSelf.currentFileReference = fileReference
|
strongSelf.currentFileReference = fileReference
|
||||||
if let size = fileReference.media.size {
|
if let size = fileReference.media.size {
|
||||||
strongSelf.scrubberNode.bufferingStatus = strongSelf.postbox.mediaBox.resourceRangesStatus(fileReference.media.resource)
|
strongSelf.scrubberNode.bufferingStatus = strongSelf.postbox.mediaBox.resourceRangesStatus(fileReference.media.resource)
|
||||||
@ -411,6 +412,8 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||||||
strongSelf.scrubberNode.bufferingStatus = nil
|
strongSelf.scrubberNode.bufferingStatus = nil
|
||||||
}
|
}
|
||||||
strongSelf.updateLabels(transition: .immediate)
|
strongSelf.updateLabels(transition: .immediate)
|
||||||
|
|
||||||
|
strongSelf.shareNode.isHidden = !canShare
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -2039,6 +2039,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}, openMessageShareMenu: { _ in
|
}, openMessageShareMenu: { _ in
|
||||||
}, presentController: { [weak self] c, a in
|
}, presentController: { [weak self] c, a in
|
||||||
self?.controller?.present(c, in: .window(.root), with: a)
|
self?.controller?.present(c, in: .window(.root), with: a)
|
||||||
|
}, presentControllerInCurrent: { [weak self] c, a in
|
||||||
|
self?.controller?.present(c, in: .current, with: a)
|
||||||
}, navigationController: { [weak self] in
|
}, navigationController: { [weak self] in
|
||||||
return self?.controller?.navigationController as? NavigationController
|
return self?.controller?.navigationController as? NavigationController
|
||||||
}, chatControllerNode: {
|
}, chatControllerNode: {
|
||||||
@ -6115,7 +6117,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
var testView: UIView? = localResult
|
var testView: UIView? = localResult
|
||||||
while true {
|
while true {
|
||||||
if let testViewValue = testView {
|
if let testViewValue = testView {
|
||||||
if let node = testViewValue.asyncdisplaykit_node as? PeerInfoVisualMediaPaneNode {
|
if let node = testViewValue.asyncdisplaykit_node as? PeerInfoHeaderNavigationButton {
|
||||||
|
node.isUserInteractionEnabled = false
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
node.isUserInteractionEnabled = true
|
||||||
|
}
|
||||||
|
return .dismiss(consume: false, result: nil)
|
||||||
|
} else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoVisualMediaPaneNode {
|
||||||
node.brieflyDisableTouchActions()
|
node.brieflyDisableTouchActions()
|
||||||
return .dismiss(consume: false, result: nil)
|
return .dismiss(consume: false, result: nil)
|
||||||
} else {
|
} else {
|
||||||
|
@ -61,7 +61,7 @@ final class MessageMediaPlaylistItem: SharedMediaPlaylistItem {
|
|||||||
var playbackData: SharedMediaPlaybackData? {
|
var playbackData: SharedMediaPlaybackData? {
|
||||||
if let file = extractFileMedia(self.message) {
|
if let file = extractFileMedia(self.message) {
|
||||||
let fileReference = FileMediaReference.message(message: MessageReference(self.message), media: file)
|
let fileReference = FileMediaReference.message(message: MessageReference(self.message), media: file)
|
||||||
let source = SharedMediaPlaybackDataSource.telegramFile(fileReference)
|
let source = SharedMediaPlaybackDataSource.telegramFile(reference: fileReference, isCopyProtected: self.message.isCopyProtected())
|
||||||
for attribute in file.attributes {
|
for attribute in file.attributes {
|
||||||
switch attribute {
|
switch attribute {
|
||||||
case let .Audio(isVoice, _, _, _, _):
|
case let .Audio(isVoice, _, _, _, _):
|
||||||
|
@ -1248,7 +1248,9 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _ in return false }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in
|
}, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _ in return false }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in
|
||||||
return false
|
return false
|
||||||
}, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
}, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||||
}, presentController: { _, _ in }, navigationController: {
|
}, presentController: { _, _ in
|
||||||
|
}, presentControllerInCurrent: { _, _ in
|
||||||
|
}, navigationController: {
|
||||||
return nil
|
return nil
|
||||||
}, chatControllerNode: {
|
}, chatControllerNode: {
|
||||||
return nil
|
return nil
|
||||||
|
@ -227,13 +227,13 @@ final class SharedMediaPlayer {
|
|||||||
switch playbackData.type {
|
switch playbackData.type {
|
||||||
case .voice, .music:
|
case .voice, .music:
|
||||||
switch playbackData.source {
|
switch playbackData.source {
|
||||||
case let .telegramFile(fileReference):
|
case let .telegramFile(fileReference, _):
|
||||||
strongSelf.playbackItem = .audio(MediaPlayer(audioSessionManager: strongSelf.audioSession, postbox: strongSelf.account.postbox, resourceReference: fileReference.resourceReference(fileReference.media.resource), streamable: playbackData.type == .music ? .conservative : .none, video: false, preferSoftwareDecoding: false, enableSound: true, baseRate: rateValue, fetchAutomatically: true, playAndRecord: controlPlaybackWithProximity))
|
strongSelf.playbackItem = .audio(MediaPlayer(audioSessionManager: strongSelf.audioSession, postbox: strongSelf.account.postbox, resourceReference: fileReference.resourceReference(fileReference.media.resource), streamable: playbackData.type == .music ? .conservative : .none, video: false, preferSoftwareDecoding: false, enableSound: true, baseRate: rateValue, fetchAutomatically: true, playAndRecord: controlPlaybackWithProximity))
|
||||||
}
|
}
|
||||||
case .instantVideo:
|
case .instantVideo:
|
||||||
if let mediaManager = strongSelf.mediaManager, let item = item as? MessageMediaPlaylistItem {
|
if let mediaManager = strongSelf.mediaManager, let item = item as? MessageMediaPlaylistItem {
|
||||||
switch playbackData.source {
|
switch playbackData.source {
|
||||||
case let .telegramFile(fileReference):
|
case let .telegramFile(fileReference, _):
|
||||||
let videoNode = OverlayInstantVideoNode(postbox: strongSelf.account.postbox, audioSession: strongSelf.audioSession, manager: mediaManager.universalVideoManager, content: NativeVideoContent(id: .message(item.message.stableId, fileReference.media.fileId), fileReference: fileReference, enableSound: false, baseRate: rateValue), close: { [weak mediaManager] in
|
let videoNode = OverlayInstantVideoNode(postbox: strongSelf.account.postbox, audioSession: strongSelf.audioSession, manager: mediaManager.universalVideoManager, content: NativeVideoContent(id: .message(item.message.stableId, fileReference.media.fileId), fileReference: fileReference, enableSound: false, baseRate: rateValue), close: { [weak mediaManager] in
|
||||||
mediaManager?.setPlaylist(nil, type: .voice, control: .playback(.pause))
|
mediaManager?.setPlaylist(nil, type: .voice, control: .playback(.pause))
|
||||||
})
|
})
|
||||||
@ -483,7 +483,7 @@ final class SharedMediaPlayer {
|
|||||||
let fetchedCurrentSignal: Signal<Never, NoError>
|
let fetchedCurrentSignal: Signal<Never, NoError>
|
||||||
let fetchedNextSignal: Signal<Never, NoError>
|
let fetchedNextSignal: Signal<Never, NoError>
|
||||||
switch current {
|
switch current {
|
||||||
case let .telegramFile(file):
|
case let .telegramFile(file, _):
|
||||||
fetchedCurrentSignal = self.account.postbox.mediaBox.resourceData(file.media.resource)
|
fetchedCurrentSignal = self.account.postbox.mediaBox.resourceData(file.media.resource)
|
||||||
|> mapToSignal { data -> Signal<Void, NoError> in
|
|> mapToSignal { data -> Signal<Void, NoError> in
|
||||||
if data.complete {
|
if data.complete {
|
||||||
@ -496,7 +496,7 @@ final class SharedMediaPlayer {
|
|||||||
|> ignoreValues
|
|> ignoreValues
|
||||||
}
|
}
|
||||||
switch next {
|
switch next {
|
||||||
case let .telegramFile(file):
|
case let .telegramFile(file, _):
|
||||||
fetchedNextSignal = fetchedMediaResource(mediaBox: self.account.postbox.mediaBox, reference: file.resourceReference(file.media.resource))
|
fetchedNextSignal = fetchedMediaResource(mediaBox: self.account.postbox.mediaBox, reference: file.resourceReference(file.media.resource))
|
||||||
|> ignoreValues
|
|> ignoreValues
|
||||||
|> `catch` { _ -> Signal<Never, NoError> in
|
|> `catch` { _ -> Signal<Never, NoError> in
|
||||||
|
Loading…
x
Reference in New Issue
Block a user