Video stream improvements

This commit is contained in:
Ali 2022-03-01 21:41:00 +04:00
parent 612f5eeef1
commit 76f932212c
17 changed files with 225 additions and 62 deletions

View File

@ -111,6 +111,11 @@ public protocol ComponentTaggedView: UIView {
func matches(tag: Any) -> Bool
}
public final class GenericComponentViewTag {
public init() {
}
}
public protocol Component: _TypeErasedComponent, Equatable {
associatedtype EnvironmentType = Empty
associatedtype View: UIView = UIView

View File

@ -4,6 +4,7 @@ import UIKit
public final class Button: Component {
public let content: AnyComponent<Empty>
public let minSize: CGSize?
public let tag: AnyObject?
public let action: () -> Void
convenience public init(
@ -13,6 +14,7 @@ public final class Button: Component {
self.init(
content: content,
minSize: nil,
tag: nil,
action: action
)
}
@ -20,10 +22,12 @@ public final class Button: Component {
private init(
content: AnyComponent<Empty>,
minSize: CGSize?,
tag: AnyObject? = nil,
action: @escaping () -> Void
) {
self.content = content
self.minSize = nil
self.tag = tag
self.action = action
}
@ -31,6 +35,16 @@ public final class Button: Component {
return Button(
content: self.content,
minSize: minSize,
tag: self.tag,
action: self.action
)
}
public func tagged(_ tag: AnyObject) -> Button {
return Button(
content: self.content,
minSize: self.minSize,
tag: tag,
action: self.action
)
}
@ -42,10 +56,13 @@ public final class Button: Component {
if lhs.minSize != rhs.minSize {
return false
}
if lhs.tag !== rhs.tag {
return false
}
return true
}
public final class View: UIButton {
public final class View: UIButton, ComponentTaggedView {
private let contentView: ComponentHostView<Empty>
private var component: Button?
@ -72,6 +89,16 @@ public final class Button: Component {
fatalError("init(coder:) has not been implemented")
}
public func matches(tag: Any) -> Bool {
if let component = self.component, let componentTag = component.tag {
let tag = tag as AnyObject
if componentTag === tag {
return true
}
}
return false
}
@objc private func pressed() {
self.component?.action()
}

View File

@ -28,35 +28,50 @@ public final class LottieAnimationComponent: Component {
public struct Animation: Equatable {
public var name: String
public var loop: Bool
public var isAnimating: Bool
public var colors: [String: UIColor]
public init(name: String, colors: [String: UIColor], loop: Bool) {
public init(name: String, colors: [String: UIColor], loop: Bool, isAnimating: Bool = true) {
self.name = name
self.colors = colors
self.loop = loop
self.isAnimating = isAnimating
}
}
public let animation: Animation
public let tag: AnyObject?
public let size: CGSize?
public init(animation: Animation, size: CGSize?) {
public init(animation: Animation, tag: AnyObject? = nil, size: CGSize?) {
self.animation = animation
self.tag = tag
self.size = size
}
public func tagged(_ tag: AnyObject?) -> LottieAnimationComponent {
return LottieAnimationComponent(
animation: self.animation,
tag: tag,
size: self.size
)
}
public static func ==(lhs: LottieAnimationComponent, rhs: LottieAnimationComponent) -> Bool {
if lhs.animation != rhs.animation {
return false
}
if lhs.tag !== rhs.tag {
return false
}
if lhs.size != rhs.size {
return false
}
return true
}
public final class View: UIView {
private var currentAnimation: Animation?
public final class View: UIView, ComponentTaggedView {
private var component: LottieAnimationComponent?
private var colorCallbacks: [LOTColorValueCallback] = []
private var animationView: LOTAnimationView?
@ -83,8 +98,29 @@ public final class LottieAnimationComponent: Component {
fatalError("init(coder:) has not been implemented")
}
public func matches(tag: Any) -> Bool {
if let component = self.component, let componentTag = component.tag {
let tag = tag as AnyObject
if componentTag === tag {
return true
}
}
return false
}
public func playOnce() {
guard let animationView = self.animationView else {
return
}
animationView.stop()
animationView.loopAnimation = false
animationView.play { _ in
}
}
func update(component: LottieAnimationComponent, availableSize: CGSize, transition: Transition) -> CGSize {
if self.currentAnimation != component.animation {
if self.component?.animation != component.animation {
if let animationView = self.animationView, animationView.isAnimationPlaying {
animationView.completionBlock = { [weak self] _ in
guard let strongSelf = self else {
@ -94,7 +130,7 @@ public final class LottieAnimationComponent: Component {
}
animationView.loopAnimation = false
} else {
self.currentAnimation = component.animation
self.component = component
self.animationView?.removeFromSuperview()
@ -130,8 +166,14 @@ public final class LottieAnimationComponent: Component {
if let animationView = self.animationView {
animationView.frame = CGRect(origin: CGPoint(x: floor((size.width - animationSize.width) / 2.0), y: floor((size.height - animationSize.height) / 2.0)), size: animationSize)
if !animationView.isAnimationPlaying {
animationView.play { _ in
if component.animation.isAnimating {
if !animationView.isAnimationPlaying {
animationView.play { _ in
}
}
} else {
if animationView.isAnimationPlaying {
animationView.stop()
}
}
}

View File

@ -30,7 +30,7 @@ private final class HeaderContextReferenceContentSource: ContextReferenceContent
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}

View File

@ -544,11 +544,11 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
case let .reference(source):
let transitionInfo = source.transitionInfo()
if let transitionInfo = transitionInfo {
let referenceNode = transitionInfo.referenceNode
self.contentContainerNode.contentNode = .reference(node: referenceNode)
let referenceView = transitionInfo.referenceView
self.contentContainerNode.contentNode = .reference(view: referenceView)
self.contentAreaInScreenSpace = transitionInfo.contentAreaInScreenSpace
self.customPosition = transitionInfo.customPosition
var projectedFrame = convertFrame(referenceNode.view.bounds, from: referenceNode.view, to: self.view)
var projectedFrame = convertFrame(referenceView.bounds, from: referenceView, to: self.view)
projectedFrame.origin.x += transitionInfo.insets.left
projectedFrame.size.width -= transitionInfo.insets.left + transitionInfo.insets.right
projectedFrame.origin.y += transitionInfo.insets.top
@ -856,7 +856,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
switch self.source {
case let .reference(source):
guard let maybeContentNode = self.contentContainerNode.contentNode, case let .reference(referenceNode) = maybeContentNode else {
guard let maybeContentNode = self.contentContainerNode.contentNode, case let .reference(referenceView) = maybeContentNode else {
return
}
@ -883,8 +883,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
self.scrollNode.view.setContentOffset(self.scrollNode.view.contentOffset, animated: false)
if let transitionInfo = transitionInfo, let parentSupernode = referenceNode.supernode {
self.originalProjectedContentViewFrame = (convertFrame(referenceNode.frame, from: parentSupernode.view, to: self.view), convertFrame(referenceNode.bounds, from: referenceNode.view, to: self.view))
if let transitionInfo = transitionInfo, let parentSuperview = referenceView.superview {
self.originalProjectedContentViewFrame = (convertFrame(referenceView.frame, from: parentSuperview, to: self.view), convertFrame(referenceView.bounds, from: referenceView, to: self.view))
var updatedContentAreaInScreenSpace = transitionInfo.contentAreaInScreenSpace
updatedContentAreaInScreenSpace.origin.x = 0.0
@ -2027,13 +2027,13 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
public final class ContextControllerReferenceViewInfo {
public let referenceNode: ContextReferenceContentNode
public let referenceView: UIView
public let contentAreaInScreenSpace: CGRect
public let insets: UIEdgeInsets
public let customPosition: CGPoint?
public init(referenceNode: ContextReferenceContentNode, contentAreaInScreenSpace: CGRect, insets: UIEdgeInsets = UIEdgeInsets(), customPosition: CGPoint? = nil) {
self.referenceNode = referenceNode
public init(referenceView: UIView, contentAreaInScreenSpace: CGRect, insets: UIEdgeInsets = UIEdgeInsets(), customPosition: CGPoint? = nil) {
self.referenceView = referenceView
self.contentAreaInScreenSpace = contentAreaInScreenSpace
self.insets = insets
self.customPosition = customPosition

View File

@ -2,6 +2,9 @@ import Foundation
import AsyncDisplayKit
open class ContextReferenceContentNode: ASDisplayNode {
override public init() {
super.init()
}
}
public final class ContextExtractedContentContainingNode: ASDisplayNode {
@ -80,7 +83,7 @@ public final class ContextControllerContentNode: ASDisplayNode {
}
public enum ContextContentNode {
case reference(node: ContextReferenceContentNode)
case reference(view: UIView)
case extracted(node: ContextExtractedContentContainingNode, keepInPlace: Bool)
case controller(ContextControllerContentNode)
}

View File

@ -2666,6 +2666,6 @@ private final class HeaderContextReferenceContentSource: ContextReferenceContent
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}

View File

@ -982,6 +982,6 @@ final class InviteLinkContextReferenceContentSource: ContextReferenceContentSour
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}

View File

@ -1240,7 +1240,7 @@ private final class MediaPickerContextReferenceContentSource: ContextReferenceCo
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}

View File

@ -1577,6 +1577,6 @@ final class InviteLinkContextReferenceContentSource: ContextReferenceContentSour
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}

View File

@ -1071,6 +1071,6 @@ private final class ShareContextReferenceContentSource: ContextReferenceContentS
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds, customPosition: self.customPosition)
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds, customPosition: self.customPosition)
}
}

View File

@ -782,6 +782,6 @@ private final class HeaderContextReferenceContentSource: ContextReferenceContent
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}

View File

@ -11,6 +11,7 @@ import ShareController
import UndoUI
import TelegramPresentationData
import LottieAnimationComponent
import ContextUI
final class NavigationBackButtonComponent: Component {
let text: String
@ -258,8 +259,8 @@ private final class NavigationBarComponent: CombinedComponent {
context.add(item
.position(CGPoint(x: rightItemX - item.size.width / 2.0, y: context.component.topInset + contentHeight / 2.0))
)
rightItemX -= item.size.width + 4.0
centerRightInset += item.size.width + 4.0
rightItemX -= item.size.width + 8.0
centerRightInset += item.size.width + 8.0
}
let maxCenterInset = max(centerLeftInset, centerRightInset)
@ -491,6 +492,7 @@ public final class MediaStreamComponent: CombinedComponent {
var storedIsLandscape: Bool?
private(set) var canManageCall: Bool = false
let isPictureInPictureSupported: Bool
private(set) var isVisibleInHierarchy: Bool = false
@ -532,20 +534,30 @@ public final class MediaStreamComponent: CombinedComponent {
return transaction.getPeer(peerId)
}
self.infoDisposable = (combineLatest(queue: .mainQueue(), call.members, callPeer)
|> deliverOnMainQueue).start(next: { [weak self] members, callPeer in
self.infoDisposable = (combineLatest(queue: .mainQueue(), call.state, call.members, callPeer)
|> deliverOnMainQueue).start(next: { [weak self] state, members, callPeer in
guard let strongSelf = self, let members = members, let callPeer = callPeer else {
return
}
var updated = false
if state.canManageCall != strongSelf.canManageCall {
strongSelf.canManageCall = state.canManageCall
updated = true
}
let originInfo = OriginInfo(title: callPeer.debugDisplayTitle, memberCount: members.totalCount)
if strongSelf.originInfo != originInfo {
strongSelf.originInfo = originInfo
updated = true
}
if updated {
strongSelf.updated(transition: .immediate)
}
})
let _ = call.accountContext.engine.calls.getGroupCallStreamCredentials(peerId: call.peerId, revokePreviousCredentials: false).start()
//let _ = call.accountContext.engine.calls.getGroupCallStreamCredentials(peerId: call.peerId, revokePreviousCredentials: false).start()
self.isVisibleInHierarchyDisposable = (call.accountContext.sharedContext.applicationBindings.applicationInForeground
|> deliverOnMainQueue).start(next: { [weak self] inForeground in
@ -611,6 +623,8 @@ public final class MediaStreamComponent: CombinedComponent {
let toolbar = Child(ToolbarComponent.self)
let activatePictureInPicture = StoredActionSlot(Action<Void>.self)
let moreButtonTag = GenericComponentViewTag()
let moreAnimationTag = GenericComponentViewTag()
return { context in
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
@ -667,36 +681,108 @@ public final class MediaStreamComponent: CombinedComponent {
).minSize(CGSize(width: 44.0, height: 44.0)))))
}
/*let whiteColor = UIColor(white: 1.0, alpha: 1.0)
navigationRightItems.append(AnyComponentWithIdentity(id: "more", component: AnyComponent(Button(
content: AnyComponent(ZStack([
AnyComponentWithIdentity(id: "b", component: AnyComponent(Circle(
color: .white,
size: CGSize(width: 22.0, height: 22.0),
width: 1.5
))),
AnyComponentWithIdentity(id: "a", component: AnyComponent(LottieAnimationComponent(
animation: LottieAnimationComponent.Animation(
name: "anim_profilemore",
colors: [
"Point 2.Group 1.Fill 1": whiteColor,
"Point 3.Group 1.Fill 1": whiteColor,
"Point 1.Group 1.Fill 1": whiteColor
],
loop: true
),
size: CGSize(width: 22.0, height: 22.0)
))),
])),
action: {
activatePictureInPicture.invoke(Action {
if context.state.canManageCall {
let whiteColor = UIColor(white: 1.0, alpha: 1.0)
navigationRightItems.append(AnyComponentWithIdentity(id: "more", component: AnyComponent(Button(
content: AnyComponent(ZStack([
AnyComponentWithIdentity(id: "b", component: AnyComponent(Circle(
color: .white,
size: CGSize(width: 22.0, height: 22.0),
width: 1.5
))),
AnyComponentWithIdentity(id: "a", component: AnyComponent(LottieAnimationComponent(
animation: LottieAnimationComponent.Animation(
name: "anim_profilemore",
colors: [
"Point 2.Group 1.Fill 1": whiteColor,
"Point 3.Group 1.Fill 1": whiteColor,
"Point 1.Group 1.Fill 1": whiteColor
],
loop: false,
isAnimating: false
),
size: CGSize(width: 22.0, height: 22.0)
).tagged(moreAnimationTag))),
])),
action: { [weak call] in
guard let call = call else {
return
}
guard let controller = controller() as? MediaStreamComponentController else {
return
}
controller.dismiss(closing: false, manual: true)
})
}
).minSize(CGSize(width: 44.0, height: 44.0)))))*/
guard let anchorView = controller.node.hostView.findTaggedView(tag: moreButtonTag) else {
return
}
if let animationView = controller.node.hostView.findTaggedView(tag: moreAnimationTag) as? LottieAnimationComponent.View {
animationView.playOnce()
}
let presentationData = call.accountContext.sharedContext.currentPresentationData.with { $0 }
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(id: nil, text: presentationData.strings.VoiceChat_StopRecordingStop, textColor: .primary, textLayout: .singleLine, textFont: .regular, badge: nil, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.primaryColor, backgroundColor: nil)
}, action: { [weak call] _, a in
guard let call = call else {
return
}
let _ = call.leave(terminateIfPossible: true).start()
a(.default)
})))
final class ReferenceContentSource: ContextReferenceContentSource {
private let sourceView: UIView
init(sourceView: UIView) {
self.sourceView = sourceView
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}
let contextController = ContextController(account: call.accountContext.account, presentationData: presentationData.withUpdated(theme: defaultDarkPresentationTheme), source: .reference(ReferenceContentSource(sourceView: anchorView)), items: .single(ContextController.Items(content: .list(items))), gesture: nil)
/*contextController.passthroughTouchEvent = { sourceView, point in
guard let strongSelf = self else {
return .ignore
}
let localPoint = strongSelf.view.convert(sourceView.convert(point, to: nil), from: nil)
guard let localResult = strongSelf.hitTest(localPoint, with: nil) else {
return .dismiss(consume: true, result: nil)
}
var testView: UIView? = localResult
while true {
if let testViewValue = testView {
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()
return .dismiss(consume: false, result: nil)
} else {
testView = testViewValue.superview
}
} else {
break
}
}
return .dismiss(consume: true, result: nil)
}*/
controller.presentInGlobalOverlay(contextController)
}
).minSize(CGSize(width: 44.0, height: 44.0)).tagged(moreButtonTag))))
}
let navigationBar = navigationBar.update(
component: NavigationBarComponent(

View File

@ -7090,6 +7090,6 @@ private final class VoiceChatContextReferenceContentSource: ContextReferenceCont
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}

View File

@ -15285,7 +15285,7 @@ final class ChatControllerContextReferenceContentSource: ContextReferenceContent
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds.inset(by: self.insets), insets: self.contentInsets)
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds.inset(by: self.insets), insets: self.contentInsets)
}
}

View File

@ -7860,7 +7860,7 @@ private final class PeerInfoContextReferenceContentSource: ContextReferenceConte
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}

@ -1 +1 @@
Subproject commit a5da9369d138bbcf6d435808fd599a59cedabe4c
Subproject commit 57ee64275928f72821ddb329765c52a4a5ec70f0