mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Various improvements
This commit is contained in:
parent
4fcdfebb02
commit
18f41cd24f
@ -379,7 +379,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
middle1RightItemLayoutAndApply = makeMiddle1RightItemLayout(
|
||||
params.width,
|
||||
item.presentationData,
|
||||
compactNumericCountString(views.forwardCount),
|
||||
compactNumericCountString(views.forwardCount - Int(item.publicShares ?? 0)),
|
||||
item.presentationData.strings.Stats_Message_PrivateShares,
|
||||
nil
|
||||
)
|
||||
|
@ -107,8 +107,14 @@ public final class MediaEditor {
|
||||
private let clock = CMClockGetHostTimeClock()
|
||||
|
||||
private var player: AVPlayer?
|
||||
private var playerAudioMix: AVMutableAudioMix?
|
||||
|
||||
private var additionalPlayer: AVPlayer?
|
||||
private var additionalPlayerAudioMix: AVMutableAudioMix?
|
||||
|
||||
private var audioPlayer: AVPlayer?
|
||||
private var audioPlayerAudioMix: AVMutableAudioMix?
|
||||
|
||||
private var volumeFadeIn: SwiftSignalKit.Timer?
|
||||
|
||||
private var timeObserver: Any?
|
||||
@ -901,7 +907,12 @@ public final class MediaEditor {
|
||||
return values.withUpdatedVideoVolume(volume)
|
||||
}
|
||||
|
||||
self.player?.volume = Float(volume ?? 1.0)
|
||||
if let audioMix = self.playerAudioMix, let asset = self.player?.currentItem?.asset {
|
||||
let audioMixInputParameters = AVMutableAudioMixInputParameters(track: asset.tracks(withMediaType: .audio).first)
|
||||
audioMixInputParameters.setVolume(Float(volume ?? 1.0), at: .zero)
|
||||
audioMix.inputParameters = [audioMixInputParameters]
|
||||
self.player?.currentItem?.audioMix = audioMix
|
||||
}
|
||||
}
|
||||
|
||||
public func setVideoIsMirrored(_ videoIsMirrored: Bool) {
|
||||
@ -1334,6 +1345,7 @@ public final class MediaEditor {
|
||||
|
||||
self.additionalPlayer = nil
|
||||
self.additionalPlayerPromise.set(.single(nil))
|
||||
self.additionalPlayerAudioMix = nil
|
||||
|
||||
if let textureSource = self.renderer.textureSource as? UniversalTextureSource {
|
||||
textureSource.forceUpdates = true
|
||||
@ -1376,12 +1388,18 @@ public final class MediaEditor {
|
||||
player.masterClock = clock
|
||||
}
|
||||
player.automaticallyWaitsToMinimizeStalling = false
|
||||
|
||||
let audioMix = AVMutableAudioMix()
|
||||
let audioMixInputParameters = AVMutableAudioMixInputParameters(track: asset.tracks(withMediaType: .audio).first)
|
||||
if let volume = self.values.additionalVideoVolume {
|
||||
audioMixInputParameters.setVolume(Float(volume), at: .zero)
|
||||
}
|
||||
audioMix.inputParameters = [audioMixInputParameters]
|
||||
player.currentItem?.audioMix = audioMix
|
||||
|
||||
self.additionalPlayer = player
|
||||
self.additionalPlayerPromise.set(.single(player))
|
||||
|
||||
if let volume = self.values.additionalVideoVolume {
|
||||
self.additionalPlayer?.volume = Float(volume)
|
||||
}
|
||||
self.additionalPlayerAudioMix = audioMix
|
||||
|
||||
(self.renderer.textureSource as? UniversalTextureSource)?.setAdditionalInput(.video(playerItem))
|
||||
}
|
||||
@ -1417,7 +1435,12 @@ public final class MediaEditor {
|
||||
return values.withUpdatedAdditionalVideoVolume(volume)
|
||||
}
|
||||
|
||||
self.additionalPlayer?.volume = Float(volume ?? 1.0)
|
||||
if let audioMix = self.additionalPlayerAudioMix, let asset = self.additionalPlayer?.currentItem?.asset {
|
||||
let audioMixInputParameters = AVMutableAudioMixInputParameters(track: asset.tracks(withMediaType: .audio).first)
|
||||
audioMixInputParameters.setVolume(Float(volume ?? 1.0), at: .zero)
|
||||
audioMix.inputParameters = [audioMixInputParameters]
|
||||
self.additionalPlayer?.currentItem?.audioMix = audioMix
|
||||
}
|
||||
}
|
||||
|
||||
private func updateAdditionalVideoPlaybackRange() {
|
||||
@ -1444,6 +1467,7 @@ public final class MediaEditor {
|
||||
|
||||
self.audioPlayer = nil
|
||||
self.audioPlayerPromise.set(.single(nil))
|
||||
self.audioPlayerAudioMix = nil
|
||||
|
||||
self.audioDelayTimer?.invalidate()
|
||||
self.audioDelayTimer = nil
|
||||
@ -1465,14 +1489,20 @@ public final class MediaEditor {
|
||||
let audioAsset = AVURLAsset(url: URL(fileURLWithPath: audioPath))
|
||||
let audioPlayer = AVPlayer(playerItem: AVPlayerItem(asset: audioAsset))
|
||||
audioPlayer.automaticallyWaitsToMinimizeStalling = false
|
||||
|
||||
let audioMix = AVMutableAudioMix()
|
||||
let audioMixInputParameters = AVMutableAudioMixInputParameters(track: audioAsset.tracks(withMediaType: .audio).first)
|
||||
if let volume = self.values.audioTrackVolume {
|
||||
audioMixInputParameters.setVolume(Float(volume), at: .zero)
|
||||
}
|
||||
audioMix.inputParameters = [audioMixInputParameters]
|
||||
audioPlayer.currentItem?.audioMix = audioMix
|
||||
|
||||
self.audioPlayer = audioPlayer
|
||||
self.audioPlayerPromise.set(.single(audioPlayer))
|
||||
self.audioPlayerAudioMix = audioMix
|
||||
self.maybeGenerateAudioSamples(asset: audioAsset)
|
||||
|
||||
if let volume = self.values.audioTrackVolume {
|
||||
self.audioPlayer?.volume = Float(volume)
|
||||
}
|
||||
|
||||
self.setupTimeObservers()
|
||||
}
|
||||
|
||||
@ -1510,7 +1540,12 @@ public final class MediaEditor {
|
||||
return values.withUpdatedAudioTrackVolume(volume)
|
||||
}
|
||||
|
||||
self.audioPlayer?.volume = Float(volume ?? 1.0)
|
||||
if let audioMix = self.audioPlayerAudioMix, let asset = self.audioPlayer?.currentItem?.asset {
|
||||
let audioMixInputParameters = AVMutableAudioMixInputParameters(track: asset.tracks(withMediaType: .audio).first)
|
||||
audioMixInputParameters.setVolume(Float(volume ?? 1.0), at: .zero)
|
||||
audioMix.inputParameters = [audioMixInputParameters]
|
||||
self.audioPlayer?.currentItem?.audioMix = audioMix
|
||||
}
|
||||
}
|
||||
|
||||
public func setDrawingAndEntities(data: Data?, image: UIImage?, entities: [CodableDrawingEntity]) {
|
||||
|
@ -520,7 +520,7 @@ public final class MediaEditorVideoExport {
|
||||
if let compositionTrack = composition?.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) {
|
||||
try? compositionTrack.insertTimeRange(CMTimeRange(start: .zero, duration: asset.duration), of: audioAssetTrack, at: .zero)
|
||||
|
||||
if let volume = self.configuration.values.videoVolume, volume < 1.0 {
|
||||
if let volume = self.configuration.values.videoVolume, volume != 1.0 {
|
||||
let trackParameters = AVMutableAudioMixInputParameters(track: compositionTrack)
|
||||
trackParameters.trackID = compositionTrack.trackID
|
||||
trackParameters.setVolume(Float(volume), at: .zero)
|
||||
@ -558,7 +558,7 @@ public final class MediaEditorVideoExport {
|
||||
if let compositionTrack = composition?.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) {
|
||||
try? compositionTrack.insertTimeRange(timeRange, of: audioAssetTrack, at: startTime)
|
||||
|
||||
if let volume = self.configuration.values.additionalVideoVolume, volume < 1.0 {
|
||||
if let volume = self.configuration.values.additionalVideoVolume, volume != 1.0 {
|
||||
let trackParameters = AVMutableAudioMixInputParameters(track: compositionTrack)
|
||||
trackParameters.trackID = compositionTrack.trackID
|
||||
trackParameters.setVolume(Float(volume), at: .zero)
|
||||
@ -582,7 +582,7 @@ public final class MediaEditorVideoExport {
|
||||
if let compositionTrack = composition?.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) {
|
||||
try? compositionTrack.insertTimeRange(timeRange, of: audioAssetTrack, at: startTime)
|
||||
|
||||
if let volume = self.configuration.values.audioTrackVolume, volume < 1.0 {
|
||||
if let volume = self.configuration.values.audioTrackVolume, volume != 1.0 {
|
||||
let trackParameters = AVMutableAudioMixInputParameters(track: compositionTrack)
|
||||
trackParameters.trackID = compositionTrack.trackID
|
||||
trackParameters.setVolume(Float(volume), at: .zero)
|
||||
|
@ -3450,7 +3450,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
items.append(
|
||||
.custom(VolumeSliderContextItem(minValue: 0.0, value: value, valueChanged: { [weak self] value, _ in
|
||||
.custom(VolumeSliderContextItem(minValue: 0.0, maxValue: 1.5, value: value, valueChanged: { [weak self] value, _ in
|
||||
if let self, let mediaEditor = self.mediaEditor {
|
||||
if trackId == 0 {
|
||||
if mediaEditor.values.videoIsMuted {
|
||||
|
@ -126,7 +126,8 @@ public final class StoryFooterPanelComponent: Component {
|
||||
private var likeStatsText: AnimatedCountLabelView?
|
||||
private var forwardButton: ComponentView<Empty>?
|
||||
private var repostButton: ComponentView<Empty>?
|
||||
|
||||
private var forwardStatsText: AnimatedCountLabelView?
|
||||
|
||||
private var reactionStatsIcon: UIImageView?
|
||||
private var reactionStatsText: AnimatedCountLabelView?
|
||||
|
||||
@ -368,11 +369,11 @@ public final class StoryFooterPanelComponent: Component {
|
||||
|
||||
var viewCount = 0
|
||||
var reactionCount = 0
|
||||
var repostCount = 0
|
||||
var forwardCount = 0
|
||||
if let views = component.externalViews ?? component.storyItem.views, views.seenCount != 0 {
|
||||
viewCount = views.seenCount
|
||||
reactionCount = views.reactedCount
|
||||
repostCount = 0
|
||||
forwardCount = views.forwardCount
|
||||
}
|
||||
|
||||
if component.isChannel {
|
||||
@ -388,9 +389,11 @@ public final class StoryFooterPanelComponent: Component {
|
||||
|
||||
if component.isChannel {
|
||||
var likeStatsTransition = transition
|
||||
var forwardStatsTransition = transition
|
||||
|
||||
if transition.animation.isImmediate, !isFirstTime, let previousComponent, previousComponent.storyItem.id == component.storyItem.id, previousComponent.expandFraction == component.expandFraction {
|
||||
likeStatsTransition = .easeInOut(duration: 0.2)
|
||||
forwardStatsTransition = .easeInOut(duration: 0.2)
|
||||
}
|
||||
|
||||
let likeStatsText: AnimatedCountLabelView
|
||||
@ -491,6 +494,40 @@ public final class StoryFooterPanelComponent: Component {
|
||||
}
|
||||
|
||||
if component.canShare {
|
||||
let forwardStatsText: AnimatedCountLabelView
|
||||
if let current = self.forwardStatsText {
|
||||
forwardStatsText = current
|
||||
} else {
|
||||
forwardStatsTransition = forwardStatsTransition.withAnimation(.none)
|
||||
forwardStatsText = AnimatedCountLabelView(frame: CGRect())
|
||||
forwardStatsText.isUserInteractionEnabled = false
|
||||
self.forwardStatsText = forwardStatsText
|
||||
self.externalContainerView.addSubview(forwardStatsText)
|
||||
}
|
||||
|
||||
let forwardStatsLayout = forwardStatsText.update(
|
||||
size: CGSize(width: availableSize.width, height: size.height),
|
||||
segments: [
|
||||
.number(forwardCount, NSAttributedString(string: "\(forwardCount)", font: Font.with(size: 15.0, traits: .monospacedNumbers), textColor: .white))
|
||||
],
|
||||
transition: (isFirstTime || likeStatsTransition.animation.isImmediate) ? .immediate : ContainedViewLayoutTransition.animated(duration: 0.25, curve: .easeInOut)
|
||||
)
|
||||
var forwardStatsFrame = CGRect(origin: CGPoint(x: rightContentOffset - forwardStatsLayout.size.width, y: floor((size.height - forwardStatsLayout.size.height) * 0.5)), size: forwardStatsLayout.size)
|
||||
forwardStatsFrame.origin.y += component.expandFraction * 45.0
|
||||
|
||||
forwardStatsTransition.setPosition(view: forwardStatsText, position: forwardStatsFrame.center)
|
||||
forwardStatsTransition.setBounds(view: forwardStatsText, bounds: CGRect(origin: CGPoint(), size: forwardStatsFrame.size))
|
||||
var forwardStatsAlpha: CGFloat = (1.0 - component.expandFraction)
|
||||
if forwardCount == 0 {
|
||||
forwardStatsAlpha = 0.0
|
||||
}
|
||||
forwardStatsTransition.setAlpha(view: forwardStatsText, alpha: forwardStatsAlpha)
|
||||
forwardStatsTransition.setScale(view: forwardStatsText, scale: forwardCount == 0 ? 0.001 : 1.0)
|
||||
|
||||
if forwardCount != 0 {
|
||||
rightContentOffset -= forwardStatsLayout.size.width + 1.0
|
||||
}
|
||||
|
||||
let repostButton: ComponentView<Empty>
|
||||
if let current = self.repostButton {
|
||||
repostButton = current
|
||||
@ -720,7 +757,7 @@ public final class StoryFooterPanelComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
if repostCount != 0 && !component.isChannel {
|
||||
if forwardCount != 0 && !component.isChannel {
|
||||
var repostTransition = transition
|
||||
let repostStatsIcon: UIImageView
|
||||
if let current = self.repostStatsIcon {
|
||||
@ -749,7 +786,7 @@ public final class StoryFooterPanelComponent: Component {
|
||||
let repostStatsLayout = repostStatsText.update(
|
||||
size: CGSize(width: availableSize.width, height: size.height),
|
||||
segments: [
|
||||
.number(repostCount, NSAttributedString(string: "\(repostCount)", font: Font.with(size: 15.0, traits: .monospacedNumbers), textColor: .white))
|
||||
.number(forwardCount, NSAttributedString(string: "\(forwardCount)", font: Font.with(size: 15.0, traits: .monospacedNumbers), textColor: .white))
|
||||
],
|
||||
reducedLetterSpacing: true,
|
||||
transition: (isFirstTime || repostTransition.animation.isImmediate) ? .immediate : ContainedViewLayoutTransition.animated(duration: 0.25, curve: .easeInOut)
|
||||
|
@ -10,17 +10,19 @@ import AnimatedCountLabelNode
|
||||
|
||||
public final class VolumeSliderContextItem: ContextMenuCustomItem {
|
||||
private let minValue: CGFloat
|
||||
private let maxValue: CGFloat
|
||||
private let value: CGFloat
|
||||
private let valueChanged: (CGFloat, Bool) -> Void
|
||||
|
||||
public init(minValue: CGFloat, value: CGFloat, valueChanged: @escaping (CGFloat, Bool) -> Void) {
|
||||
public init(minValue: CGFloat, maxValue: CGFloat = 1.0, value: CGFloat, valueChanged: @escaping (CGFloat, Bool) -> Void) {
|
||||
self.minValue = minValue
|
||||
self.maxValue = maxValue
|
||||
self.value = value
|
||||
self.valueChanged = valueChanged
|
||||
}
|
||||
|
||||
public func node(presentationData: PresentationData, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) -> ContextMenuCustomNode {
|
||||
return VolumeSliderContextItemNode(presentationData: presentationData, getController: getController, minValue: self.minValue, value: self.value, valueChanged: self.valueChanged)
|
||||
return VolumeSliderContextItemNode(presentationData: presentationData, getController: getController, minValue: self.minValue, maxValue: self.maxValue, value: self.value, valueChanged: self.valueChanged)
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,6 +42,7 @@ private final class VolumeSliderContextItemNode: ASDisplayNode, ContextMenuCusto
|
||||
private let foregroundTextNode: ImmediateAnimatedCountLabelNode
|
||||
|
||||
let minValue: CGFloat
|
||||
let maxValue: CGFloat
|
||||
var value: CGFloat = 1.0 {
|
||||
didSet {
|
||||
self.updateValue(transition: .animated(duration: 0.2, curve: .spring))
|
||||
@ -50,9 +53,10 @@ private final class VolumeSliderContextItemNode: ASDisplayNode, ContextMenuCusto
|
||||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
init(presentationData: PresentationData, getController: @escaping () -> ContextControllerProtocol?, minValue: CGFloat, value: CGFloat, valueChanged: @escaping (CGFloat, Bool) -> Void) {
|
||||
init(presentationData: PresentationData, getController: @escaping () -> ContextControllerProtocol?, minValue: CGFloat, maxValue: CGFloat, value: CGFloat, valueChanged: @escaping (CGFloat, Bool) -> Void) {
|
||||
self.presentationData = presentationData
|
||||
self.minValue = minValue
|
||||
self.maxValue = maxValue
|
||||
self.value = value
|
||||
self.valueChanged = valueChanged
|
||||
|
||||
@ -153,8 +157,9 @@ private final class VolumeSliderContextItemNode: ASDisplayNode, ContextMenuCusto
|
||||
private func updateValue(transition: ContainedViewLayoutTransition = .immediate) {
|
||||
let width = self.frame.width
|
||||
|
||||
let range = self.maxValue - self.minValue
|
||||
let value = self.value
|
||||
transition.updateFrameAdditive(node: self.foregroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: value * width, height: self.frame.height)))
|
||||
transition.updateFrameAdditive(node: self.foregroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: value / range * width, height: self.frame.height)))
|
||||
|
||||
let stringValue = "\(Int(self.value * 100.0))%"
|
||||
|
||||
@ -236,10 +241,10 @@ private final class VolumeSliderContextItemNode: ASDisplayNode, ContextMenuCusto
|
||||
|
||||
let translation: CGFloat = gestureRecognizer.translation(in: gestureRecognizer.view).x
|
||||
let delta = translation / self.bounds.width
|
||||
self.value = max(self.minValue, min(1.0, self.value + delta))
|
||||
self.value = max(self.minValue, min(self.maxValue, self.value + delta))
|
||||
gestureRecognizer.setTranslation(CGPoint(), in: gestureRecognizer.view)
|
||||
|
||||
if self.value == 1.0 && previousValue != 1.0 {
|
||||
if self.value == self.maxValue && previousValue != self.maxValue {
|
||||
self.backgroundIconNode.layer.animateScale(from: 1.0, to: 1.1, duration: 0.16, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.backgroundIconNode.layer.animateScale(from: 1.1, to: 1.0, duration: 0.16)
|
||||
@ -251,6 +256,8 @@ private final class VolumeSliderContextItemNode: ASDisplayNode, ContextMenuCusto
|
||||
}
|
||||
})
|
||||
self.hapticFeedback.impact(.soft)
|
||||
} else if self.maxValue != 1.0 && self.value == 1.0 && previousValue != 1.0 {
|
||||
self.hapticFeedback.impact(.soft)
|
||||
} else if self.value == 0.0 && previousValue != 0.0 {
|
||||
self.hapticFeedback.impact(.soft)
|
||||
}
|
||||
@ -260,7 +267,7 @@ private final class VolumeSliderContextItemNode: ASDisplayNode, ContextMenuCusto
|
||||
case .ended:
|
||||
let translation: CGFloat = gestureRecognizer.translation(in: gestureRecognizer.view).x
|
||||
let delta = translation / self.bounds.width
|
||||
self.value = max(self.minValue, min(1.0, self.value + delta))
|
||||
self.value = max(self.minValue, min(self.maxValue, self.value + delta))
|
||||
self.valueChanged(self.value, true)
|
||||
default:
|
||||
break
|
||||
@ -269,7 +276,7 @@ private final class VolumeSliderContextItemNode: ASDisplayNode, ContextMenuCusto
|
||||
|
||||
@objc private func tapGesture(_ gestureRecognizer: UITapGestureRecognizer) {
|
||||
let location = gestureRecognizer.location(in: gestureRecognizer.view)
|
||||
self.value = max(self.minValue, min(1.0, location.x / self.bounds.width))
|
||||
self.value = max(self.minValue, min(self.maxValue, location.x / self.bounds.width))
|
||||
self.valueChanged(self.value, true)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user