mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Stories
This commit is contained in:
parent
7dd76ef329
commit
d39cfca5a5
@ -178,6 +178,7 @@ private final class ChatTextInputMediaRecordingButtonPresenter : NSObject, TGMod
|
|||||||
public final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButton, TGModernConversationInputMicButtonDelegate {
|
public final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButton, TGModernConversationInputMicButtonDelegate {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private var theme: PresentationTheme
|
private var theme: PresentationTheme
|
||||||
|
private let useDarkTheme: Bool
|
||||||
private let strings: PresentationStrings
|
private let strings: PresentationStrings
|
||||||
|
|
||||||
public var mode: ChatTextInputMediaRecordingButtonMode = .audio
|
public var mode: ChatTextInputMediaRecordingButtonMode = .audio
|
||||||
@ -293,16 +294,17 @@ public final class ChatTextInputMediaRecordingButton: TGModernConversationInputM
|
|||||||
if let current = self.micLockValue {
|
if let current = self.micLockValue {
|
||||||
return current
|
return current
|
||||||
} else {
|
} else {
|
||||||
let lockView = LockView(frame: CGRect(origin: CGPoint(), size: CGSize(width: 40.0, height: 60.0)), theme: self.theme, strings: self.strings)
|
let lockView = LockView(frame: CGRect(origin: CGPoint(), size: CGSize(width: 40.0, height: 60.0)), theme: self.theme, useDarkTheme: self.useDarkTheme, strings: self.strings)
|
||||||
lockView.addTarget(self, action: #selector(handleStopTap), for: .touchUpInside)
|
lockView.addTarget(self, action: #selector(handleStopTap), for: .touchUpInside)
|
||||||
self.micLockValue = lockView
|
self.micLockValue = lockView
|
||||||
return lockView
|
return lockView
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, presentController: @escaping (ViewController) -> Void) {
|
public init(context: AccountContext, theme: PresentationTheme, useDarkTheme: Bool = false, strings: PresentationStrings, presentController: @escaping (ViewController) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
self.useDarkTheme = useDarkTheme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.animationView = ComponentView<Empty>()
|
self.animationView = ComponentView<Empty>()
|
||||||
self.presentController = presentController
|
self.presentController = presentController
|
||||||
@ -382,7 +384,7 @@ public final class ChatTextInputMediaRecordingButton: TGModernConversationInputM
|
|||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(LottieComponent(
|
component: AnyComponent(LottieComponent(
|
||||||
content: LottieComponent.AppBundleContent(name: animationName),
|
content: LottieComponent.AppBundleContent(name: animationName),
|
||||||
color: self.theme.chat.inputPanel.panelControlColor.blitOver(self.theme.chat.inputPanel.inputBackgroundColor, alpha: 1.0)
|
color: self.useDarkTheme ? .white : self.theme.chat.inputPanel.panelControlColor.blitOver(self.theme.chat.inputPanel.inputBackgroundColor, alpha: 1.0)
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: animationFrame.size
|
containerSize: animationFrame.size
|
||||||
@ -407,7 +409,7 @@ public final class ChatTextInputMediaRecordingButton: TGModernConversationInputM
|
|||||||
self.updateAnimation(previousMode: self.mode)
|
self.updateAnimation(previousMode: self.mode)
|
||||||
|
|
||||||
self.pallete = legacyInputMicPalette(from: theme)
|
self.pallete = legacyInputMicPalette(from: theme)
|
||||||
self.micDecorationValue?.setColor(self.theme.chat.inputPanel.actionControlFillColor)
|
self.micDecorationValue?.setColor( self.theme.chat.inputPanel.actionControlFillColor)
|
||||||
(self.micLockValue as? LockView)?.updateTheme(theme)
|
(self.micLockValue as? LockView)?.updateTheme(theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import Lottie
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
|
||||||
final class LockView: UIButton, TGModernConversationInputMicButtonLock {
|
final class LockView: UIButton, TGModernConversationInputMicButtonLock {
|
||||||
//private var colorCallbacks = [LOTValueDelegate]()
|
private let useDarkTheme: Bool
|
||||||
|
|
||||||
private let idleView: AnimationView = {
|
private let idleView: AnimationView = {
|
||||||
guard let url = getAppBundle().url(forResource: "LockWait", withExtension: "json"), let animation = Animation.filepath(url.path)
|
guard let url = getAppBundle().url(forResource: "LockWait", withExtension: "json"), let animation = Animation.filepath(url.path)
|
||||||
@ -28,7 +28,9 @@ final class LockView: UIButton, TGModernConversationInputMicButtonLock {
|
|||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
|
|
||||||
init(frame: CGRect, theme: PresentationTheme, strings: PresentationStrings) {
|
init(frame: CGRect, theme: PresentationTheme, useDarkTheme: Bool = false, strings: PresentationStrings) {
|
||||||
|
self.useDarkTheme = useDarkTheme
|
||||||
|
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
accessibilityLabel = strings.VoiceOver_Recording_StopAndPreview
|
accessibilityLabel = strings.VoiceOver_Recording_StopAndPreview
|
||||||
@ -60,8 +62,6 @@ final class LockView: UIButton, TGModernConversationInputMicButtonLock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateTheme(_ theme: PresentationTheme) {
|
func updateTheme(_ theme: PresentationTheme) {
|
||||||
//colorCallbacks.removeAll()
|
|
||||||
|
|
||||||
[
|
[
|
||||||
"Rectangle.Заливка 1": theme.chat.inputPanel.panelBackgroundColor,
|
"Rectangle.Заливка 1": theme.chat.inputPanel.panelBackgroundColor,
|
||||||
"Rectangle.Rectangle.Обводка 1": theme.chat.inputPanel.panelControlAccentColor,
|
"Rectangle.Rectangle.Обводка 1": theme.chat.inputPanel.panelControlAccentColor,
|
||||||
@ -69,9 +69,6 @@ final class LockView: UIButton, TGModernConversationInputMicButtonLock {
|
|||||||
"Path 4.Path 4.Обводка 1": theme.chat.inputPanel.panelControlAccentColor
|
"Path 4.Path 4.Обводка 1": theme.chat.inputPanel.panelControlAccentColor
|
||||||
].forEach { key, value in
|
].forEach { key, value in
|
||||||
idleView.setValueProvider(ColorValueProvider(value.lottieColorValue), keypath: AnimationKeypath(keypath: "\(key).Color"))
|
idleView.setValueProvider(ColorValueProvider(value.lottieColorValue), keypath: AnimationKeypath(keypath: "\(key).Color"))
|
||||||
/*let colorCallback = LOTColorValueCallback(color: value.cgColor)
|
|
||||||
self.colorCallbacks.append(colorCallback)
|
|
||||||
idleView.setValueDelegate(colorCallback, for: LOTKeypath(string: "\(key).Color"))*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[
|
[
|
||||||
@ -82,9 +79,6 @@ final class LockView: UIButton, TGModernConversationInputMicButtonLock {
|
|||||||
"Path 4.Path 4.Обводка 1": theme.chat.inputPanel.panelControlAccentColor
|
"Path 4.Path 4.Обводка 1": theme.chat.inputPanel.panelControlAccentColor
|
||||||
].forEach { key, value in
|
].forEach { key, value in
|
||||||
lockingView.setValueProvider(ColorValueProvider(value.lottieColorValue), keypath: AnimationKeypath(keypath: "\(key).Color"))
|
lockingView.setValueProvider(ColorValueProvider(value.lottieColorValue), keypath: AnimationKeypath(keypath: "\(key).Color"))
|
||||||
/*let colorCallback = LOTColorValueCallback(color: value.cgColor)
|
|
||||||
self.colorCallbacks.append(colorCallback)
|
|
||||||
lockingView.setValueDelegate(colorCallback, for: LOTKeypath(string: "\(key).Color"))*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,16 +55,24 @@ public final class LottieComponent: Component {
|
|||||||
return EmptyDisposable
|
return EmptyDisposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum StartingPosition {
|
||||||
|
case begin
|
||||||
|
case end
|
||||||
|
}
|
||||||
|
|
||||||
public let content: Content
|
public let content: Content
|
||||||
public let color: UIColor
|
public let color: UIColor
|
||||||
|
public let startingPosition: StartingPosition
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
content: Content,
|
content: Content,
|
||||||
color: UIColor
|
color: UIColor,
|
||||||
|
startingPosition: StartingPosition = .end
|
||||||
) {
|
) {
|
||||||
self.content = content
|
self.content = content
|
||||||
self.color = color
|
self.color = color
|
||||||
|
self.startingPosition = startingPosition
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: LottieComponent, rhs: LottieComponent) -> Bool {
|
public static func ==(lhs: LottieComponent, rhs: LottieComponent) -> Bool {
|
||||||
@ -74,6 +82,9 @@ public final class LottieComponent: Component {
|
|||||||
if lhs.color != rhs.color {
|
if lhs.color != rhs.color {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.startingPosition != rhs.startingPosition {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,6 +93,7 @@ public final class LottieComponent: Component {
|
|||||||
private var component: LottieComponent?
|
private var component: LottieComponent?
|
||||||
|
|
||||||
private var scheduledPlayOnce: Bool = false
|
private var scheduledPlayOnce: Bool = false
|
||||||
|
private var playOnceCompletion: (() -> Void)?
|
||||||
private var animationInstance: LottieInstance?
|
private var animationInstance: LottieInstance?
|
||||||
private var currentDisplaySize: CGSize?
|
private var currentDisplaySize: CGSize?
|
||||||
private var currentContentDisposable: Disposable?
|
private var currentContentDisposable: Disposable?
|
||||||
@ -147,7 +159,9 @@ public final class LottieComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func playOnce(delay: Double = 0.0) {
|
public func playOnce(delay: Double = 0.0, completion: (() -> Void)? = nil) {
|
||||||
|
self.playOnceCompletion = completion
|
||||||
|
|
||||||
guard let _ = self.animationInstance else {
|
guard let _ = self.animationInstance else {
|
||||||
self.scheduledPlayOnce = true
|
self.scheduledPlayOnce = true
|
||||||
return
|
return
|
||||||
@ -194,13 +208,18 @@ public final class LottieComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadAnimation(data: Data, cacheKey: String?) {
|
private func loadAnimation(data: Data, cacheKey: String?, startingPosition: StartingPosition) {
|
||||||
self.animationInstance = LottieInstance(data: data, fitzModifier: .none, colorReplacements: nil, cacheKey: cacheKey ?? "")
|
self.animationInstance = LottieInstance(data: data, fitzModifier: .none, colorReplacements: nil, cacheKey: cacheKey ?? "")
|
||||||
if self.scheduledPlayOnce {
|
if self.scheduledPlayOnce {
|
||||||
self.scheduledPlayOnce = false
|
self.scheduledPlayOnce = false
|
||||||
self.playOnce()
|
self.playOnce()
|
||||||
} else if let animationInstance = self.animationInstance {
|
} else if let animationInstance = self.animationInstance {
|
||||||
self.currentFrame = Int(animationInstance.frameCount - 1)
|
switch startingPosition {
|
||||||
|
case .begin:
|
||||||
|
self.currentFrame = 0
|
||||||
|
case .end:
|
||||||
|
self.currentFrame = Int(animationInstance.frameCount - 1)
|
||||||
|
}
|
||||||
self.updateImage()
|
self.updateImage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,12 +241,21 @@ public final class LottieComponent: Component {
|
|||||||
|
|
||||||
let timestamp = CACurrentMediaTime()
|
let timestamp = CACurrentMediaTime()
|
||||||
if currentFrameStartTime + timestamp >= secondsPerFrame * 0.9 {
|
if currentFrameStartTime + timestamp >= secondsPerFrame * 0.9 {
|
||||||
self.currentFrame += 1
|
var advanceFrameCount = 1
|
||||||
|
if animationInstance.frameRate == 360 {
|
||||||
|
advanceFrameCount = 6
|
||||||
|
}
|
||||||
|
self.currentFrame += advanceFrameCount
|
||||||
if self.currentFrame >= Int(animationInstance.frameCount) - 1 {
|
if self.currentFrame >= Int(animationInstance.frameCount) - 1 {
|
||||||
self.currentFrame = Int(animationInstance.frameCount) - 1
|
self.currentFrame = Int(animationInstance.frameCount) - 1
|
||||||
self.updateImage()
|
self.updateImage()
|
||||||
self.displayLink?.invalidate()
|
self.displayLink?.invalidate()
|
||||||
self.displayLink = nil
|
self.displayLink = nil
|
||||||
|
|
||||||
|
if let playOnceCompletion = self.playOnceCompletion {
|
||||||
|
self.playOnceCompletion = nil
|
||||||
|
playOnceCompletion()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.currentFrameStartTime = timestamp
|
self.currentFrameStartTime = timestamp
|
||||||
self.updateImage()
|
self.updateImage()
|
||||||
@ -271,10 +299,10 @@ public final class LottieComponent: Component {
|
|||||||
let content = component.content
|
let content = component.content
|
||||||
self.currentContentDisposable = component.content.load { [weak self, weak content] data, cacheKey in
|
self.currentContentDisposable = component.content.load { [weak self, weak content] data, cacheKey in
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
guard let self, self.component?.content == content else {
|
guard let self, let component = self.component, component.content == content else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.loadAnimation(data: data, cacheKey: cacheKey)
|
self.loadAnimation(data: data, cacheKey: cacheKey, startingPosition: component.startingPosition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if redrawImage {
|
} else if redrawImage {
|
||||||
|
@ -16,9 +16,11 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/TextFieldComponent",
|
"//submodules/TelegramUI/Components/TextFieldComponent",
|
||||||
"//submodules/Components/BundleIconComponent",
|
"//submodules/Components/BundleIconComponent",
|
||||||
"//submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton",
|
"//submodules/TelegramUI/Components/ChatTextInputMediaRecordingButton",
|
||||||
|
"//submodules/TelegramUI/Components/LottieComponent",
|
||||||
"//submodules/AccountContext",
|
"//submodules/AccountContext",
|
||||||
"//submodules/TelegramPresentationData",
|
"//submodules/TelegramPresentationData",
|
||||||
"//submodules/SSignalKit/SwiftSignalKit",
|
"//submodules/SSignalKit/SwiftSignalKit",
|
||||||
|
"//submodules/Components/HierarchyTrackingLayer",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -9,6 +9,8 @@ import AccountContext
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import ChatPresentationInterfaceState
|
import ChatPresentationInterfaceState
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
|
import LottieComponent
|
||||||
|
import HierarchyTrackingLayer
|
||||||
|
|
||||||
public final class MediaRecordingPanelComponent: Component {
|
public final class MediaRecordingPanelComponent: Component {
|
||||||
public let audioRecorder: ManagedAudioRecorder?
|
public let audioRecorder: ManagedAudioRecorder?
|
||||||
@ -42,10 +44,15 @@ public final class MediaRecordingPanelComponent: Component {
|
|||||||
private var component: MediaRecordingPanelComponent?
|
private var component: MediaRecordingPanelComponent?
|
||||||
private weak var state: EmptyComponentState?
|
private weak var state: EmptyComponentState?
|
||||||
|
|
||||||
private let indicatorView: UIImageView
|
private let trackingLayer: HierarchyTrackingLayer
|
||||||
|
|
||||||
|
private let indicator = ComponentView<Empty>()
|
||||||
|
|
||||||
|
private let cancelContainerView: UIView
|
||||||
private let cancelIconView: UIImageView
|
private let cancelIconView: UIImageView
|
||||||
private let cancelText = ComponentView<Empty>()
|
private let cancelText = ComponentView<Empty>()
|
||||||
|
|
||||||
|
private let timerFont: UIFont
|
||||||
private let timerText = ComponentView<Empty>()
|
private let timerText = ComponentView<Empty>()
|
||||||
|
|
||||||
private var timerTextDisposable: Disposable?
|
private var timerTextDisposable: Disposable?
|
||||||
@ -53,13 +60,26 @@ public final class MediaRecordingPanelComponent: Component {
|
|||||||
private var timerTextValue: String = "0:00,00"
|
private var timerTextValue: String = "0:00,00"
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
self.indicatorView = UIImageView()
|
self.trackingLayer = HierarchyTrackingLayer()
|
||||||
self.cancelIconView = UIImageView()
|
self.cancelIconView = UIImageView()
|
||||||
|
|
||||||
|
self.timerFont = Font.with(size: 15.0, design: .camera, traits: .monospacedNumbers)
|
||||||
|
|
||||||
|
self.cancelContainerView = UIView()
|
||||||
|
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
self.addSubview(self.indicatorView)
|
self.layer.addSublayer(self.trackingLayer)
|
||||||
self.addSubview(self.cancelIconView)
|
|
||||||
|
self.cancelContainerView.addSubview(self.cancelIconView)
|
||||||
|
self.addSubview(self.cancelContainerView)
|
||||||
|
|
||||||
|
self.trackingLayer.didEnterHierarchy = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.updateAnimations()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
@ -70,6 +90,72 @@ public final class MediaRecordingPanelComponent: Component {
|
|||||||
self.timerTextDisposable?.dispose()
|
self.timerTextDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updateAnimations() {
|
||||||
|
if let indicatorView = self.indicator.view {
|
||||||
|
if indicatorView.layer.animation(forKey: "recording") == nil {
|
||||||
|
let animation = CAKeyframeAnimation(keyPath: "opacity")
|
||||||
|
animation.values = [1.0 as NSNumber, 1.0 as NSNumber, 0.0 as NSNumber]
|
||||||
|
animation.keyTimes = [0.0 as NSNumber, 0.4546 as NSNumber, 0.9091 as NSNumber, 1 as NSNumber]
|
||||||
|
animation.duration = 0.5
|
||||||
|
animation.autoreverses = true
|
||||||
|
animation.repeatCount = Float.infinity
|
||||||
|
|
||||||
|
indicatorView.layer.add(animation, forKey: "recording")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.cancelContainerView.layer.animation(forKey: "recording") == nil {
|
||||||
|
let animation = CAKeyframeAnimation(keyPath: "position.x")
|
||||||
|
animation.values = [-5.0 as NSNumber, 5.0 as NSNumber, 0.0 as NSNumber]
|
||||||
|
animation.keyTimes = [0.0 as NSNumber, 0.4546 as NSNumber, 0.9091 as NSNumber, 1 as NSNumber]
|
||||||
|
animation.duration = 1.5
|
||||||
|
animation.autoreverses = true
|
||||||
|
animation.isAdditive = true
|
||||||
|
animation.repeatCount = Float.infinity
|
||||||
|
|
||||||
|
self.cancelContainerView.layer.add(animation, forKey: "recording")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func animateIn() {
|
||||||
|
if let indicatorView = self.indicator.view {
|
||||||
|
indicatorView.layer.animatePosition(from: CGPoint(x: -20.0, y: 0.0), to: CGPoint(), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||||
|
}
|
||||||
|
if let timerTextView = self.timerText.view {
|
||||||
|
timerTextView.layer.animatePosition(from: CGPoint(x: -20.0, y: 0.0), to: CGPoint(), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||||
|
}
|
||||||
|
self.cancelContainerView.layer.animatePosition(from: CGPoint(x: self.bounds.width, y: 0.0), to: CGPoint(), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func animateOut(dismissRecording: Bool, completion: @escaping () -> Void) {
|
||||||
|
if let indicatorView = self.indicator.view as? LottieComponent.View {
|
||||||
|
if let _ = indicatorView.layer.animation(forKey: "recording") {
|
||||||
|
let fromAlpha = indicatorView.layer.presentation()?.opacity ?? indicatorView.layer.opacity
|
||||||
|
indicatorView.layer.removeAnimation(forKey: "recording")
|
||||||
|
indicatorView.layer.animateAlpha(from: CGFloat(fromAlpha), to: 1.0, duration: 0.2)
|
||||||
|
|
||||||
|
indicatorView.playOnce(completion: { [weak indicatorView] in
|
||||||
|
if let indicatorView {
|
||||||
|
let transition = Transition(animation: .curve(duration: 0.3, curve: .spring))
|
||||||
|
transition.setScale(view: indicatorView, scale: 0.001)
|
||||||
|
}
|
||||||
|
|
||||||
|
completion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
completion()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let transition = Transition(animation: .curve(duration: 0.3, curve: .spring))
|
||||||
|
if let timerTextView = self.timerText.view {
|
||||||
|
transition.setAlpha(view: timerTextView, alpha: 0.0)
|
||||||
|
transition.setScale(view: timerTextView, scale: 0.001)
|
||||||
|
}
|
||||||
|
|
||||||
|
transition.setAlpha(view: self.cancelContainerView, alpha: 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
func update(component: MediaRecordingPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
func update(component: MediaRecordingPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||||
let previousComponent = self.component
|
let previousComponent = self.component
|
||||||
self.component = component
|
self.component = component
|
||||||
@ -134,26 +220,36 @@ public final class MediaRecordingPanelComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.indicatorView.image == nil {
|
let indicatorSize = self.indicator.update(
|
||||||
self.indicatorView.image = generateFilledCircleImage(diameter: 10.0, color: UIColor(rgb: 0xFF3B30))
|
transition: .immediate,
|
||||||
}
|
component: AnyComponent(LottieComponent(
|
||||||
if let image = self.indicatorView.image {
|
content: LottieComponent.AppBundleContent(name: "BinRed"),
|
||||||
transition.setFrame(view: self.indicatorView, frame: CGRect(origin: CGPoint(x: 10.0, y: floor((availableSize.height - image.size.height) * 0.5)), size: image.size))
|
color: UIColor(rgb: 0xFF3B30),
|
||||||
|
startingPosition: .begin
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: 40.0, height: 40.0)
|
||||||
|
)
|
||||||
|
if let indicatorView = self.indicator.view {
|
||||||
|
if indicatorView.superview == nil {
|
||||||
|
self.addSubview(indicatorView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: indicatorView, frame: CGRect(origin: CGPoint(x: 3.0, y: floor((availableSize.height - indicatorSize.height) * 0.5)), size: indicatorSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
let timerTextSize = self.timerText.update(
|
let timerTextSize = self.timerText.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(Text(text: self.timerTextValue, font: Font.regular(15.0), color: .white)),
|
component: AnyComponent(Text(text: self.timerTextValue, font: self.timerFont, color: .white)),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||||
)
|
)
|
||||||
if let timerTextView = self.timerText.view {
|
if let timerTextView = self.timerText.view {
|
||||||
if timerTextView.superview == nil {
|
if timerTextView.superview == nil {
|
||||||
self.addSubview(timerTextView)
|
self.addSubview(timerTextView)
|
||||||
timerTextView.layer.anchorPoint = CGPoint()
|
timerTextView.layer.anchorPoint = CGPoint(x: 0.0, y: 0.5)
|
||||||
}
|
}
|
||||||
let timerTextFrame = CGRect(origin: CGPoint(x: 28.0, y: floor((availableSize.height - timerTextSize.height) * 0.5)), size: timerTextSize)
|
let timerTextFrame = CGRect(origin: CGPoint(x: 38.0, y: floor((availableSize.height - timerTextSize.height) * 0.5)), size: timerTextSize)
|
||||||
transition.setPosition(view: timerTextView, position: timerTextFrame.origin)
|
transition.setPosition(view: timerTextView, position: CGPoint(x: timerTextFrame.minX, y: timerTextFrame.midY))
|
||||||
timerTextView.bounds = CGRect(origin: CGPoint(), size: timerTextFrame.size)
|
timerTextView.bounds = CGRect(origin: CGPoint(), size: timerTextFrame.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,11 +257,11 @@ public final class MediaRecordingPanelComponent: Component {
|
|||||||
self.cancelIconView.image = UIImage(bundleImageName: "Chat/Input/Text/AudioRecordingCancelArrow")?.withRenderingMode(.alwaysTemplate)
|
self.cancelIconView.image = UIImage(bundleImageName: "Chat/Input/Text/AudioRecordingCancelArrow")?.withRenderingMode(.alwaysTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cancelIconView.tintColor = UIColor(white: 1.0, alpha: 0.3)
|
self.cancelIconView.tintColor = UIColor(white: 1.0, alpha: 0.4)
|
||||||
|
|
||||||
let cancelTextSize = self.cancelText.update(
|
let cancelTextSize = self.cancelText.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(Text(text: "Slide to cancel", font: Font.regular(15.0), color: UIColor(white: 1.0, alpha: 0.3))),
|
component: AnyComponent(Text(text: "Slide to cancel", font: Font.regular(15.0), color: UIColor(white: 1.0, alpha: 0.4))),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: max(30.0, availableSize.width - 100.0), height: 44.0)
|
containerSize: CGSize(width: max(30.0, availableSize.width - 100.0), height: 44.0)
|
||||||
)
|
)
|
||||||
@ -182,7 +278,7 @@ public final class MediaRecordingPanelComponent: Component {
|
|||||||
|
|
||||||
if let cancelTextView = self.cancelText.view {
|
if let cancelTextView = self.cancelText.view {
|
||||||
if cancelTextView.superview == nil {
|
if cancelTextView.superview == nil {
|
||||||
self.addSubview(cancelTextView)
|
self.cancelContainerView.addSubview(cancelTextView)
|
||||||
}
|
}
|
||||||
transition.setFrame(view: cancelTextView, frame: textFrame)
|
transition.setFrame(view: cancelTextView, frame: textFrame)
|
||||||
}
|
}
|
||||||
@ -190,6 +286,8 @@ public final class MediaRecordingPanelComponent: Component {
|
|||||||
transition.setFrame(view: self.cancelIconView, frame: CGRect(origin: CGPoint(x: textFrame.minX - 4.0 - image.size.width, y: textFrame.minY + floor((textFrame.height - image.size.height) * 0.5)), size: image.size))
|
transition.setFrame(view: self.cancelIconView, frame: CGRect(origin: CGPoint(x: textFrame.minX - 4.0 - image.size.width, y: textFrame.minY + floor((textFrame.height - image.size.height) * 0.5)), size: image.size))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.updateAnimations()
|
||||||
|
|
||||||
return availableSize
|
return availableSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,6 +141,7 @@ public final class MessageInputActionButtonComponent: Component {
|
|||||||
let micButton = ChatTextInputMediaRecordingButton(
|
let micButton = ChatTextInputMediaRecordingButton(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
theme: component.theme,
|
theme: component.theme,
|
||||||
|
useDarkTheme: true,
|
||||||
strings: component.strings,
|
strings: component.strings,
|
||||||
presentController: component.presentController
|
presentController: component.presentController
|
||||||
)
|
)
|
||||||
|
@ -90,6 +90,7 @@ public final class MessageInputPanelComponent: Component {
|
|||||||
private let stickerIconView: UIImageView
|
private let stickerIconView: UIImageView
|
||||||
|
|
||||||
private var mediaRecordingPanel: ComponentView<Empty>?
|
private var mediaRecordingPanel: ComponentView<Empty>?
|
||||||
|
private weak var dismissingMediaRecordingPanel: UIView?
|
||||||
|
|
||||||
private var currentMediaInputIsVoice: Bool = true
|
private var currentMediaInputIsVoice: Bool = true
|
||||||
private var mediaCancelFraction: CGFloat = 0.0
|
private var mediaCancelFraction: CGFloat = 0.0
|
||||||
@ -202,8 +203,6 @@ public final class MessageInputPanelComponent: Component {
|
|||||||
self.addSubview(attachmentButtonView)
|
self.addSubview(attachmentButtonView)
|
||||||
}
|
}
|
||||||
transition.setFrame(view: attachmentButtonView, frame: CGRect(origin: CGPoint(x: floor((insets.left - attachmentButtonSize.width) * 0.5), y: size.height - baseHeight + floor((baseHeight - attachmentButtonSize.height) * 0.5)), size: attachmentButtonSize))
|
transition.setFrame(view: attachmentButtonView, frame: CGRect(origin: CGPoint(x: floor((insets.left - attachmentButtonSize.width) * 0.5), y: size.height - baseHeight + floor((baseHeight - attachmentButtonSize.height) * 0.5)), size: attachmentButtonSize))
|
||||||
|
|
||||||
transition.setAlpha(view: attachmentButtonView, alpha: (component.audioRecorder != nil || component.videoRecordingStatus != nil) ? 0.0 : 1.0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let inputActionButtonSize = self.inputActionButton.update(
|
let inputActionButtonSize = self.inputActionButton.update(
|
||||||
@ -272,6 +271,13 @@ public final class MessageInputPanelComponent: Component {
|
|||||||
component.externalState.hasText = self.textFieldExternalState.hasText
|
component.externalState.hasText = self.textFieldExternalState.hasText
|
||||||
|
|
||||||
if component.audioRecorder != nil || component.videoRecordingStatus != nil {
|
if component.audioRecorder != nil || component.videoRecordingStatus != nil {
|
||||||
|
if let dismissingMediaRecordingPanel = self.dismissingMediaRecordingPanel {
|
||||||
|
self.dismissingMediaRecordingPanel = nil
|
||||||
|
transition.setAlpha(view: dismissingMediaRecordingPanel, alpha: 0.0, completion: { [weak dismissingMediaRecordingPanel] _ in
|
||||||
|
dismissingMediaRecordingPanel?.removeFromSuperview()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
let mediaRecordingPanel: ComponentView<Empty>
|
let mediaRecordingPanel: ComponentView<Empty>
|
||||||
var mediaRecordingPanelTransition = transition
|
var mediaRecordingPanelTransition = transition
|
||||||
if let current = self.mediaRecordingPanel {
|
if let current = self.mediaRecordingPanel {
|
||||||
@ -292,7 +298,7 @@ public final class MessageInputPanelComponent: Component {
|
|||||||
environment: {},
|
environment: {},
|
||||||
containerSize: size
|
containerSize: size
|
||||||
)
|
)
|
||||||
if let mediaRecordingPanelView = mediaRecordingPanel.view {
|
if let mediaRecordingPanelView = mediaRecordingPanel.view as? MediaRecordingPanelComponent.View {
|
||||||
var animateIn = false
|
var animateIn = false
|
||||||
if mediaRecordingPanelView.superview == nil {
|
if mediaRecordingPanelView.superview == nil {
|
||||||
animateIn = true
|
animateIn = true
|
||||||
@ -300,16 +306,43 @@ public final class MessageInputPanelComponent: Component {
|
|||||||
}
|
}
|
||||||
mediaRecordingPanelTransition.setFrame(view: mediaRecordingPanelView, frame: CGRect(origin: CGPoint(), size: size))
|
mediaRecordingPanelTransition.setFrame(view: mediaRecordingPanelView, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
if animateIn && !transition.animation.isImmediate {
|
if animateIn && !transition.animation.isImmediate {
|
||||||
transition.animateAlpha(view: mediaRecordingPanelView, from: 0.0, to: 1.0)
|
mediaRecordingPanelView.animateIn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let attachmentButtonView = self.attachmentButton.view {
|
||||||
|
transition.setAlpha(view: attachmentButtonView, alpha: 0.0)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if let mediaRecordingPanel = self.mediaRecordingPanel {
|
if let mediaRecordingPanel = self.mediaRecordingPanel {
|
||||||
self.mediaRecordingPanel = nil
|
self.mediaRecordingPanel = nil
|
||||||
|
|
||||||
if let mediaRecordingPanelView = mediaRecordingPanel.view {
|
if let dismissingMediaRecordingPanel = self.dismissingMediaRecordingPanel {
|
||||||
transition.setAlpha(view: mediaRecordingPanelView, alpha: 0.0, completion: { [weak mediaRecordingPanelView] _ in
|
self.dismissingMediaRecordingPanel = nil
|
||||||
mediaRecordingPanelView?.removeFromSuperview()
|
transition.setAlpha(view: dismissingMediaRecordingPanel, alpha: 0.0, completion: { [weak dismissingMediaRecordingPanel] _ in
|
||||||
|
dismissingMediaRecordingPanel?.removeFromSuperview()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dismissingMediaRecordingPanel = mediaRecordingPanel.view
|
||||||
|
|
||||||
|
if let mediaRecordingPanelView = mediaRecordingPanel.view as? MediaRecordingPanelComponent.View {
|
||||||
|
mediaRecordingPanelView.animateOut(dismissRecording: true, completion: { [weak self, weak mediaRecordingPanelView] in
|
||||||
|
let transition = Transition(animation: .curve(duration: 0.3, curve: .spring))
|
||||||
|
|
||||||
|
if let mediaRecordingPanelView = mediaRecordingPanelView {
|
||||||
|
transition.setAlpha(view: mediaRecordingPanelView, alpha: 0.0, completion: { [weak mediaRecordingPanelView] _ in
|
||||||
|
mediaRecordingPanelView?.removeFromSuperview()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if self.mediaRecordingPanel == nil, let attachmentButtonView = self.attachmentButton.view {
|
||||||
|
transition.setAlpha(view: attachmentButtonView, alpha: 1.0)
|
||||||
|
transition.animateScale(view: attachmentButtonView, from: 0.001, to: 1.0)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -461,7 +461,7 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
itemTransition.setFrame(view: view, frame: CGRect(origin: CGPoint(), size: itemLayout.size))
|
itemTransition.setFrame(view: view, frame: CGRect(origin: CGPoint(), size: itemLayout.size))
|
||||||
|
|
||||||
if let view = view as? StoryContentItem.View {
|
if let view = view as? StoryContentItem.View {
|
||||||
view.setIsProgressPaused(self.inputPanelExternalState.isEditing || self.attachmentController != nil)
|
view.setIsProgressPaused(self.inputPanelExternalState.isEditing || self.attachmentController != nil || self.audioRecorderValue != nil || self.videoRecorderValue != nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -484,7 +484,7 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
for (_, visibleItem) in self.visibleItems {
|
for (_, visibleItem) in self.visibleItems {
|
||||||
if let view = visibleItem.view.view {
|
if let view = visibleItem.view.view {
|
||||||
if let view = view as? StoryContentItem.View {
|
if let view = view as? StoryContentItem.View {
|
||||||
view.setIsProgressPaused(self.inputPanelExternalState.isEditing || self.attachmentController?.window != nil)
|
view.setIsProgressPaused(self.inputPanelExternalState.isEditing || self.attachmentController?.window != nil || self.audioRecorderValue != nil || self.videoRecorderValue != nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
|
|
||||||
_dimensions = CGSizeMake(width, height);
|
_dimensions = CGSizeMake(width, height);
|
||||||
|
|
||||||
if ((_frameRate > 60) || _animation->duration() > 9.0) {
|
if ((_frameRate > 360) || _animation->duration() > 9.0) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user