mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 01:10:09 +00:00
[WIP] Call UI
This commit is contained in:
parent
63575a4a30
commit
743551096f
@ -48,7 +48,7 @@ public final class ViewController: UIViewController {
|
|||||||
self.callState.lifecycleState = .active(PrivateCallScreen.State.ActiveState(
|
self.callState.lifecycleState = .active(PrivateCallScreen.State.ActiveState(
|
||||||
startTime: Date().timeIntervalSince1970,
|
startTime: Date().timeIntervalSince1970,
|
||||||
signalInfo: PrivateCallScreen.State.SignalInfo(quality: 1.0),
|
signalInfo: PrivateCallScreen.State.SignalInfo(quality: 1.0),
|
||||||
emojiKey: ["A", "B", "C", "D"]
|
emojiKey: ["😂", "😘", "😍", "😊"]
|
||||||
))
|
))
|
||||||
case var .active(activeState):
|
case var .active(activeState):
|
||||||
activeState.signalInfo.quality = activeState.signalInfo.quality == 1.0 ? 0.1 : 1.0
|
activeState.signalInfo.quality = activeState.signalInfo.quality == 1.0 ? 0.1 : 1.0
|
||||||
@ -57,7 +57,7 @@ public final class ViewController: UIViewController {
|
|||||||
self.callState.lifecycleState = .active(PrivateCallScreen.State.ActiveState(
|
self.callState.lifecycleState = .active(PrivateCallScreen.State.ActiveState(
|
||||||
startTime: Date().timeIntervalSince1970,
|
startTime: Date().timeIntervalSince1970,
|
||||||
signalInfo: PrivateCallScreen.State.SignalInfo(quality: 1.0),
|
signalInfo: PrivateCallScreen.State.SignalInfo(quality: 1.0),
|
||||||
emojiKey: ["A", "B", "C", "D"]
|
emojiKey: ["😂", "😘", "😍", "😊"]
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,146 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import ComponentFlow
|
||||||
|
|
||||||
|
final class EmojiExpandedInfoView: OverlayMaskContainerView {
|
||||||
|
private struct Params: Equatable {
|
||||||
|
var constrainedWidth: CGFloat
|
||||||
|
var sideInset: CGFloat
|
||||||
|
|
||||||
|
init(constrainedWidth: CGFloat, sideInset: CGFloat) {
|
||||||
|
self.constrainedWidth = constrainedWidth
|
||||||
|
self.sideInset = sideInset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct Layout: Equatable {
|
||||||
|
var params: Params
|
||||||
|
var size: CGSize
|
||||||
|
|
||||||
|
init(params: Params, size: CGSize) {
|
||||||
|
self.params = params
|
||||||
|
self.size = size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let title: String
|
||||||
|
private let text: String
|
||||||
|
|
||||||
|
private let backgroundView: UIImageView
|
||||||
|
private let titleView: TextView
|
||||||
|
private let textView: TextView
|
||||||
|
|
||||||
|
private let actionButton: HighlightTrackingButton
|
||||||
|
private let actionTitleView: TextView
|
||||||
|
|
||||||
|
private var currentLayout: Layout?
|
||||||
|
|
||||||
|
var closeAction: (() -> Void)?
|
||||||
|
|
||||||
|
init(title: String, text: String) {
|
||||||
|
self.title = title
|
||||||
|
self.text = text
|
||||||
|
|
||||||
|
self.backgroundView = UIImageView()
|
||||||
|
let cornerRadius: CGFloat = 18.0
|
||||||
|
let buttonHeight: CGFloat = 56.0
|
||||||
|
self.backgroundView.image = generateImage(CGSize(width: cornerRadius * 2.0 + 10.0, height: cornerRadius + 10.0 + buttonHeight), rotatedContext: { size, context in
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: cornerRadius).cgPath)
|
||||||
|
context.setFillColor(UIColor.white.cgColor)
|
||||||
|
context.fillPath()
|
||||||
|
|
||||||
|
context.setBlendMode(.copy)
|
||||||
|
context.setFillColor(UIColor.clear.cgColor)
|
||||||
|
context.fill(CGRect(origin: CGPoint(x: 0.0, y: size.height - buttonHeight), size: CGSize(width: size.width, height: UIScreenPixel)))
|
||||||
|
})?.stretchableImage(withLeftCapWidth: Int(cornerRadius) + 5, topCapHeight: Int(cornerRadius) + 5)
|
||||||
|
|
||||||
|
self.titleView = TextView()
|
||||||
|
self.textView = TextView()
|
||||||
|
|
||||||
|
self.actionButton = HighlightTrackingButton()
|
||||||
|
self.actionTitleView = TextView()
|
||||||
|
self.actionTitleView.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
super.init(frame: CGRect())
|
||||||
|
|
||||||
|
self.maskContents.addSubview(self.backgroundView)
|
||||||
|
|
||||||
|
self.addSubview(self.titleView)
|
||||||
|
self.addSubview(self.textView)
|
||||||
|
|
||||||
|
self.addSubview(self.actionButton)
|
||||||
|
self.actionButton.addSubview(self.actionTitleView)
|
||||||
|
|
||||||
|
self.actionButton.internalHighligthedChanged = { [weak self] highlighted in
|
||||||
|
if let self, self.bounds.width > 0.0 {
|
||||||
|
let topScale: CGFloat = (self.bounds.width - 8.0) / self.bounds.width
|
||||||
|
let maxScale: CGFloat = (self.bounds.width + 2.0) / self.bounds.width
|
||||||
|
|
||||||
|
if highlighted {
|
||||||
|
self.actionButton.layer.removeAnimation(forKey: "sublayerTransform")
|
||||||
|
let transition = Transition(animation: .curve(duration: 0.15, curve: .easeInOut))
|
||||||
|
transition.setScale(layer: self.actionButton.layer, scale: topScale)
|
||||||
|
} else {
|
||||||
|
let t = self.actionButton.layer.presentation()?.transform ?? layer.transform
|
||||||
|
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
||||||
|
|
||||||
|
let transition = Transition(animation: .none)
|
||||||
|
transition.setScale(layer: self.actionButton.layer, scale: 1.0)
|
||||||
|
|
||||||
|
self.actionButton.layer.animateScale(from: currentScale, to: maxScale, duration: 0.13, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, completion: { [weak self] completed in
|
||||||
|
guard let self, completed else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.actionButton.layer.animateScale(from: maxScale, to: 1.0, duration: 0.1, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.actionButton.addTarget(self, action: #selector(self.actionButtonPressed), for: .touchUpInside)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func actionButtonPressed() {
|
||||||
|
self.closeAction?()
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(constrainedWidth: CGFloat, sideInset: CGFloat, transition: Transition) -> CGSize {
|
||||||
|
let params = Params(constrainedWidth: constrainedWidth, sideInset: sideInset)
|
||||||
|
if let currentLayout = self.currentLayout, currentLayout.params == params {
|
||||||
|
return currentLayout.size
|
||||||
|
}
|
||||||
|
let size = self.update(params: params, transition: transition)
|
||||||
|
self.currentLayout = Layout(params: params, size: size)
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
private func update(params: Params, transition: Transition) -> CGSize {
|
||||||
|
let size = CGSize(width: 304.0, height: 227.0)
|
||||||
|
|
||||||
|
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
|
let titleSize = self.titleView.update(string: self.title, fontSize: 16.0, fontWeight: 0.3, alignment: .center, color: .white, constrainedWidth: params.constrainedWidth - params.sideInset * 2.0 - 16.0 * 2.0, transition: transition)
|
||||||
|
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) * 0.5), y: 78.0), size: titleSize)
|
||||||
|
transition.setFrame(view: self.titleView, frame: titleFrame)
|
||||||
|
|
||||||
|
let textSize = self.textView.update(string: self.text, fontSize: 16.0, fontWeight: 0.0, alignment: .center, color: .white, constrainedWidth: params.constrainedWidth - params.sideInset * 2.0 - 16.0 * 2.0, transition: transition)
|
||||||
|
let textFrame = CGRect(origin: CGPoint(x: floor((size.width - textSize.width) * 0.5), y: titleFrame.maxY + 10.0), size: textSize)
|
||||||
|
transition.setFrame(view: self.textView, frame: textFrame)
|
||||||
|
|
||||||
|
let buttonHeight: CGFloat = 56.0
|
||||||
|
let buttonFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - buttonHeight), size: CGSize(width: size.width, height: buttonHeight))
|
||||||
|
transition.setFrame(view: self.actionButton, frame: buttonFrame)
|
||||||
|
|
||||||
|
let actionTitleSize = self.actionTitleView.update(string: "OK", fontSize: 19.0, fontWeight: 0.3, color: .white, constrainedWidth: size.width, transition: transition)
|
||||||
|
let actionTitleFrame = CGRect(origin: CGPoint(x: floor((buttonFrame.width - actionTitleSize.width) * 0.5), y: floor((buttonFrame.height - actionTitleSize.height) * 0.5)), size: actionTitleSize)
|
||||||
|
transition.setFrame(view: self.actionTitleView, frame: actionTitleFrame)
|
||||||
|
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,47 +1,90 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
import Display
|
import Display
|
||||||
|
import ComponentFlow
|
||||||
|
|
||||||
final class KeyEmojiView: UIView {
|
final class KeyEmojiView: HighlightTrackingButton {
|
||||||
|
private struct Params: Equatable {
|
||||||
|
var isExpanded: Bool
|
||||||
|
|
||||||
|
init(isExpanded: Bool) {
|
||||||
|
self.isExpanded = isExpanded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct Layout: Equatable {
|
||||||
|
var params: Params
|
||||||
|
var size: CGSize
|
||||||
|
|
||||||
|
init(params: Params, size: CGSize) {
|
||||||
|
self.params = params
|
||||||
|
self.size = size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let emoji: [String]
|
||||||
private let emojiViews: [TextView]
|
private let emojiViews: [TextView]
|
||||||
|
|
||||||
let size: CGSize
|
var pressAction: (() -> Void)?
|
||||||
|
|
||||||
|
private var currentLayout: Layout?
|
||||||
|
|
||||||
init(emoji: [String]) {
|
init(emoji: [String]) {
|
||||||
self.emojiViews = emoji.map { emoji in
|
self.emoji = emoji
|
||||||
|
self.emojiViews = emoji.map { _ in
|
||||||
TextView()
|
TextView()
|
||||||
}
|
}
|
||||||
|
|
||||||
let itemSpacing: CGFloat = 3.0
|
|
||||||
|
|
||||||
var height: CGFloat = 0.0
|
|
||||||
var nextX = 0.0
|
|
||||||
for i in 0 ..< self.emojiViews.count {
|
|
||||||
if nextX != 0.0 {
|
|
||||||
nextX += itemSpacing
|
|
||||||
}
|
|
||||||
let emojiView = self.emojiViews[i]
|
|
||||||
let itemSize = emojiView.update(string: emoji[i], fontSize: 16.0, fontWeight: 0.0, color: .white, constrainedWidth: 100.0, transition: .immediate)
|
|
||||||
if height == 0.0 {
|
|
||||||
height = itemSize.height
|
|
||||||
}
|
|
||||||
emojiView.frame = CGRect(origin: CGPoint(x: nextX, y: 0.0), size: itemSize)
|
|
||||||
nextX += itemSize.width
|
|
||||||
}
|
|
||||||
|
|
||||||
self.size = CGSize(width: nextX, height: height)
|
|
||||||
|
|
||||||
super.init(frame: CGRect())
|
super.init(frame: CGRect())
|
||||||
|
|
||||||
for emojiView in self.emojiViews {
|
for emojiView in self.emojiViews {
|
||||||
|
emojiView.contentMode = .scaleToFill
|
||||||
|
emojiView.isUserInteractionEnabled = false
|
||||||
self.addSubview(emojiView)
|
self.addSubview(emojiView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.internalHighligthedChanged = { [weak self] highlighted in
|
||||||
|
if let self, self.bounds.width > 0.0 {
|
||||||
|
let topScale: CGFloat = (self.bounds.width - 8.0) / self.bounds.width
|
||||||
|
let maxScale: CGFloat = (self.bounds.width + 2.0) / self.bounds.width
|
||||||
|
|
||||||
|
if highlighted {
|
||||||
|
self.layer.removeAnimation(forKey: "opacity")
|
||||||
|
self.layer.removeAnimation(forKey: "transform")
|
||||||
|
let transition = Transition(animation: .curve(duration: 0.15, curve: .easeInOut))
|
||||||
|
transition.setScale(layer: self.layer, scale: topScale)
|
||||||
|
} else {
|
||||||
|
let t = self.layer.presentation()?.transform ?? layer.transform
|
||||||
|
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
|
||||||
|
|
||||||
|
let transition = Transition(animation: .none)
|
||||||
|
transition.setScale(layer: self.layer, scale: 1.0)
|
||||||
|
|
||||||
|
self.layer.animateScale(from: currentScale, to: maxScale, duration: 0.13, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, completion: { [weak self] completed in
|
||||||
|
guard let self, completed else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.layer.animateScale(from: maxScale, to: 1.0, duration: 0.1, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
|
return super.hitTest(point, with: event)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func pressed() {
|
||||||
|
self.pressAction?()
|
||||||
|
}
|
||||||
|
|
||||||
func animateIn() {
|
func animateIn() {
|
||||||
for i in 0 ..< self.emojiViews.count {
|
for i in 0 ..< self.emojiViews.count {
|
||||||
let emojiView = self.emojiViews[i]
|
let emojiView = self.emojiViews[i]
|
||||||
@ -49,4 +92,37 @@ final class KeyEmojiView: UIView {
|
|||||||
emojiView.layer.animatePosition(from: CGPoint(x: -CGFloat(self.emojiViews.count - 1 - i) * 30.0, y: 0.0), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
emojiView.layer.animatePosition(from: CGPoint(x: -CGFloat(self.emojiViews.count - 1 - i) * 30.0, y: 0.0), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func update(isExpanded: Bool, transition: Transition) -> CGSize {
|
||||||
|
let params = Params(isExpanded: isExpanded)
|
||||||
|
if let currentLayout = self.currentLayout, currentLayout.params == params {
|
||||||
|
return currentLayout.size
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = self.update(params: params, transition: transition)
|
||||||
|
self.currentLayout = Layout(params: params, size: size)
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
private func update(params: Params, transition: Transition) -> CGSize {
|
||||||
|
let itemSpacing: CGFloat = 3.0
|
||||||
|
|
||||||
|
var height: CGFloat = 0.0
|
||||||
|
var nextX = 0.0
|
||||||
|
for i in 0 ..< self.emojiViews.count {
|
||||||
|
if nextX != 0.0 {
|
||||||
|
nextX += itemSpacing
|
||||||
|
}
|
||||||
|
let emojiView = self.emojiViews[i]
|
||||||
|
let itemSize = emojiView.update(string: emoji[i], fontSize: params.isExpanded ? 40.0 : 16.0, fontWeight: 0.0, color: .white, constrainedWidth: 100.0, transition: transition)
|
||||||
|
if height == 0.0 {
|
||||||
|
height = itemSize.height
|
||||||
|
}
|
||||||
|
let itemFrame = CGRect(origin: CGPoint(x: nextX, y: 0.0), size: itemSize)
|
||||||
|
transition.setFrame(view: emojiView, frame: itemFrame)
|
||||||
|
nextX += itemSize.width
|
||||||
|
}
|
||||||
|
|
||||||
|
return CGSize(width: nextX, height: height)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ final class TextView: UIView {
|
|||||||
var fontSize: CGFloat
|
var fontSize: CGFloat
|
||||||
var fontWeight: CGFloat
|
var fontWeight: CGFloat
|
||||||
var monospacedDigits: Bool
|
var monospacedDigits: Bool
|
||||||
|
var alignment: NSTextAlignment
|
||||||
var constrainedWidth: CGFloat
|
var constrainedWidth: CGFloat
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,8 +44,8 @@ final class TextView: UIView {
|
|||||||
return super.action(for: layer, forKey: event)
|
return super.action(for: layer, forKey: event)
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(string: String, fontSize: CGFloat, fontWeight: CGFloat, monospacedDigits: Bool = false, color: UIColor, constrainedWidth: CGFloat, transition: Transition) -> CGSize {
|
func update(string: String, fontSize: CGFloat, fontWeight: CGFloat, monospacedDigits: Bool = false, alignment: NSTextAlignment = .natural, color: UIColor, constrainedWidth: CGFloat, transition: Transition) -> CGSize {
|
||||||
let params = Params(string: string, fontSize: fontSize, fontWeight: fontWeight, monospacedDigits: monospacedDigits, constrainedWidth: constrainedWidth)
|
let params = Params(string: string, fontSize: fontSize, fontWeight: fontWeight, monospacedDigits: monospacedDigits, alignment: alignment, constrainedWidth: constrainedWidth)
|
||||||
if let layoutState = self.layoutState, layoutState.params == params {
|
if let layoutState = self.layoutState, layoutState.params == params {
|
||||||
return layoutState.size
|
return layoutState.size
|
||||||
}
|
}
|
||||||
@ -56,9 +57,13 @@ final class TextView: UIView {
|
|||||||
font = UIFont.systemFont(ofSize: fontSize, weight: UIFont.Weight(fontWeight))
|
font = UIFont.systemFont(ofSize: fontSize, weight: UIFont.Weight(fontWeight))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let paragraphStyle = NSMutableParagraphStyle()
|
||||||
|
paragraphStyle.alignment = alignment
|
||||||
|
paragraphStyle.lineSpacing = 0.6
|
||||||
let attributedString = NSAttributedString(string: string, attributes: [
|
let attributedString = NSAttributedString(string: string, attributes: [
|
||||||
.font: font,
|
.font: font,
|
||||||
.foregroundColor: color,
|
.foregroundColor: color,
|
||||||
|
.paragraphStyle: paragraphStyle
|
||||||
])
|
])
|
||||||
let stringBounds = attributedString.boundingRect(with: CGSize(width: constrainedWidth, height: 200.0), options: .usesLineFragmentOrigin, context: nil)
|
let stringBounds = attributedString.boundingRect(with: CGSize(width: constrainedWidth, height: 200.0), options: .usesLineFragmentOrigin, context: nil)
|
||||||
let stringSize = CGSize(width: ceil(stringBounds.width), height: ceil(stringBounds.height))
|
let stringSize = CGSize(width: ceil(stringBounds.width), height: ceil(stringBounds.height))
|
||||||
|
|||||||
@ -39,6 +39,28 @@ final class MirroringLayer: SimpleLayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override var anchorPoint: CGPoint {
|
||||||
|
get {
|
||||||
|
return super.anchorPoint
|
||||||
|
} set(value) {
|
||||||
|
if let targetLayer = self.targetLayer {
|
||||||
|
targetLayer.anchorPoint = value
|
||||||
|
}
|
||||||
|
super.anchorPoint = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var anchorPointZ: CGFloat {
|
||||||
|
get {
|
||||||
|
return super.anchorPointZ
|
||||||
|
} set(value) {
|
||||||
|
if let targetLayer = self.targetLayer {
|
||||||
|
targetLayer.anchorPointZ = value
|
||||||
|
}
|
||||||
|
super.anchorPointZ = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override var opacity: Float {
|
override var opacity: Float {
|
||||||
get {
|
get {
|
||||||
return super.opacity
|
return super.opacity
|
||||||
|
|||||||
@ -127,6 +127,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
private var weakSignalView: WeakSignalView?
|
private var weakSignalView: WeakSignalView?
|
||||||
|
|
||||||
private var emojiView: KeyEmojiView?
|
private var emojiView: KeyEmojiView?
|
||||||
|
private var emojiExpandedInfoView: EmojiExpandedInfoView?
|
||||||
|
|
||||||
private let videoContainerBackgroundView: RoundedCornersView
|
private let videoContainerBackgroundView: RoundedCornersView
|
||||||
private let overlayContentsVideoContainerBackgroundView: RoundedCornersView
|
private let overlayContentsVideoContainerBackgroundView: RoundedCornersView
|
||||||
@ -139,6 +140,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
private var activeLocalVideoSource: VideoSource?
|
private var activeLocalVideoSource: VideoSource?
|
||||||
private var waitingForFirstLocalVideoFrameDisposable: Disposable?
|
private var waitingForFirstLocalVideoFrameDisposable: Disposable?
|
||||||
|
|
||||||
|
private var isEmojiKeyExpanded: Bool = false
|
||||||
private var areControlsHidden: Bool = false
|
private var areControlsHidden: Bool = false
|
||||||
private var swapLocalAndRemoteVideo: Bool = false
|
private var swapLocalAndRemoteVideo: Bool = false
|
||||||
|
|
||||||
@ -458,6 +460,53 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
}
|
}
|
||||||
let contentBottomInset = self.buttonGroupView.update(size: params.size, insets: params.insets, controlsHidden: currentAreControlsHidden, buttons: buttons, transition: transition)
|
let contentBottomInset = self.buttonGroupView.update(size: params.size, insets: params.insets, controlsHidden: currentAreControlsHidden, buttons: buttons, transition: transition)
|
||||||
|
|
||||||
|
if self.isEmojiKeyExpanded {
|
||||||
|
let emojiExpandedInfoView: EmojiExpandedInfoView
|
||||||
|
var emojiExpandedInfoTransition = transition
|
||||||
|
var animateIn = false
|
||||||
|
if let current = self.emojiExpandedInfoView {
|
||||||
|
emojiExpandedInfoView = current
|
||||||
|
} else {
|
||||||
|
emojiExpandedInfoTransition = emojiExpandedInfoTransition.withAnimation(.none)
|
||||||
|
animateIn = true
|
||||||
|
|
||||||
|
emojiExpandedInfoView = EmojiExpandedInfoView(title: "This call is end-to-end encrypted", text: "If the emoji on Emma's screen are the same, this call is 100% secure.")
|
||||||
|
self.emojiExpandedInfoView = emojiExpandedInfoView
|
||||||
|
emojiExpandedInfoView.layer.anchorPoint = CGPoint(x: 1.0, y: 0.0)
|
||||||
|
if let emojiView = self.emojiView {
|
||||||
|
self.insertSubview(emojiExpandedInfoView, belowSubview: emojiView)
|
||||||
|
} else {
|
||||||
|
self.addSubview(emojiExpandedInfoView)
|
||||||
|
}
|
||||||
|
|
||||||
|
emojiExpandedInfoView.closeAction = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.isEmojiKeyExpanded = false
|
||||||
|
self.update(transition: .spring(duration: 0.4))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let emojiExpandedInfoSize = emojiExpandedInfoView.update(constrainedWidth: params.size.width, sideInset: params.insets.left + 44.0, transition: emojiExpandedInfoTransition)
|
||||||
|
let emojiExpandedInfoFrame = CGRect(origin: CGPoint(x: floor((params.size.width - emojiExpandedInfoSize.width) * 0.5), y: params.insets.top + 73.0), size: emojiExpandedInfoSize)
|
||||||
|
emojiExpandedInfoTransition.setPosition(view: emojiExpandedInfoView, position: CGPoint(x: emojiExpandedInfoFrame.maxX, y: emojiExpandedInfoFrame.minY))
|
||||||
|
emojiExpandedInfoTransition.setBounds(view: emojiExpandedInfoView, bounds: CGRect(origin: CGPoint(), size: emojiExpandedInfoFrame.size))
|
||||||
|
|
||||||
|
if animateIn {
|
||||||
|
transition.animateAlpha(view: emojiExpandedInfoView, from: 0.0, to: 1.0)
|
||||||
|
transition.animateScale(view: emojiExpandedInfoView, from: 0.001, to: 1.0)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let emojiExpandedInfoView = self.emojiExpandedInfoView {
|
||||||
|
self.emojiExpandedInfoView = nil
|
||||||
|
transition.setAlpha(view: emojiExpandedInfoView, alpha: 0.0, completion: { [weak emojiExpandedInfoView] _ in
|
||||||
|
emojiExpandedInfoView?.removeFromSuperview()
|
||||||
|
})
|
||||||
|
transition.setScale(view: emojiExpandedInfoView, scale: 0.001)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if case let .active(activeState) = params.state.lifecycleState {
|
if case let .active(activeState) = params.state.lifecycleState {
|
||||||
let emojiView: KeyEmojiView
|
let emojiView: KeyEmojiView
|
||||||
var emojiTransition = transition
|
var emojiTransition = transition
|
||||||
@ -469,6 +518,15 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
emojiAlphaTransition = genericAlphaTransition.withAnimation(.none)
|
emojiAlphaTransition = genericAlphaTransition.withAnimation(.none)
|
||||||
emojiView = KeyEmojiView(emoji: activeState.emojiKey)
|
emojiView = KeyEmojiView(emoji: activeState.emojiKey)
|
||||||
self.emojiView = emojiView
|
self.emojiView = emojiView
|
||||||
|
emojiView.pressAction = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !self.isEmojiKeyExpanded {
|
||||||
|
self.isEmojiKeyExpanded = true
|
||||||
|
self.update(transition: .spring(duration: 0.4))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if emojiView.superview == nil {
|
if emojiView.superview == nil {
|
||||||
self.addSubview(emojiView)
|
self.addSubview(emojiView)
|
||||||
@ -476,14 +534,25 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
emojiView.animateIn()
|
emojiView.animateIn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let emojiY: CGFloat
|
emojiView.isUserInteractionEnabled = !self.isEmojiKeyExpanded
|
||||||
if currentAreControlsHidden {
|
|
||||||
emojiY = -8.0 - emojiView.size.height
|
let emojiViewSize = emojiView.update(isExpanded: self.isEmojiKeyExpanded, transition: emojiTransition)
|
||||||
|
|
||||||
|
if self.isEmojiKeyExpanded {
|
||||||
|
let emojiViewFrame = CGRect(origin: CGPoint(x: floor((params.size.width - emojiViewSize.width) * 0.5), y: params.insets.top + 93.0), size: emojiViewSize)
|
||||||
|
emojiTransition.setFrame(view: emojiView, frame: emojiViewFrame)
|
||||||
} else {
|
} else {
|
||||||
emojiY = params.insets.top + 12.0
|
let emojiY: CGFloat
|
||||||
|
if currentAreControlsHidden {
|
||||||
|
emojiY = -8.0 - emojiViewSize.height
|
||||||
|
} else {
|
||||||
|
emojiY = params.insets.top + 12.0
|
||||||
|
}
|
||||||
|
emojiTransition.setFrame(view: emojiView, frame: CGRect(origin: CGPoint(x: params.size.width - params.insets.right - 12.0 - emojiViewSize.width, y: emojiY), size: emojiViewSize))
|
||||||
|
emojiAlphaTransition.setAlpha(view: emojiView, alpha: currentAreControlsHidden ? 0.0 : 1.0)
|
||||||
}
|
}
|
||||||
emojiTransition.setFrame(view: emojiView, frame: CGRect(origin: CGPoint(x: params.size.width - params.insets.right - 12.0 - emojiView.size.width, y: emojiY), size: emojiView.size))
|
|
||||||
emojiAlphaTransition.setAlpha(view: emojiView, alpha: currentAreControlsHidden ? 0.0 : 1.0)
|
emojiAlphaTransition.setAlpha(view: emojiView, alpha: 1.0)
|
||||||
} else {
|
} else {
|
||||||
if let emojiView = self.emojiView {
|
if let emojiView = self.emojiView {
|
||||||
self.emojiView = nil
|
self.emojiView = nil
|
||||||
@ -666,6 +735,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
transition.setPosition(layer: self.avatarLayer, position: avatarFrame.center)
|
transition.setPosition(layer: self.avatarLayer, position: avatarFrame.center)
|
||||||
transition.setBounds(layer: self.avatarLayer, bounds: CGRect(origin: CGPoint(), size: avatarFrame.size))
|
transition.setBounds(layer: self.avatarLayer, bounds: CGRect(origin: CGPoint(), size: avatarFrame.size))
|
||||||
self.avatarLayer.update(size: collapsedAvatarFrame.size, isExpanded: havePrimaryVideo, cornerRadius: avatarCornerRadius, transition: transition)
|
self.avatarLayer.update(size: collapsedAvatarFrame.size, isExpanded: havePrimaryVideo, cornerRadius: avatarCornerRadius, transition: transition)
|
||||||
|
transition.setAlpha(layer: self.avatarLayer, alpha: (self.isEmojiKeyExpanded && !havePrimaryVideo) ? 0.0 : 1.0)
|
||||||
|
|
||||||
transition.setPosition(view: self.videoContainerBackgroundView, position: avatarFrame.center)
|
transition.setPosition(view: self.videoContainerBackgroundView, position: avatarFrame.center)
|
||||||
transition.setBounds(view: self.videoContainerBackgroundView, bounds: CGRect(origin: CGPoint(), size: avatarFrame.size))
|
transition.setBounds(view: self.videoContainerBackgroundView, bounds: CGRect(origin: CGPoint(), size: avatarFrame.size))
|
||||||
@ -693,7 +763,7 @@ public final class PrivateCallScreen: OverlayMaskContainerView {
|
|||||||
transition.setAlpha(layer: self.blobLayer, alpha: 0.0)
|
transition.setAlpha(layer: self.blobLayer, alpha: 0.0)
|
||||||
default:
|
default:
|
||||||
titleString = params.state.name
|
titleString = params.state.name
|
||||||
transition.setAlpha(layer: self.blobLayer, alpha: 1.0)
|
transition.setAlpha(layer: self.blobLayer, alpha: (self.isEmojiKeyExpanded && !havePrimaryVideo) ? 0.0 : 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
let titleSize = self.titleView.update(
|
let titleSize = self.titleView.update(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user