mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Temp
This commit is contained in:
parent
c31e0372f8
commit
c9aa7d70d6
@ -63,9 +63,20 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)completeWithContent:(UNNotificationContent * _Nonnull)content {
|
||||
/*NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"org.telegram.Telegram-iOS.background"];
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
|
||||
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:[NSURL URLWithString:@"https://telegram.org"]];
|
||||
[task resume];*/
|
||||
|
||||
if (_contentHandler) {
|
||||
_contentHandler(content);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
|
||||
if (_rootPath == nil) {
|
||||
contentHandler(request.content);
|
||||
[self completeWithContent:request.content];
|
||||
return;
|
||||
}
|
||||
|
||||
@ -289,8 +300,8 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
|
||||
_bestAttemptContent.attachments = @[attachment];
|
||||
}
|
||||
}
|
||||
if (_contentHandler && _bestAttemptContent != nil) {
|
||||
_contentHandler(_bestAttemptContent);
|
||||
if (_bestAttemptContent != nil) {
|
||||
[self completeWithContent:_bestAttemptContent];
|
||||
}
|
||||
} else {
|
||||
BuildConfig *buildConfig = [[BuildConfig alloc] initWithBaseAppBundleId:_baseAppBundleId];
|
||||
@ -322,21 +333,21 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
|
||||
}
|
||||
}
|
||||
|
||||
if (strongSelf->_contentHandler && strongSelf->_bestAttemptContent != nil) {
|
||||
strongSelf->_contentHandler(strongSelf->_bestAttemptContent);
|
||||
if (strongSelf->_bestAttemptContent != nil) {
|
||||
[strongSelf completeWithContent:strongSelf->_bestAttemptContent];
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (_contentHandler && _bestAttemptContent != nil) {
|
||||
_contentHandler(_bestAttemptContent);
|
||||
if (_bestAttemptContent != nil) {
|
||||
[self completeWithContent:_bestAttemptContent];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (_contentHandler && _bestAttemptContent != nil) {
|
||||
_contentHandler(_bestAttemptContent);
|
||||
if (_bestAttemptContent != nil) {
|
||||
[self completeWithContent:_bestAttemptContent];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -349,7 +360,7 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
|
||||
|
||||
if (_contentHandler) {
|
||||
if(_bestAttemptContent) {
|
||||
_contentHandler(_bestAttemptContent);
|
||||
[self completeWithContent:_bestAttemptContent];
|
||||
_bestAttemptContent = nil;
|
||||
}
|
||||
_contentHandler = nil;
|
||||
|
@ -15,6 +15,7 @@ import TelegramNotices
|
||||
import SearchUI
|
||||
import DeleteChatPeerActionSheetItem
|
||||
import LanguageSuggestionUI
|
||||
import ContextUI
|
||||
|
||||
public func useSpecialTabBarIcons() -> Bool {
|
||||
return (Date(timeIntervalSince1970: 1545642000)...Date(timeIntervalSince1970: 1546387200)).contains(Date())
|
||||
@ -64,6 +65,27 @@ private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBa
|
||||
return false
|
||||
}
|
||||
|
||||
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
||||
let controller: ViewController
|
||||
weak var sourceNode: ASDisplayNode?
|
||||
|
||||
init(controller: ViewController, sourceNode: ASDisplayNode?) {
|
||||
self.controller = controller
|
||||
self.sourceNode = sourceNode
|
||||
}
|
||||
|
||||
func transitionInfo() -> ContextControllerTakeControllerInfo? {
|
||||
let sourceNode = self.sourceNode
|
||||
return ContextControllerTakeControllerInfo(contentAreaInScreenSpace: CGRect(origin: CGPoint(), size: CGSize(width: 10.0, height: 10.0)), sourceNode: { [weak sourceNode] in
|
||||
if let sourceNode = sourceNode {
|
||||
return (sourceNode, sourceNode.bounds)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public class ChatListControllerImpl: TelegramBaseController, ChatListController, UIViewControllerPreviewingDelegate {
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
@ -356,6 +378,19 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
preconditionFailure("debug tap")
|
||||
}
|
||||
}
|
||||
|
||||
self.customPresentPreviewingController = { [weak self] controller, sourceNode in
|
||||
guard let strongSelf = self else {
|
||||
return nil
|
||||
}
|
||||
var items: [ContextMenuItem] = []
|
||||
for i in 0 ..< 5 {
|
||||
items.append(.action(ContextMenuActionItem(text: "Item \(i)", icon: { _ in nil }, action: { controller, f in
|
||||
f(.default)
|
||||
})))
|
||||
}
|
||||
return ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: .controller(ContextControllerContentSourceImpl(controller: controller, sourceNode: sourceNode)), items: items, reactionItems: [])
|
||||
}
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
@ -1068,6 +1103,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
||||
if !self.didSetup3dTouch {
|
||||
self.didSetup3dTouch = true
|
||||
self.registerForPreviewingNonNative(with: self, sourceView: self.view, theme: PeekControllerTheme(presentationTheme: self.presentationData.theme))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func viewWillDisappear(_ animated: Bool) {
|
||||
|
@ -9,7 +9,7 @@ final class ContextContentContainerNode: ASDisplayNode {
|
||||
super.init()
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
func updateLayout(size: CGSize, scaledSize: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
guard let contentNode = self.contentNode else {
|
||||
return
|
||||
}
|
||||
@ -17,6 +17,9 @@ final class ContextContentContainerNode: ASDisplayNode {
|
||||
case .extracted:
|
||||
break
|
||||
case let .controller(controller):
|
||||
transition.updatePosition(node: controller, position: CGPoint(x: scaledSize.width / 2.0, y: scaledSize.height / 2.0))
|
||||
transition.updateBounds(node: controller, bounds: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateTransformScale(node: controller, scale: scaledSize.width / size.width)
|
||||
controller.updateLayout(size: size, transition: transition)
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,8 @@ final class ContextControllerContentNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
controller.containerLayoutUpdated(ContainerViewLayout(size: size, metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), deviceMetrics: .iPhoneX, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: transition)
|
||||
transition.updateFrame(node: self.controller.displayNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.controller.containerLayoutUpdated(ContainerViewLayout(size: size, metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), deviceMetrics: .iPhoneX, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,7 +339,25 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self.clippingNode.layer.animateBoundsOriginYAdditive(from: updatedContentAreaInScreenSpace.minY, to: 0.0, duration: 0.18 * animationDurationFactor, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
}
|
||||
case let .controller(source):
|
||||
break
|
||||
let contentParentNode = ContextControllerContentNode(controller: source.controller)
|
||||
self.contentContainerNode.contentNode = .controller(contentParentNode)
|
||||
self.contentContainerNode.clipsToBounds = true
|
||||
self.contentContainerNode.cornerRadius = 14.0
|
||||
self.contentContainerNode.addSubnode(contentParentNode)
|
||||
|
||||
let transitionInfo = source.transitionInfo()
|
||||
if let transitionInfo = transitionInfo, let (sourceNode, sourceNodeRect) = transitionInfo.sourceNode() {
|
||||
let projectedFrame = convertFrame(sourceNodeRect, from: sourceNode.view, to: self.view)
|
||||
self.originalProjectedContentViewFrame = (projectedFrame, projectedFrame)
|
||||
|
||||
var updatedContentAreaInScreenSpace = transitionInfo.contentAreaInScreenSpace
|
||||
updatedContentAreaInScreenSpace.origin.x = 0.0
|
||||
updatedContentAreaInScreenSpace.size.width = self.bounds.width
|
||||
self.contentAreaInScreenSpace = updatedContentAreaInScreenSpace
|
||||
|
||||
//self.clippingNode.layer.animateFrame(from: updatedContentAreaInScreenSpace, to: self.clippingNode.frame, duration: 0.18 * animationDurationFactor, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
//self.clippingNode.layer.animateBoundsOriginYAdditive(from: updatedContentAreaInScreenSpace.minY, to: 0.0, duration: 0.18 * animationDurationFactor, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
if let validLayout = self.validLayout {
|
||||
@ -375,14 +393,15 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self?.didCompleteAnimationIn = true
|
||||
})
|
||||
}
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor)
|
||||
|
||||
let springDuration: Double = 0.42 * animationDurationFactor
|
||||
let springDamping: CGFloat = 104.0
|
||||
self.actionsContainerNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
|
||||
if let contentNode = self.contentContainerNode.contentNode {
|
||||
switch contentNode {
|
||||
case let .extracted(extracted):
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor)
|
||||
self.actionsContainerNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
|
||||
|
||||
if let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
|
||||
let contentParentNode = extracted
|
||||
let localSourceFrame = self.view.convert(originalProjectedContentViewFrame.1, to: self.scrollNode.view)
|
||||
@ -396,8 +415,23 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: contentContainerOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
contentParentNode.applyAbsoluteOffsetSpring?(-contentContainerOffset.y, springDuration, springDamping)
|
||||
}
|
||||
case let .controller(controller):
|
||||
break
|
||||
case .controller:
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor)
|
||||
self.actionsContainerNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
|
||||
self.contentContainerNode.allowsGroupOpacity = true
|
||||
self.contentContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor, completion: { [weak self] _ in
|
||||
self?.contentContainerNode.allowsGroupOpacity = false
|
||||
})
|
||||
self.contentContainerNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
|
||||
|
||||
if let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
|
||||
let actionsSideInset: CGFloat = 11.0
|
||||
|
||||
let localSourceFrame = self.view.convert(CGRect(origin: CGPoint(x: actionsSideInset, y: originalProjectedContentViewFrame.1.minY), size: CGSize(width: actionsSideInset, height: originalProjectedContentViewFrame.1.height)), to: self.scrollNode.view)
|
||||
self.actionsContainerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
let contentContainerOffset = CGPoint(x: localSourceFrame.center.x - self.contentContainerNode.frame.center.x, y: localSourceFrame.center.y - self.contentContainerNode.frame.center.y)
|
||||
self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: contentContainerOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -410,6 +444,10 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
|
||||
switch self.source {
|
||||
case let .extracted(source):
|
||||
guard let maybeContentNode = self.contentContainerNode.contentNode, case let .extracted(contentParentNode) = maybeContentNode else {
|
||||
return
|
||||
}
|
||||
|
||||
let putBackInfo = source.putBack()
|
||||
|
||||
if putBackInfo == nil {
|
||||
@ -438,7 +476,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
var completedContentNode = false
|
||||
var completedActionsNode = false
|
||||
|
||||
if let putBackInfo = putBackInfo, let contentParentNode = self.contentParentNode, let parentSupernode = contentParentNode.supernode {
|
||||
if let putBackInfo = putBackInfo, let parentSupernode = contentParentNode.supernode {
|
||||
self.originalProjectedContentViewFrame = (convertFrame(contentParentNode.frame, from: parentSupernode.view, to: self.view), convertFrame(contentParentNode.contentRect, from: contentParentNode.view, to: self.view))
|
||||
|
||||
var updatedContentAreaInScreenSpace = putBackInfo.contentAreaInScreenSpace
|
||||
@ -449,9 +487,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self.clippingNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: updatedContentAreaInScreenSpace.minY, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
let contentParentNode = self.contentParentNode
|
||||
|
||||
contentParentNode?.willUpdateIsExtractedToContextPreview?(false)
|
||||
contentParentNode.willUpdateIsExtractedToContextPreview?(false)
|
||||
|
||||
let intermediateCompletion: () -> Void = { [weak contentParentNode] in
|
||||
if completedEffect && completedContentNode && completedActionsNode {
|
||||
@ -518,7 +554,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
animateOutToItem = false
|
||||
}
|
||||
|
||||
if animateOutToItem, let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame, let contentParentNode = self.contentParentNode {
|
||||
if animateOutToItem, let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
|
||||
let localSourceFrame = self.view.convert(originalProjectedContentViewFrame.1, to: self.scrollNode.view)
|
||||
self.actionsContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y), duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false, additive: true)
|
||||
let contentContainerOffset = CGPoint(x: localSourceFrame.center.x - self.contentContainerNode.frame.center.x - contentParentNode.contentRect.minX, y: localSourceFrame.center.y - self.contentContainerNode.frame.center.y - contentParentNode.contentRect.minY)
|
||||
@ -532,7 +568,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
if let reactionContextNode = self.reactionContextNode {
|
||||
reactionContextNode.animateOut(to: CGRect(origin: CGPoint(x: originalProjectedContentViewFrame.1.minX, y: originalProjectedContentViewFrame.1.minY), size: contentParentNode.contentRect.size), animatingOutToReaction: self.reactionContextNodeIsAnimatingOut)
|
||||
}
|
||||
} else if let contentParentNode = self.contentParentNode {
|
||||
} else {
|
||||
if let snapshotView = contentParentNode.contentNode.view.snapshotContentTree() {
|
||||
self.contentContainerNode.view.addSubview(snapshotView)
|
||||
}
|
||||
@ -553,8 +589,138 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
}
|
||||
case let .controller(source):
|
||||
guard let maybeContentNode = self.contentContainerNode.contentNode, case let .controller(controller) = maybeContentNode else {
|
||||
return
|
||||
}
|
||||
|
||||
let transitionInfo = source.transitionInfo()
|
||||
|
||||
if transitionInfo == nil {
|
||||
result = .dismissWithoutContent
|
||||
}
|
||||
|
||||
switch result {
|
||||
case let .custom(value):
|
||||
switch value {
|
||||
case let .animated(duration, curve):
|
||||
transitionDuration = duration
|
||||
transitionCurve = curve
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
self.isUserInteractionEnabled = false
|
||||
self.isAnimatingOut = true
|
||||
|
||||
self.scrollNode.view.setContentOffset(self.scrollNode.view.contentOffset, animated: false)
|
||||
|
||||
var completedEffect = false
|
||||
var completedContentNode = false
|
||||
var completedActionsNode = false
|
||||
|
||||
if let transitionInfo = transitionInfo, let (sourceNode, sourceNodeRect) = transitionInfo.sourceNode() {
|
||||
let projectedFrame = convertFrame(sourceNodeRect, from: sourceNode.view, to: self.view)
|
||||
self.originalProjectedContentViewFrame = (projectedFrame, projectedFrame)
|
||||
|
||||
var updatedContentAreaInScreenSpace = transitionInfo.contentAreaInScreenSpace
|
||||
updatedContentAreaInScreenSpace.origin.x = 0.0
|
||||
updatedContentAreaInScreenSpace.size.width = self.bounds.width
|
||||
}
|
||||
|
||||
let intermediateCompletion: () -> Void = {
|
||||
if completedEffect && completedContentNode && completedActionsNode {
|
||||
switch result {
|
||||
case .default, .custom:
|
||||
break
|
||||
case .dismissWithoutContent:
|
||||
break
|
||||
}
|
||||
|
||||
completion()
|
||||
}
|
||||
}
|
||||
|
||||
if #available(iOS 10.0, *) {
|
||||
if let propertyAnimator = self.propertyAnimator {
|
||||
let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator
|
||||
propertyAnimator?.stopAnimation(true)
|
||||
}
|
||||
self.propertyAnimator = UIViewPropertyAnimator(duration: transitionDuration, curve: .easeInOut, animations: { [weak self] in
|
||||
self?.effectView.effect = nil
|
||||
})
|
||||
}
|
||||
|
||||
if let _ = self.propertyAnimator {
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.2 * animationDurationFactor, from: 0.0, to: 0.999, update: { [weak self] value in
|
||||
(self?.propertyAnimator as? UIViewPropertyAnimator)?.fractionComplete = value
|
||||
}, completion: {
|
||||
completedEffect = true
|
||||
intermediateCompletion()
|
||||
})
|
||||
}
|
||||
self.effectView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.05 * animationDurationFactor, delay: 0.15, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false)
|
||||
} else {
|
||||
UIView.animate(withDuration: 0.21 * animationDurationFactor, animations: {
|
||||
if #available(iOS 9.0, *) {
|
||||
self.effectView.effect = nil
|
||||
} else {
|
||||
self.effectView.alpha = 0.0
|
||||
}
|
||||
}, completion: { _ in
|
||||
completedEffect = true
|
||||
intermediateCompletion()
|
||||
})
|
||||
}
|
||||
|
||||
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false)
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2 * animationDurationFactor, removeOnCompletion: false, completion: { _ in
|
||||
completedActionsNode = true
|
||||
intermediateCompletion()
|
||||
})
|
||||
self.contentContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2 * animationDurationFactor, removeOnCompletion: false, completion: { _ in
|
||||
})
|
||||
self.actionsContainerNode.layer.animateScale(from: 1.0, to: 0.1, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
||||
self.contentContainerNode.layer.animateScale(from: 1.0, to: 0.01, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
||||
|
||||
let animateOutToItem: Bool
|
||||
switch result {
|
||||
case .default, .custom:
|
||||
animateOutToItem = true
|
||||
case .dismissWithoutContent:
|
||||
animateOutToItem = false
|
||||
}
|
||||
|
||||
if animateOutToItem, let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
|
||||
let actionsSideInset: CGFloat = 11.0
|
||||
|
||||
let localSourceFrame = self.view.convert(CGRect(origin: CGPoint(x: actionsSideInset, y: originalProjectedContentViewFrame.1.minY), size: CGSize(width: actionsSideInset, height: originalProjectedContentViewFrame.1.height)), to: self.scrollNode.view)
|
||||
|
||||
self.actionsContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y), duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false, additive: true)
|
||||
let contentContainerOffset = CGPoint(x: localSourceFrame.center.x - self.contentContainerNode.frame.center.x, y: localSourceFrame.center.y - self.contentContainerNode.frame.center.y)
|
||||
self.contentContainerNode.layer.animatePosition(from: CGPoint(), to: contentContainerOffset, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false, additive: true, completion: { _ in
|
||||
completedContentNode = true
|
||||
intermediateCompletion()
|
||||
})
|
||||
} else {
|
||||
if let snapshotView = controller.view.snapshotContentTree() {
|
||||
self.contentContainerNode.view.addSubview(snapshotView)
|
||||
}
|
||||
|
||||
self.contentContainerNode.allowsGroupOpacity = true
|
||||
self.contentContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false, completion: { _ in
|
||||
completedContentNode = true
|
||||
intermediateCompletion()
|
||||
})
|
||||
|
||||
if let reactionContextNode = self.reactionContextNode {
|
||||
reactionContextNode.animateOut(to: nil, animatingOutToReaction: self.reactionContextNodeIsAnimatingOut)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func animateOutToReaction(value: String, into targetNode: ASImageNode, hideNode: Bool, completion: @escaping () -> Void) {
|
||||
@ -646,13 +812,16 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
let actionsBottomInset: CGFloat = 11.0
|
||||
|
||||
if let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame, let contentParentNode = self.contentParentNode {
|
||||
if let contentNode = self.contentContainerNode.contentNode {
|
||||
switch contentNode {
|
||||
case let .extracted(contentParentNode):
|
||||
if let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
|
||||
let isInitialLayout = self.actionsContainerNode.frame.size.width.isZero
|
||||
let previousContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
|
||||
|
||||
let actionsSize = self.actionsContainerNode.updateLayout(constrainedWidth: layout.size.width - actionsSideInset * 2.0, transition: actionsContainerTransition)
|
||||
let contentSize = originalProjectedContentViewFrame.1.size
|
||||
self.contentContainerNode.updateLayout(size: contentSize, transition: transition)
|
||||
self.contentContainerNode.updateLayout(size: contentSize, scaledSize: contentSize, transition: transition)
|
||||
|
||||
let maximumActionsFrameOrigin = max(60.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - actionsSize.height)
|
||||
var originalActionsFrame = CGRect(origin: CGPoint(x: max(actionsSideInset, min(layout.size.width - actionsSize.width - actionsSideInset, originalProjectedContentViewFrame.1.minX)), y: min(originalProjectedContentViewFrame.1.maxY + contentActionsSpacing, maximumActionsFrameOrigin)), size: actionsSize)
|
||||
@ -697,6 +866,63 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
reactionContextNode.updateLayout(size: layout.size, insets: insets, anchorRect: CGRect(origin: CGPoint(x: absoluteContentRect.minX + contentParentNode.contentRect.minX, y: absoluteContentRect.minY + contentParentNode.contentRect.minY), size: contentParentNode.contentRect.size), transition: transition)
|
||||
}
|
||||
}
|
||||
case let .controller(contentParentNode):
|
||||
let topEdge = max(contentTopInset, self.contentAreaInScreenSpace?.minY ?? 0.0)
|
||||
|
||||
contentParentNode.updateLayout(size: layout.size, transition: transition)
|
||||
|
||||
let isInitialLayout = self.actionsContainerNode.frame.size.width.isZero
|
||||
let previousContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
|
||||
|
||||
let actionsSize = self.actionsContainerNode.updateLayout(constrainedWidth: layout.size.width - actionsSideInset * 2.0, transition: actionsContainerTransition)
|
||||
let contentScale = (layout.size.width - actionsSideInset * 2.0) / layout.size.width
|
||||
let proposedContentHeight = layout.size.height - topEdge - contentActionsSpacing - actionsSize.height - layout.intrinsicInsets.bottom - actionsBottomInset
|
||||
let contentUnscaledSize = CGSize(width: layout.size.width, height: max(400.0, proposedContentHeight))
|
||||
let contentSize = CGSize(width: floor(contentUnscaledSize.width * contentScale), height: floor(contentUnscaledSize.height * contentScale))
|
||||
|
||||
self.contentContainerNode.updateLayout(size: contentUnscaledSize, scaledSize: contentSize, transition: transition)
|
||||
|
||||
let maximumActionsFrameOrigin = max(60.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - actionsSize.height)
|
||||
var originalActionsFrame = CGRect(origin: CGPoint(x: actionsSideInset, y: maximumActionsFrameOrigin), size: actionsSize)
|
||||
var originalContentFrame = CGRect(origin: CGPoint(x: actionsSideInset, y: originalActionsFrame.minY - contentActionsSpacing - contentSize.height), size: contentSize)
|
||||
if originalContentFrame.minY < topEdge {
|
||||
let requiredOffset = topEdge - originalContentFrame.minY
|
||||
let availableOffset = max(0.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - originalActionsFrame.maxY)
|
||||
let offset = min(requiredOffset, availableOffset)
|
||||
originalActionsFrame = originalActionsFrame.offsetBy(dx: 0.0, dy: offset)
|
||||
originalContentFrame = originalContentFrame.offsetBy(dx: 0.0, dy: offset)
|
||||
}
|
||||
|
||||
let contentHeight = max(layout.size.height, max(layout.size.height, originalActionsFrame.maxY + actionsBottomInset) - originalContentFrame.minY + contentTopInset)
|
||||
|
||||
let scrollContentSize = CGSize(width: layout.size.width, height: contentHeight)
|
||||
if self.scrollNode.view.contentSize != scrollContentSize {
|
||||
self.scrollNode.view.contentSize = scrollContentSize
|
||||
}
|
||||
|
||||
let overflowOffset = min(0.0, originalContentFrame.minY - contentTopInset)
|
||||
|
||||
let contentContainerFrame = originalContentFrame
|
||||
transition.updateFrame(node: self.contentContainerNode, frame: contentContainerFrame.offsetBy(dx: 0.0, dy: -overflowOffset))
|
||||
actionsContainerTransition.updateFrame(node: self.actionsContainerNode, frame: originalActionsFrame.offsetBy(dx: 0.0, dy: -overflowOffset))
|
||||
|
||||
if isInitialLayout {
|
||||
self.scrollNode.view.contentOffset = CGPoint(x: 0.0, y: -overflowOffset)
|
||||
let currentContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
|
||||
if overflowOffset < 0.0 {
|
||||
transition.animateOffsetAdditive(node: self.scrollNode, offset: currentContainerFrame.minY - previousContainerFrame.minY)
|
||||
}
|
||||
}
|
||||
|
||||
let absoluteContentRect = contentContainerFrame.offsetBy(dx: 0.0, dy: -self.scrollNode.view.contentOffset.y)
|
||||
|
||||
if let reactionContextNode = self.reactionContextNode {
|
||||
let insets = layout.insets(options: [.statusBar])
|
||||
transition.updateFrame(node: reactionContextNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
reactionContextNode.updateLayout(size: layout.size, insets: insets, anchorRect: CGRect(origin: CGPoint(x: absoluteContentRect.minX, y: absoluteContentRect.minY), size: contentSize), transition: transition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let previousActionsContainerNode = previousActionsContainerNode {
|
||||
if transition.isAnimated {
|
||||
@ -718,7 +944,10 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if let contentParentNode = self.contentParentNode, let layout = self.validLayout {
|
||||
guard let layout = self.validLayout else {
|
||||
return
|
||||
}
|
||||
if let maybeContentNode = self.contentContainerNode.contentNode, case let .extracted(contentParentNode) = maybeContentNode {
|
||||
let contentContainerFrame = self.contentContainerNode.frame
|
||||
contentParentNode.updateAbsoluteRect?(contentContainerFrame.offsetBy(dx: 0.0, dy: -self.scrollNode.view.contentOffset.y), layout.size)
|
||||
}
|
||||
@ -734,7 +963,9 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
}
|
||||
let mappedPoint = self.view.convert(point, to: self.scrollNode.view)
|
||||
if let contentParentNode = self.contentParentNode {
|
||||
if let maybeContentNode = self.contentContainerNode.contentNode {
|
||||
switch maybeContentNode {
|
||||
case let .extracted(contentParentNode):
|
||||
let contentPoint = self.view.convert(point, to: contentParentNode.contentNode.view)
|
||||
if let result = contentParentNode.contentNode.hitTest(contentPoint, with: event) {
|
||||
if result is TextSelectionNodeView {
|
||||
@ -743,6 +974,9 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
return contentParentNode.contentNode.view
|
||||
}
|
||||
}
|
||||
case let .controller(controller):
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if self.actionsContainerNode.frame.contains(mappedPoint) {
|
||||
@ -765,9 +999,11 @@ public final class ContextControllerTakeViewInfo {
|
||||
|
||||
public final class ContextControllerTakeControllerInfo {
|
||||
public let contentAreaInScreenSpace: CGRect
|
||||
public let sourceNode: () -> (ASDisplayNode, CGRect)?
|
||||
|
||||
public init(contentAreaInScreenSpace: CGRect) {
|
||||
public init(contentAreaInScreenSpace: CGRect, sourceNode: @escaping () -> (ASDisplayNode, CGRect)?) {
|
||||
self.contentAreaInScreenSpace = contentAreaInScreenSpace
|
||||
self.sourceNode = sourceNode
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ private func traceDeceleratingScrollView(_ view: UIView, at point: CGPoint) -> B
|
||||
|
||||
public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
private let contentAtPoint: (CGPoint) -> Signal<(ASDisplayNode, PeekControllerContent)?, NoError>?
|
||||
private let present: (PeekControllerContent, ASDisplayNode) -> PeekController?
|
||||
private let present: (PeekControllerContent, ASDisplayNode) -> ViewController?
|
||||
private let updateContent: (PeekControllerContent?) -> Void
|
||||
private let activateBySingleTap: Bool
|
||||
|
||||
@ -36,7 +36,7 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
private var menuActivation: PeerkControllerMenuActivation?
|
||||
private weak var presentedController: PeekController?
|
||||
|
||||
public init(contentAtPoint: @escaping (CGPoint) -> Signal<(ASDisplayNode, PeekControllerContent)?, NoError>?, present: @escaping (PeekControllerContent, ASDisplayNode) -> PeekController?, updateContent: @escaping (PeekControllerContent?) -> Void = { _ in }, activateBySingleTap: Bool = false) {
|
||||
public init(contentAtPoint: @escaping (CGPoint) -> Signal<(ASDisplayNode, PeekControllerContent)?, NoError>?, present: @escaping (PeekControllerContent, ASDisplayNode) -> ViewController?, updateContent: @escaping (PeekControllerContent?) -> Void = { _ in }, activateBySingleTap: Bool = false) {
|
||||
self.contentAtPoint = contentAtPoint
|
||||
self.present = present
|
||||
self.updateContent = updateContent
|
||||
@ -259,6 +259,7 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
}
|
||||
} else {
|
||||
if let presentedController = strongSelf.present(content, sourceNode) {
|
||||
if let presentedController = presentedController as? PeekController {
|
||||
if forceActivate {
|
||||
strongSelf.candidateContent = nil
|
||||
if case .press = content.menuActivation() {
|
||||
@ -284,6 +285,11 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if strongSelf.state != .ended {
|
||||
strongSelf.state = .ended
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if strongSelf.presentedController == nil {
|
||||
|
@ -224,6 +224,8 @@ open class ViewControllerPresentationArguments {
|
||||
public var scrollToTopWithTabBar: (() -> Void)?
|
||||
public var longTapWithTabBar: (() -> Void)?
|
||||
|
||||
public var customPresentPreviewingController: ((ViewController, ASDisplayNode) -> ViewController?)?
|
||||
|
||||
open func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
|
||||
}
|
||||
@ -491,12 +493,14 @@ open class ViewControllerPresentationArguments {
|
||||
|
||||
@available(iOSApplicationExtension 9.0, iOS 9.0, *)
|
||||
open func registerForPreviewing(with delegate: UIViewControllerPreviewingDelegate, sourceView: UIView, theme: PeekControllerTheme, onlyNative: Bool) {
|
||||
if self.traitCollection.forceTouchCapability == .available {
|
||||
if false, self.traitCollection.forceTouchCapability == .available {
|
||||
let _ = super.registerForPreviewing(with: delegate, sourceView: sourceView)
|
||||
} else if !onlyNative {
|
||||
if self.previewingContext == nil {
|
||||
let previewingContext = SimulatedViewControllerPreviewing(theme: theme, delegate: delegate, sourceView: sourceView, node: self.displayNode, present: { [weak self] c, a in
|
||||
self?.presentInGlobalOverlay(c, with: a)
|
||||
}, customPresent: { [weak self] c, n in
|
||||
return self?.customPresentPreviewingController?(c, n)
|
||||
})
|
||||
self.previewingContext = previewingContext
|
||||
}
|
||||
@ -505,10 +509,12 @@ open class ViewControllerPresentationArguments {
|
||||
|
||||
@available(iOSApplicationExtension 9.0, iOS 9.0, *)
|
||||
public func registerForPreviewingNonNative(with delegate: UIViewControllerPreviewingDelegate, sourceView: UIView, theme: PeekControllerTheme) {
|
||||
if self.traitCollection.forceTouchCapability != .available {
|
||||
if true || self.traitCollection.forceTouchCapability != .available {
|
||||
if self.previewingContext == nil {
|
||||
let previewingContext = SimulatedViewControllerPreviewing(theme: theme, delegate: delegate, sourceView: sourceView, node: self.displayNode, present: { [weak self] c, a in
|
||||
self?.presentInGlobalOverlay(c, with: a)
|
||||
}, customPresent: { [weak self] c, n in
|
||||
return self?.customPresentPreviewingController?(c, n)
|
||||
})
|
||||
self.previewingContext = previewingContext
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import SwiftSignalKit
|
||||
|
||||
@available(iOSApplicationExtension 9.0, iOS 9.0, *)
|
||||
private final class ViewControllerPeekContent: PeekControllerContent {
|
||||
private let controller: ViewController
|
||||
let controller: ViewController
|
||||
private let menu: [PeekControllerMenuItem]
|
||||
|
||||
init(controller: ViewController) {
|
||||
@ -101,7 +101,7 @@ final class SimulatedViewControllerPreviewing: NSObject, UIViewControllerPreview
|
||||
|
||||
var sourceRect: CGRect = CGRect()
|
||||
|
||||
init(theme: PeekControllerTheme, delegate: UIViewControllerPreviewingDelegate, sourceView: UIView, node: ASDisplayNode, present: @escaping (ViewController, Any?) -> Void) {
|
||||
init(theme: PeekControllerTheme, delegate: UIViewControllerPreviewingDelegate, sourceView: UIView, node: ASDisplayNode, present: @escaping (ViewController, Any?) -> Void, customPresent: ((ViewController, ASDisplayNode) -> ViewController?)?) {
|
||||
self.delegateImpl = delegate
|
||||
self.sourceView = sourceView
|
||||
self.node = node
|
||||
@ -109,11 +109,16 @@ final class SimulatedViewControllerPreviewing: NSObject, UIViewControllerPreview
|
||||
self.recognizer = PeekControllerGestureRecognizer(contentAtPoint: { point in
|
||||
return contentAtPointImpl?(point)
|
||||
}, present: { content, sourceNode in
|
||||
if let content = content as? ViewControllerPeekContent, let controller = customPresent?(content.controller, sourceNode) {
|
||||
present(controller, nil)
|
||||
return controller
|
||||
} else {
|
||||
let controller = PeekController(theme: theme, content: content, sourceNode: {
|
||||
return sourceNode
|
||||
})
|
||||
present(controller, nil)
|
||||
return controller
|
||||
}
|
||||
})
|
||||
|
||||
node.view.addGestureRecognizer(self.recognizer)
|
||||
|
@ -1277,6 +1277,14 @@ final class SharedApplicationContext {
|
||||
self.isInForegroundPromise.set(true)
|
||||
self.isActiveValue = true
|
||||
self.isActivePromise.set(true)
|
||||
|
||||
let configuration = URLSessionConfiguration.background(withIdentifier: "org.telegram.Telegram-iOS.background")
|
||||
let session = URLSession(configuration: configuration)
|
||||
if #available(iOS 9.0, *) {
|
||||
session.getAllTasks(completionHandler: { tasks in
|
||||
print(tasks)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ application: UIApplication) {
|
||||
@ -1741,6 +1749,11 @@ final class SharedApplicationContext {
|
||||
})
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
|
||||
Logger.shared.log("App \(self.episodeId)", "handleEventsForBackgroundURLSession \(identifier)")
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
override var next: UIResponder? {
|
||||
if let context = self.contextValue, let controller = context.context.keyShortcutsController {
|
||||
return controller
|
||||
|
@ -563,7 +563,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if Namespaces.Message.allScheduled.contains(message.id.namespace) {
|
||||
reactionItems = []
|
||||
}
|
||||
let controller = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message), items: actions, reactionItems: reactionItems, recognizer: recognizer)
|
||||
let controller = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message)), items: actions, reactionItems: reactionItems, recognizer: recognizer)
|
||||
strongSelf.currentContextController = controller
|
||||
controller.reactionSelected = { [weak controller] value in
|
||||
guard let strongSelf = self, let message = updatedMessages.first else {
|
||||
@ -1392,7 +1392,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
})))
|
||||
|
||||
let controller = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message), items: actions, reactionItems: [], recognizer: nil)
|
||||
let controller = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message)), items: actions, reactionItems: [], recognizer: nil)
|
||||
strongSelf.currentContextController = controller
|
||||
strongSelf.window?.presentInGlobalOverlay(controller)
|
||||
})
|
||||
@ -1432,7 +1432,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
f(.dismissWithoutContent)
|
||||
})))
|
||||
|
||||
let controller = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message), items: actions, reactionItems: [], recognizer: nil)
|
||||
let controller = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message)), items: actions, reactionItems: [], recognizer: nil)
|
||||
strongSelf.currentContextController = controller
|
||||
strongSelf.window?.presentInGlobalOverlay(controller)
|
||||
})
|
||||
|
@ -832,7 +832,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
}
|
||||
strongSelf.fetchDisposable.set(visibilityAwareFetchSignal.start())
|
||||
}
|
||||
} else if case .prefetch = automaticDownload, message.id.namespace != Namespaces.Message.SecretIncoming && message.id.namespace != Namespaces.Message.Local {
|
||||
} else if case .prefetch = automaticDownload, message.id.namespace != Namespaces.Message.SecretIncoming /*&& message.id.namespace != Namespaces.Message.Local*/ {
|
||||
if let file = media as? TelegramMediaFile {
|
||||
let fetchSignal = preloadVideoResource(postbox: context.account.postbox, resourceReference: AnyMediaReference.message(message: MessageReference(message), media: file).resourceReference(file.resource), duration: 4.0)
|
||||
let visibilityAwareFetchSignal = strongSelf.visibilityPromise.get()
|
||||
|
@ -73,6 +73,151 @@ struct VideoConversionConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
private final class FetchVideoLibraryMediaResourceItem {
|
||||
let priority: MediaBoxFetchPriority
|
||||
let signal: Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>
|
||||
let next: (MediaResourceDataFetchResult) -> Void
|
||||
let error: (MediaResourceDataFetchError) -> Void
|
||||
let completion: () -> Void
|
||||
var isActive: Bool = false
|
||||
var disposable: Disposable?
|
||||
|
||||
init(priority: MediaBoxFetchPriority, signal: Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>, next: @escaping (MediaResourceDataFetchResult) -> Void, error: @escaping (MediaResourceDataFetchError) -> Void, completion: @escaping () -> Void) {
|
||||
self.priority = priority
|
||||
self.signal = signal
|
||||
self.next = next
|
||||
self.error = error
|
||||
self.completion = completion
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.disposable?.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
private let fetchVideoLimit: Int = 2
|
||||
|
||||
private final class FetchVideoLibraryMediaResourceContextImpl {
|
||||
private let queue: Queue
|
||||
var items: [FetchVideoLibraryMediaResourceItem] = []
|
||||
|
||||
init(queue: Queue) {
|
||||
self.queue = queue
|
||||
}
|
||||
|
||||
func add(priority: MediaBoxFetchPriority, signal: Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>, next: @escaping (MediaResourceDataFetchResult) -> Void, error: @escaping (MediaResourceDataFetchError) -> Void, completion: @escaping () -> Void) -> Disposable {
|
||||
let queue = self.queue
|
||||
|
||||
let item = FetchVideoLibraryMediaResourceItem(priority: priority, signal: signal, next: next, error: error, completion: completion)
|
||||
self.items.append(item)
|
||||
|
||||
self.update()
|
||||
|
||||
return ActionDisposable { [weak self, weak item] in
|
||||
queue.async {
|
||||
guard let strongSelf = self, let item = item else {
|
||||
return
|
||||
}
|
||||
for i in 0 ..< strongSelf.items.count {
|
||||
if strongSelf.items[i] === item {
|
||||
strongSelf.items[i].disposable?.dispose()
|
||||
strongSelf.items.remove(at: i)
|
||||
strongSelf.update()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update() {
|
||||
let queue = self.queue
|
||||
|
||||
var activeCount = 0
|
||||
for item in self.items {
|
||||
if item.isActive {
|
||||
activeCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
while activeCount < fetchVideoLimit {
|
||||
var maxPriorityIndex: Int?
|
||||
for i in 0 ..< self.items.count {
|
||||
if !self.items[i].isActive {
|
||||
if let maxPriorityIndexValue = maxPriorityIndex {
|
||||
if self.items[i].priority.rawValue > self.items[maxPriorityIndexValue].priority.rawValue {
|
||||
maxPriorityIndex = i
|
||||
}
|
||||
} else {
|
||||
maxPriorityIndex = i
|
||||
}
|
||||
}
|
||||
}
|
||||
if let maxPriorityIndex = maxPriorityIndex {
|
||||
let item = self.items[maxPriorityIndex]
|
||||
item.isActive = true
|
||||
activeCount += 1
|
||||
assert(item.disposable == nil)
|
||||
item.disposable = self.items[maxPriorityIndex].signal.start(next: { [weak item] value in
|
||||
queue.async {
|
||||
item?.next(value)
|
||||
}
|
||||
}, error: { [weak item] value in
|
||||
queue.async {
|
||||
item?.error(value)
|
||||
}
|
||||
}, completed: { [weak self, weak item] in
|
||||
queue.async {
|
||||
guard let strongSelf = self, let item = item else {
|
||||
return
|
||||
}
|
||||
for i in 0 ..< strongSelf.items.count {
|
||||
if strongSelf.items[i] === item {
|
||||
strongSelf.items.remove(at: i)
|
||||
item.completion()
|
||||
strongSelf.update()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class FetchVideoLibraryMediaResourceContext {
|
||||
private let queue = Queue()
|
||||
private let impl: QueueLocalObject<FetchVideoLibraryMediaResourceContextImpl>
|
||||
|
||||
init() {
|
||||
let queue = self.queue
|
||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||
return FetchVideoLibraryMediaResourceContextImpl(queue: queue)
|
||||
})
|
||||
}
|
||||
|
||||
func wrap(priority: MediaBoxFetchPriority, signal: Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
disposable.set(impl.add(priority: priority, signal: signal, next: { value in
|
||||
subscriber.putNext(value)
|
||||
}, error: { error in
|
||||
subscriber.putError(error)
|
||||
}, completion: {
|
||||
subscriber.putCompletion()
|
||||
}))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let throttlingContext = FetchVideoLibraryMediaResourceContext()
|
||||
|
||||
public func fetchVideoLibraryMediaResource(postbox: Postbox, resource: VideoLibraryMediaResource) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
|
||||
return postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|
||||
|> take(1)
|
||||
@ -82,9 +227,8 @@ public func fetchVideoLibraryMediaResource(postbox: Postbox, resource: VideoLibr
|
||||
|> introduceError(MediaResourceDataFetchError.self)
|
||||
|> mapToSignal { appConfiguration -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> in
|
||||
let config = VideoConversionConfiguration.with(appConfiguration: appConfiguration)
|
||||
return Signal { subscriber in
|
||||
let signal = Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> { subscriber in
|
||||
subscriber.putNext(.reset)
|
||||
|
||||
let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [resource.localIdentifier], options: nil)
|
||||
var requestId: PHImageRequestID?
|
||||
let disposable = MetaDisposable()
|
||||
@ -189,6 +333,7 @@ public func fetchVideoLibraryMediaResource(postbox: Postbox, resource: VideoLibr
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
||||
return throttlingContext.wrap(priority: .default, signal: signal)
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,7 +346,7 @@ func fetchLocalFileVideoMediaResource(postbox: Postbox, resource: LocalFileVideo
|
||||
|> introduceError(MediaResourceDataFetchError.self)
|
||||
|> mapToSignal { appConfiguration -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> in
|
||||
let config = VideoConversionConfiguration.with(appConfiguration: appConfiguration)
|
||||
return Signal { subscriber in
|
||||
let signal = Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> { subscriber in
|
||||
subscriber.putNext(.reset)
|
||||
|
||||
let avAsset = AVURLAsset(url: URL(fileURLWithPath: resource.path))
|
||||
@ -267,6 +412,7 @@ func fetchLocalFileVideoMediaResource(postbox: Postbox, resource: LocalFileVideo
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
||||
return throttlingContext.wrap(priority: .default, signal: signal)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user