mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit '10b679446e7c0560683ee80747e0ded7cce20976'
This commit is contained in:
commit
f173aaa5e5
@ -2746,7 +2746,21 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let text: String?
|
||||
let rate: CGFloat?
|
||||
if baseRate == .x1 {
|
||||
if case let .sliderCommit(previousValue, newValue) = changeType {
|
||||
let value = String(format: "%0.1f", baseRate.doubleValue)
|
||||
if baseRate == .x1 {
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipNormal
|
||||
} else {
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipCustom(value).string
|
||||
}
|
||||
if newValue > previousValue {
|
||||
rate = .infinity
|
||||
} else if newValue < previousValue {
|
||||
rate = -.infinity
|
||||
} else {
|
||||
rate = nil
|
||||
}
|
||||
} else if baseRate == .x1 {
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipNormal
|
||||
rate = 1.0
|
||||
} else if baseRate == .x1_5 {
|
||||
@ -2756,19 +2770,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipSpeedUp
|
||||
rate = 2.0
|
||||
} else {
|
||||
let value = String(format: "%0.1f", baseRate.doubleValue)
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipCustom(value).string
|
||||
if case let .sliderCommit(previousValue, newValue) = changeType {
|
||||
if newValue > previousValue {
|
||||
rate = .infinity
|
||||
} else if newValue < previousValue {
|
||||
rate = -.infinity
|
||||
} else {
|
||||
rate = nil
|
||||
}
|
||||
} else {
|
||||
rate = nil
|
||||
}
|
||||
text = nil
|
||||
rate = nil
|
||||
}
|
||||
var showTooltip = true
|
||||
if case .sliderChange = changeType {
|
||||
|
@ -1086,7 +1086,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
let avatarIconContent: EmojiStatusComponent.Content
|
||||
if let fileId = icon, fileId != 0 {
|
||||
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .forever)
|
||||
avatarIconContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(0))
|
||||
} else {
|
||||
avatarIconContent = .topic(title: String(title.prefix(1)), color: color, size: CGSize(width: 32.0, height: 32.0))
|
||||
}
|
||||
|
@ -2625,10 +2625,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
c.setItems(strongSelf.contextMenuMainItems(dismiss: dismiss) |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
|
||||
})))
|
||||
|
||||
items.append(.custom(SliderContextItem(minValue: 0.5, maxValue: 2.5, value: status.baseRate, valueChanged: { [weak self] newValue, finished in
|
||||
items.append(.custom(SliderContextItem(minValue: 0.2, maxValue: 2.5, value: status.baseRate, valueChanged: { [weak self] newValue, finished in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let newValue = normalizeValue(newValue)
|
||||
strongSelf.updatePlaybackRate(newValue)
|
||||
if finished {
|
||||
//dismiss()
|
||||
@ -2852,3 +2853,7 @@ final class HeaderContextReferenceContentSource: ContextReferenceContentSource {
|
||||
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
}
|
||||
|
||||
private func normalizeValue(_ value: CGFloat) -> CGFloat {
|
||||
return round(value * 10.0) / 10.0
|
||||
}
|
||||
|
@ -18,6 +18,10 @@ import SliderContextItem
|
||||
private let titleFont = Font.regular(12.0)
|
||||
private let subtitleFont = Font.regular(10.0)
|
||||
|
||||
private func normalizeValue(_ value: CGFloat) -> CGFloat {
|
||||
return round(value * 10.0) / 10.0
|
||||
}
|
||||
|
||||
private class MediaHeaderItemNode: ASDisplayNode {
|
||||
private let titleNode: TextNode
|
||||
private let subtitleNode: TextNode
|
||||
@ -527,7 +531,8 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
|
||||
var presetItems: [ContextMenuItem] = []
|
||||
|
||||
let previousValue = self.playbackBaseRate?.doubleValue ?? 1.0
|
||||
let sliderItem: ContextMenuItem = .custom(SliderContextItem(minValue: 0.5, maxValue: 2.5, value: previousValue, valueChanged: { [weak self] newValue, finished in
|
||||
let sliderItem: ContextMenuItem = .custom(SliderContextItem(minValue: 0.2, maxValue: 2.5, value: previousValue, valueChanged: { [weak self] newValue, finished in
|
||||
let newValue = normalizeValue(newValue)
|
||||
self?.setRate?(AudioPlaybackRate(newValue), .sliderChange)
|
||||
if finished {
|
||||
scheduleTooltip(.sliderCommit(previousValue, newValue))
|
||||
|
@ -699,7 +699,21 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let text: String?
|
||||
let rate: CGFloat?
|
||||
if baseRate == .x1 {
|
||||
if case let .sliderCommit(previousValue, newValue) = changeType {
|
||||
let value = String(format: "%0.1f", baseRate.doubleValue)
|
||||
if baseRate == .x1 {
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipNormal
|
||||
} else {
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipCustom(value).string
|
||||
}
|
||||
if newValue > previousValue {
|
||||
rate = .infinity
|
||||
} else if newValue < previousValue {
|
||||
rate = -.infinity
|
||||
} else {
|
||||
rate = nil
|
||||
}
|
||||
} else if baseRate == .x1 {
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipNormal
|
||||
rate = 1.0
|
||||
} else if baseRate == .x1_5 {
|
||||
@ -709,19 +723,8 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipSpeedUp
|
||||
rate = 2.0
|
||||
} else {
|
||||
let value = String(format: "%0.1f", baseRate.doubleValue)
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipCustom(value).string
|
||||
if case let .sliderCommit(previousValue, newValue) = changeType {
|
||||
if newValue > previousValue {
|
||||
rate = .infinity
|
||||
} else if newValue < previousValue {
|
||||
rate = -.infinity
|
||||
} else {
|
||||
rate = nil
|
||||
}
|
||||
} else {
|
||||
rate = nil
|
||||
}
|
||||
text = nil
|
||||
rate = nil
|
||||
}
|
||||
var showTooltip = true
|
||||
if case .sliderChange = changeType {
|
||||
|
@ -83,6 +83,15 @@ private class CallStatusBarBackgroundNode: ASDisplayNode {
|
||||
|
||||
private let hierarchyTrackingNode: HierarchyTrackingNode
|
||||
private var isCurrentlyInHierarchy = true
|
||||
|
||||
var animationsEnabled: Bool = false {
|
||||
didSet {
|
||||
self.updateAnimations()
|
||||
if !self.animationsEnabled {
|
||||
self.maskCurveView.disableCurves()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override init() {
|
||||
self.foregroundView = UIView()
|
||||
@ -137,42 +146,11 @@ private class CallStatusBarBackgroundNode: ASDisplayNode {
|
||||
CATransaction.commit()
|
||||
}
|
||||
|
||||
private func setupGradientAnimations() {
|
||||
/*if let _ = self.foregroundGradientLayer.animation(forKey: "movement") {
|
||||
} else {
|
||||
let previousValue = self.foregroundGradientLayer.startPoint
|
||||
let newValue: CGPoint
|
||||
if self.maskCurveView.presentationAudioLevel > 0.1 {
|
||||
newValue = CGPoint(x: CGFloat.random(in: 1.0 ..< 1.3), y: 0.5)
|
||||
} else {
|
||||
newValue = CGPoint(x: CGFloat.random(in: 0.85 ..< 1.2), y: 0.5)
|
||||
}
|
||||
self.foregroundGradientLayer.startPoint = newValue
|
||||
|
||||
CATransaction.begin()
|
||||
|
||||
let animation = CABasicAnimation(keyPath: "endPoint")
|
||||
animation.duration = Double.random(in: 0.8 ..< 1.4)
|
||||
animation.fromValue = previousValue
|
||||
animation.toValue = newValue
|
||||
|
||||
CATransaction.setCompletionBlock { [weak self] in
|
||||
self?.setupGradientAnimations()
|
||||
}
|
||||
|
||||
self.foregroundGradientLayer.add(animation, forKey: "movement")
|
||||
CATransaction.commit()
|
||||
}*/
|
||||
}
|
||||
|
||||
func updateAnimations() {
|
||||
if !isCurrentlyInHierarchy {
|
||||
if !self.isCurrentlyInHierarchy || !self.animationsEnabled {
|
||||
self.foregroundGradientLayer.removeAllAnimations()
|
||||
self.maskCurveView.stopAnimating()
|
||||
return
|
||||
}
|
||||
self.setupGradientAnimations()
|
||||
if isCurrentlyInHierarchy {
|
||||
} else {
|
||||
self.maskCurveView.startAnimating()
|
||||
}
|
||||
}
|
||||
@ -182,6 +160,13 @@ public class CallStatusBarNodeImpl: CallStatusBarNode {
|
||||
public enum Content {
|
||||
case call(SharedAccountContext, Account, PresentationCall)
|
||||
case groupCall(SharedAccountContext, Account, PresentationGroupCall)
|
||||
|
||||
var sharedContext: SharedAccountContext {
|
||||
switch self {
|
||||
case let .call(sharedContext, _, _), let .groupCall(sharedContext, _, _):
|
||||
return sharedContext
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let backgroundNode: CallStatusBarBackgroundNode
|
||||
@ -252,6 +237,7 @@ public class CallStatusBarNodeImpl: CallStatusBarNode {
|
||||
|
||||
public func update(content: Content) {
|
||||
self.currentContent = content
|
||||
self.backgroundNode.animationsEnabled = content.sharedContext.energyUsageSettings.fullTranslucency
|
||||
if self.isCurrentlyInHierarchy {
|
||||
self.update()
|
||||
}
|
||||
@ -553,6 +539,7 @@ private final class VoiceCurveView: UIView {
|
||||
private let smallCurve: CurveView
|
||||
private let mediumCurve: CurveView
|
||||
private let bigCurve: CurveView
|
||||
private var solidView: UIView?
|
||||
|
||||
private let maxLevel: CGFloat
|
||||
|
||||
@ -604,11 +591,11 @@ private final class VoiceCurveView: UIView {
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
addSubview(bigCurve)
|
||||
addSubview(mediumCurve)
|
||||
addSubview(smallCurve)
|
||||
self.addSubview(self.bigCurve)
|
||||
self.addSubview(self.mediumCurve)
|
||||
self.addSubview(self.smallCurve)
|
||||
|
||||
displayLinkAnimator = ConstantDisplayLinkAnimator() { [weak self] in
|
||||
self.displayLinkAnimator = ConstantDisplayLinkAnimator() { [weak self] in
|
||||
guard let strongSelf = self else { return }
|
||||
|
||||
strongSelf.presentationAudioLevel = strongSelf.presentationAudioLevel * 0.9 + strongSelf.audioLevel * 0.1
|
||||
@ -624,28 +611,33 @@ private final class VoiceCurveView: UIView {
|
||||
}
|
||||
|
||||
public func setColor(_ color: UIColor) {
|
||||
smallCurve.setColor(color.withAlphaComponent(1.0))
|
||||
mediumCurve.setColor(color.withAlphaComponent(0.55))
|
||||
bigCurve.setColor(color.withAlphaComponent(0.35))
|
||||
self.smallCurve.setColor(color.withAlphaComponent(1.0))
|
||||
self.mediumCurve.setColor(color.withAlphaComponent(0.55))
|
||||
self.bigCurve.setColor(color.withAlphaComponent(0.35))
|
||||
}
|
||||
|
||||
public func updateLevel(_ level: CGFloat) {
|
||||
let normalizedLevel = min(1, max(level / maxLevel, 0))
|
||||
let normalizedLevel = min(1, max(level / self.maxLevel, 0))
|
||||
|
||||
smallCurve.updateSpeedLevel(to: normalizedLevel)
|
||||
mediumCurve.updateSpeedLevel(to: normalizedLevel)
|
||||
bigCurve.updateSpeedLevel(to: normalizedLevel)
|
||||
self.smallCurve.updateSpeedLevel(to: normalizedLevel)
|
||||
self.mediumCurve.updateSpeedLevel(to: normalizedLevel)
|
||||
self.bigCurve.updateSpeedLevel(to: normalizedLevel)
|
||||
|
||||
audioLevel = normalizedLevel
|
||||
self.audioLevel = normalizedLevel
|
||||
}
|
||||
|
||||
public func startAnimating() {
|
||||
guard !isAnimating else { return }
|
||||
isAnimating = true
|
||||
guard !self.isAnimating else { return }
|
||||
self.isAnimating = true
|
||||
|
||||
updateCurvesState()
|
||||
if let solidView = self.solidView {
|
||||
solidView.removeFromSuperview()
|
||||
self.solidView = nil
|
||||
}
|
||||
|
||||
displayLinkAnimator?.isPaused = false
|
||||
self.updateCurvesState()
|
||||
|
||||
self.displayLinkAnimator?.isPaused = false
|
||||
}
|
||||
|
||||
public func stopAnimating() {
|
||||
@ -653,36 +645,48 @@ private final class VoiceCurveView: UIView {
|
||||
}
|
||||
|
||||
public func stopAnimating(duration: Double) {
|
||||
guard isAnimating else { return }
|
||||
isAnimating = false
|
||||
guard self.isAnimating else { return }
|
||||
self.isAnimating = false
|
||||
|
||||
updateCurvesState()
|
||||
self.updateCurvesState()
|
||||
|
||||
displayLinkAnimator?.isPaused = true
|
||||
self.displayLinkAnimator?.isPaused = true
|
||||
}
|
||||
|
||||
func disableCurves() {
|
||||
self.smallCurve.isHidden = true
|
||||
self.mediumCurve.isHidden = true
|
||||
self.bigCurve.isHidden = true
|
||||
|
||||
let view = UIView(frame: .zero)
|
||||
view.backgroundColor = .white
|
||||
self.addSubview(view)
|
||||
self.solidView = view
|
||||
}
|
||||
|
||||
private func updateCurvesState() {
|
||||
if isAnimating {
|
||||
if smallCurve.frame.size != .zero {
|
||||
smallCurve.startAnimating()
|
||||
mediumCurve.startAnimating()
|
||||
bigCurve.startAnimating()
|
||||
if self.isAnimating {
|
||||
if self.smallCurve.frame.size != .zero {
|
||||
self.smallCurve.startAnimating()
|
||||
self.mediumCurve.startAnimating()
|
||||
self.bigCurve.startAnimating()
|
||||
}
|
||||
} else {
|
||||
smallCurve.stopAnimating()
|
||||
mediumCurve.stopAnimating()
|
||||
bigCurve.stopAnimating()
|
||||
self.smallCurve.stopAnimating()
|
||||
self.mediumCurve.stopAnimating()
|
||||
self.bigCurve.stopAnimating()
|
||||
}
|
||||
}
|
||||
|
||||
override public func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
smallCurve.frame = bounds
|
||||
mediumCurve.frame = bounds
|
||||
bigCurve.frame = bounds
|
||||
self.smallCurve.frame = self.bounds
|
||||
self.mediumCurve.frame = self.bounds
|
||||
self.bigCurve.frame = self.bounds
|
||||
self.solidView?.frame = CGRect(origin: .zero, size: CGSize(width: self.bounds.width, height: self.bounds.height - 18.0))
|
||||
|
||||
updateCurvesState()
|
||||
self.updateCurvesState()
|
||||
}
|
||||
}
|
||||
|
||||
@ -721,7 +725,6 @@ final class CurveView: UIView {
|
||||
return layer
|
||||
}()
|
||||
|
||||
|
||||
override var frame: CGRect {
|
||||
didSet {
|
||||
if self.frame.size != oldValue.size {
|
||||
@ -764,11 +767,7 @@ final class CurveView: UIView {
|
||||
}
|
||||
|
||||
func updateSpeedLevel(to newSpeedLevel: CGFloat) {
|
||||
speedLevel = max(speedLevel, newSpeedLevel)
|
||||
|
||||
// if abs(lastSpeedLevel - newSpeedLevel) > 0.45 {
|
||||
// animateToNewShape()
|
||||
// }
|
||||
self.speedLevel = max(self.speedLevel, newSpeedLevel)
|
||||
}
|
||||
|
||||
func startAnimating() {
|
||||
|
@ -130,6 +130,12 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode {
|
||||
}
|
||||
}
|
||||
|
||||
var animationsEnabled: Bool = true {
|
||||
didSet {
|
||||
self.backgroundNode.animationsEnabled = self.animationsEnabled
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
self.bottomNode = ASDisplayNode()
|
||||
self.bottomNode.isUserInteractionEnabled = false
|
||||
@ -833,7 +839,9 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
private func playMuteAnimation() {
|
||||
self.maskBlobView.startAnimating()
|
||||
if self.animationsEnabled {
|
||||
self.maskBlobView.startAnimating()
|
||||
}
|
||||
self.maskBlobView.layer.animateScale(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -862,7 +870,9 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
|
||||
self.maskGradientLayer.removeAllAnimations()
|
||||
self.updateGlowAndGradientAnimations(type: .connecting, previousType: nil)
|
||||
|
||||
self.maskBlobView.startAnimating()
|
||||
if self.animationsEnabled {
|
||||
self.maskBlobView.startAnimating()
|
||||
}
|
||||
self.maskBlobView.layer.animateScale(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -915,7 +925,9 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
|
||||
self.updateGlowAndGradientAnimations(type: active ? .speaking : .active, previousType: nil)
|
||||
|
||||
self.maskBlobView.isHidden = false
|
||||
self.maskBlobView.startAnimating()
|
||||
if self.animationsEnabled {
|
||||
self.maskBlobView.startAnimating()
|
||||
}
|
||||
self.maskBlobView.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.45)
|
||||
}
|
||||
|
||||
@ -968,7 +980,9 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
|
||||
if case .connecting = self.state {
|
||||
} else {
|
||||
self.maskBlobView.isHidden = false
|
||||
self.maskBlobView.startAnimating()
|
||||
if self.animationsEnabled {
|
||||
self.maskBlobView.startAnimating()
|
||||
}
|
||||
self.maskBlobView.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.45)
|
||||
}
|
||||
|
||||
@ -1038,7 +1052,9 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
|
||||
self.maskCircleLayer.animateSpring(from: previousPath as AnyObject, to: largerCirclePath as AnyObject, keyPath: "path", duration: 0.6, initialVelocity: 0.0, damping: 100.0)
|
||||
|
||||
self.maskBlobView.isHidden = false
|
||||
self.maskBlobView.startAnimating()
|
||||
if self.animationsEnabled {
|
||||
self.maskBlobView.startAnimating()
|
||||
}
|
||||
self.maskBlobView.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.6, damping: 100.0)
|
||||
|
||||
self.disableGlowAnimations = true
|
||||
@ -1048,6 +1064,12 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
|
||||
})
|
||||
}
|
||||
|
||||
var animationsEnabled: Bool = true {
|
||||
didSet {
|
||||
self.updateAnimations()
|
||||
}
|
||||
}
|
||||
|
||||
var isActive = false
|
||||
func updateAnimations() {
|
||||
if !self.isCurrentlyInHierarchy {
|
||||
@ -1058,7 +1080,13 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
|
||||
self.maskBlobView.stopAnimating()
|
||||
return
|
||||
}
|
||||
self.setupGradientAnimations()
|
||||
|
||||
if !self.animationsEnabled {
|
||||
self.foregroundGradientLayer.removeAllAnimations()
|
||||
self.maskBlobView.stopAnimating()
|
||||
} else {
|
||||
self.setupGradientAnimations()
|
||||
}
|
||||
|
||||
switch self.state {
|
||||
case .connecting:
|
||||
@ -1093,7 +1121,9 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
|
||||
}
|
||||
self.transition = nil
|
||||
} else {
|
||||
self.maskBlobView.startAnimating()
|
||||
if self.animationsEnabled {
|
||||
self.maskBlobView.startAnimating()
|
||||
}
|
||||
}
|
||||
case .disabled:
|
||||
self.updatedActive?(true)
|
||||
@ -1118,7 +1148,9 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode {
|
||||
self.maskProgressLayer.isHidden = true
|
||||
self.maskGradientLayer.isHidden = false
|
||||
self.maskBlobView.isHidden = false
|
||||
self.maskBlobView.startAnimating()
|
||||
if self.animationsEnabled {
|
||||
self.maskBlobView.startAnimating()
|
||||
}
|
||||
self.maskBlobView.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.45)
|
||||
}
|
||||
}
|
||||
@ -1287,12 +1319,12 @@ private final class VoiceBlobView: UIView {
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
addSubnode(hierarchyTrackingNode)
|
||||
self.addSubnode(self.hierarchyTrackingNode)
|
||||
|
||||
addSubview(bigBlob)
|
||||
addSubview(mediumBlob)
|
||||
self.addSubview(self.bigBlob)
|
||||
self.addSubview(self.mediumBlob)
|
||||
|
||||
displayLinkAnimator = ConstantDisplayLinkAnimator() { [weak self] in
|
||||
self.displayLinkAnimator = ConstantDisplayLinkAnimator() { [weak self] in
|
||||
guard let strongSelf = self else { return }
|
||||
|
||||
if !strongSelf.isCurrentlyInHierarchy {
|
||||
@ -1317,29 +1349,29 @@ private final class VoiceBlobView: UIView {
|
||||
}
|
||||
|
||||
public func setColor(_ color: UIColor) {
|
||||
mediumBlob.setColor(color.withAlphaComponent(0.5))
|
||||
bigBlob.setColor(color.withAlphaComponent(0.21))
|
||||
self.mediumBlob.setColor(color.withAlphaComponent(0.5))
|
||||
self.bigBlob.setColor(color.withAlphaComponent(0.21))
|
||||
}
|
||||
|
||||
public func updateLevel(_ level: CGFloat, immediately: Bool) {
|
||||
let normalizedLevel = min(1, max(level / maxLevel, 0))
|
||||
|
||||
mediumBlob.updateSpeedLevel(to: normalizedLevel)
|
||||
bigBlob.updateSpeedLevel(to: normalizedLevel)
|
||||
self.mediumBlob.updateSpeedLevel(to: normalizedLevel)
|
||||
self.bigBlob.updateSpeedLevel(to: normalizedLevel)
|
||||
|
||||
audioLevel = normalizedLevel
|
||||
self.audioLevel = normalizedLevel
|
||||
if immediately {
|
||||
presentationAudioLevel = normalizedLevel
|
||||
self.presentationAudioLevel = normalizedLevel
|
||||
}
|
||||
}
|
||||
|
||||
public func startAnimating() {
|
||||
guard !isAnimating else { return }
|
||||
isAnimating = true
|
||||
guard !self.isAnimating else { return }
|
||||
self.isAnimating = true
|
||||
|
||||
updateBlobsState()
|
||||
self.updateBlobsState()
|
||||
|
||||
displayLinkAnimator?.isPaused = false
|
||||
self.displayLinkAnimator?.isPaused = false
|
||||
}
|
||||
|
||||
public func stopAnimating() {
|
||||
@ -1348,32 +1380,32 @@ private final class VoiceBlobView: UIView {
|
||||
|
||||
public func stopAnimating(duration: Double) {
|
||||
guard isAnimating else { return }
|
||||
isAnimating = false
|
||||
self.isAnimating = false
|
||||
|
||||
updateBlobsState()
|
||||
self.updateBlobsState()
|
||||
|
||||
displayLinkAnimator?.isPaused = true
|
||||
self.displayLinkAnimator?.isPaused = true
|
||||
}
|
||||
|
||||
private func updateBlobsState() {
|
||||
if isAnimating {
|
||||
if mediumBlob.frame.size != .zero {
|
||||
mediumBlob.startAnimating()
|
||||
bigBlob.startAnimating()
|
||||
if self.isAnimating {
|
||||
if self.mediumBlob.frame.size != .zero {
|
||||
self.mediumBlob.startAnimating()
|
||||
self.bigBlob.startAnimating()
|
||||
}
|
||||
} else {
|
||||
mediumBlob.stopAnimating()
|
||||
bigBlob.stopAnimating()
|
||||
self.mediumBlob.stopAnimating()
|
||||
self.bigBlob.stopAnimating()
|
||||
}
|
||||
}
|
||||
|
||||
override public func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
mediumBlob.frame = bounds
|
||||
bigBlob.frame = bounds
|
||||
self.mediumBlob.frame = bounds
|
||||
self.bigBlob.frame = bounds
|
||||
|
||||
updateBlobsState()
|
||||
self.updateBlobsState()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1451,10 +1483,6 @@ final class BlobView: UIView {
|
||||
|
||||
func updateSpeedLevel(to newSpeedLevel: CGFloat) {
|
||||
self.speedLevel = max(self.speedLevel, newSpeedLevel)
|
||||
|
||||
// if abs(lastSpeedLevel - newSpeedLevel) > 0.45 {
|
||||
// animateToNewShape()
|
||||
// }
|
||||
}
|
||||
|
||||
func startAnimating() {
|
||||
@ -1540,7 +1568,7 @@ final class BlobView: UIView {
|
||||
|
||||
CATransaction.begin()
|
||||
CATransaction.setDisableActions(true)
|
||||
shapeLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
|
||||
self.shapeLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
|
||||
CATransaction.commit()
|
||||
}
|
||||
}
|
||||
|
@ -1064,6 +1064,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
self.switchCameraButton.isUserInteractionEnabled = false
|
||||
self.leaveButton = CallControllerButtonItemNode()
|
||||
self.actionButton = VoiceChatActionButton()
|
||||
self.actionButton.animationsEnabled = sharedContext.energyUsageSettings.fullTranslucency
|
||||
|
||||
if self.isScheduling {
|
||||
self.cameraButton.alpha = 0.0
|
||||
|
@ -15,100 +15,101 @@ func managedConfigurationUpdates(accountManager: AccountManager<TelegramAccountM
|
||||
return postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
switch result {
|
||||
case let .config(flags, _, _, _, _, dcOptions, _, chatSizeMax, megagroupSizeMax, forwardedCountMax, _, _, _, _, _, _, _, _, editTimeLimit, revokeTimeLimit, revokePmTimeLimit, _, stickersRecentLimit, _, _, _, _, _, _, _, autoupdateUrlPrefix, gifSearchUsername, venueSearchUsername, imgSearchUsername, _, captionLengthMax, _, webfileDcId, suggestedLangCode, langPackVersion, baseLangPackVersion, reactionsDefault, autologinToken):
|
||||
let _ = autologinToken
|
||||
var addressList: [Int: [MTDatacenterAddress]] = [:]
|
||||
for option in dcOptions {
|
||||
switch option {
|
||||
case let .dcOption(flags, id, ipAddress, port, secret):
|
||||
let preferForMedia = (flags & (1 << 1)) != 0
|
||||
if addressList[Int(id)] == nil {
|
||||
addressList[Int(id)] = []
|
||||
}
|
||||
let restrictToTcp = (flags & (1 << 2)) != 0
|
||||
let isCdn = (flags & (1 << 3)) != 0
|
||||
let preferForProxy = (flags & (1 << 4)) != 0
|
||||
addressList[Int(id)]!.append(MTDatacenterAddress(ip: ipAddress, port: UInt16(port), preferForMedia: preferForMedia, restrictToTcp: restrictToTcp, cdn: isCdn, preferForProxy: preferForProxy, secret: secret?.makeData()))
|
||||
}
|
||||
}
|
||||
network.context.performBatchUpdates {
|
||||
for (id, list) in addressList {
|
||||
network.context.updateAddressSetForDatacenter(withId: id, addressSet: MTDatacenterAddressSet(addressList: list), forceUpdateSchemes: false)
|
||||
}
|
||||
}
|
||||
|
||||
let blockedMode = (flags & 8) != 0
|
||||
|
||||
updateNetworkSettingsInteractively(transaction: transaction, network: network, { settings in
|
||||
var settings = settings
|
||||
settings.reducedBackupDiscoveryTimeout = blockedMode
|
||||
settings.applicationUpdateUrlPrefix = autoupdateUrlPrefix
|
||||
return settings
|
||||
})
|
||||
|
||||
updateRemoteStorageConfiguration(transaction: transaction, configuration: RemoteStorageConfiguration(webDocumentsHostDatacenterId: webfileDcId))
|
||||
|
||||
transaction.updatePreferencesEntry(key: PreferencesKeys.suggestedLocalization, { entry in
|
||||
var currentLanguageCode: String?
|
||||
if let entry = entry?.get(SuggestedLocalizationEntry.self) {
|
||||
currentLanguageCode = entry.languageCode
|
||||
}
|
||||
if currentLanguageCode != suggestedLangCode {
|
||||
if let suggestedLangCode = suggestedLangCode {
|
||||
return PreferencesEntry(SuggestedLocalizationEntry(languageCode: suggestedLangCode, isSeen: false))
|
||||
} else {
|
||||
return nil
|
||||
var addressList: [Int: [MTDatacenterAddress]] = [:]
|
||||
for option in dcOptions {
|
||||
switch option {
|
||||
case let .dcOption(flags, id, ipAddress, port, secret):
|
||||
let preferForMedia = (flags & (1 << 1)) != 0
|
||||
if addressList[Int(id)] == nil {
|
||||
addressList[Int(id)] = []
|
||||
}
|
||||
}
|
||||
return entry
|
||||
})
|
||||
|
||||
updateLimitsConfiguration(transaction: transaction, configuration: LimitsConfiguration(maxPinnedChatCount: 5, maxArchivedPinnedChatCount: 5, maxGroupMemberCount: chatSizeMax, maxSupergroupMemberCount: megagroupSizeMax, maxMessageForwardBatchSize: forwardedCountMax, maxSavedGifCount: 10, maxRecentStickerCount: stickersRecentLimit, maxFavedStickerCount: 10, maxMessageEditingInterval: editTimeLimit, maxMediaCaptionLength: captionLengthMax, canRemoveIncomingMessagesInPrivateChats: (flags & (1 << 6)) != 0, maxMessageRevokeInterval: revokeTimeLimit, maxMessageRevokeIntervalInPrivateChats: revokePmTimeLimit))
|
||||
|
||||
updateSearchBotsConfiguration(transaction: transaction, configuration: SearchBotsConfiguration(imageBotUsername: imgSearchUsername, gifBotUsername: gifSearchUsername, venueBotUsername: venueSearchUsername))
|
||||
|
||||
if let defaultReaction = reactionsDefault, let reaction = MessageReaction.Reaction(apiReaction: defaultReaction) {
|
||||
updateReactionSettings(transaction: transaction, { settings in
|
||||
var settings = settings
|
||||
settings.quickReaction = reaction
|
||||
return settings
|
||||
})
|
||||
let restrictToTcp = (flags & (1 << 2)) != 0
|
||||
let isCdn = (flags & (1 << 3)) != 0
|
||||
let preferForProxy = (flags & (1 << 4)) != 0
|
||||
addressList[Int(id)]!.append(MTDatacenterAddress(ip: ipAddress, port: UInt16(port), preferForMedia: preferForMedia, restrictToTcp: restrictToTcp, cdn: isCdn, preferForProxy: preferForProxy, secret: secret?.makeData()))
|
||||
}
|
||||
}
|
||||
network.context.performBatchUpdates {
|
||||
for (id, list) in addressList {
|
||||
network.context.updateAddressSetForDatacenter(withId: id, addressSet: MTDatacenterAddressSet(addressList: list), forceUpdateSchemes: false)
|
||||
}
|
||||
}
|
||||
|
||||
let messageAutoremoveSeconds: Int32?
|
||||
switch defaultHistoryTtl {
|
||||
case let .defaultHistoryTTL(period):
|
||||
if period != 0 {
|
||||
messageAutoremoveSeconds = period
|
||||
let blockedMode = (flags & 8) != 0
|
||||
|
||||
updateNetworkSettingsInteractively(transaction: transaction, network: network, { settings in
|
||||
var settings = settings
|
||||
settings.reducedBackupDiscoveryTimeout = blockedMode
|
||||
settings.applicationUpdateUrlPrefix = autoupdateUrlPrefix
|
||||
return settings
|
||||
})
|
||||
|
||||
updateRemoteStorageConfiguration(transaction: transaction, configuration: RemoteStorageConfiguration(webDocumentsHostDatacenterId: webfileDcId))
|
||||
|
||||
transaction.updatePreferencesEntry(key: PreferencesKeys.suggestedLocalization, { entry in
|
||||
var currentLanguageCode: String?
|
||||
if let entry = entry?.get(SuggestedLocalizationEntry.self) {
|
||||
currentLanguageCode = entry.languageCode
|
||||
}
|
||||
if currentLanguageCode != suggestedLangCode {
|
||||
if let suggestedLangCode = suggestedLangCode {
|
||||
return PreferencesEntry(SuggestedLocalizationEntry(languageCode: suggestedLangCode, isSeen: false))
|
||||
} else {
|
||||
messageAutoremoveSeconds = nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
updateGlobalMessageAutoremoveTimeoutSettings(transaction: transaction, { settings in
|
||||
return entry
|
||||
})
|
||||
|
||||
updateLimitsConfiguration(transaction: transaction, configuration: LimitsConfiguration(maxPinnedChatCount: 5, maxArchivedPinnedChatCount: 5, maxGroupMemberCount: chatSizeMax, maxSupergroupMemberCount: megagroupSizeMax, maxMessageForwardBatchSize: forwardedCountMax, maxSavedGifCount: 10, maxRecentStickerCount: stickersRecentLimit, maxFavedStickerCount: 10, maxMessageEditingInterval: editTimeLimit, maxMediaCaptionLength: captionLengthMax, canRemoveIncomingMessagesInPrivateChats: (flags & (1 << 6)) != 0, maxMessageRevokeInterval: revokeTimeLimit, maxMessageRevokeIntervalInPrivateChats: revokePmTimeLimit))
|
||||
|
||||
updateSearchBotsConfiguration(transaction: transaction, configuration: SearchBotsConfiguration(imageBotUsername: imgSearchUsername, gifBotUsername: gifSearchUsername, venueBotUsername: venueSearchUsername))
|
||||
|
||||
updateLinksConfiguration(transaction: transaction, configuration: LinksConfiguration(autologinToken: autologinToken))
|
||||
|
||||
if let defaultReaction = reactionsDefault, let reaction = MessageReaction.Reaction(apiReaction: defaultReaction) {
|
||||
updateReactionSettings(transaction: transaction, { settings in
|
||||
var settings = settings
|
||||
settings.messageAutoremoveTimeout = messageAutoremoveSeconds
|
||||
settings.quickReaction = reaction
|
||||
return settings
|
||||
})
|
||||
|
||||
return accountManager.transaction { transaction -> Signal<Void, NoError> in
|
||||
let (primary, secondary) = getLocalization(transaction)
|
||||
var invalidateLocalization = false
|
||||
if primary.version != langPackVersion {
|
||||
}
|
||||
|
||||
let messageAutoremoveSeconds: Int32?
|
||||
switch defaultHistoryTtl {
|
||||
case let .defaultHistoryTTL(period):
|
||||
if period != 0 {
|
||||
messageAutoremoveSeconds = period
|
||||
} else {
|
||||
messageAutoremoveSeconds = nil
|
||||
}
|
||||
}
|
||||
updateGlobalMessageAutoremoveTimeoutSettings(transaction: transaction, { settings in
|
||||
var settings = settings
|
||||
settings.messageAutoremoveTimeout = messageAutoremoveSeconds
|
||||
return settings
|
||||
})
|
||||
|
||||
return accountManager.transaction { transaction -> Signal<Void, NoError> in
|
||||
let (primary, secondary) = getLocalization(transaction)
|
||||
var invalidateLocalization = false
|
||||
if primary.version != langPackVersion {
|
||||
invalidateLocalization = true
|
||||
}
|
||||
if let secondary = secondary, let baseLangPackVersion = baseLangPackVersion {
|
||||
if secondary.version != baseLangPackVersion {
|
||||
invalidateLocalization = true
|
||||
}
|
||||
if let secondary = secondary, let baseLangPackVersion = baseLangPackVersion {
|
||||
if secondary.version != baseLangPackVersion {
|
||||
invalidateLocalization = true
|
||||
}
|
||||
}
|
||||
if invalidateLocalization {
|
||||
return postbox.transaction { transaction -> Void in
|
||||
addSynchronizeLocalizationUpdatesOperation(transaction: transaction)
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|> switchToLatest
|
||||
if invalidateLocalization {
|
||||
return postbox.transaction { transaction -> Void in
|
||||
addSynchronizeLocalizationUpdatesOperation(transaction: transaction)
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|> switchToLatest
|
||||
}
|
||||
}
|
||||
|> switchToLatest
|
||||
|
@ -0,0 +1,44 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
|
||||
public struct LinksConfiguration: Equatable, Codable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case autologinToken
|
||||
}
|
||||
|
||||
public let autologinToken: String?
|
||||
|
||||
public static var defaultValue: LinksConfiguration {
|
||||
return LinksConfiguration(autologinToken: nil)
|
||||
}
|
||||
|
||||
public init(autologinToken: String?) {
|
||||
self.autologinToken = autologinToken
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.autologinToken = try container.decodeIfPresent(String.self, forKey: .autologinToken)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encodeIfPresent(self.autologinToken, forKey: .autologinToken)
|
||||
}
|
||||
}
|
||||
|
||||
public func currentLinksConfiguration(transaction: Transaction) -> LinksConfiguration {
|
||||
if let entry = transaction.getPreferencesEntry(key: PreferencesKeys.linksConfiguration)?.get(LinksConfiguration.self) {
|
||||
return entry
|
||||
} else {
|
||||
return LinksConfiguration.defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
func updateLinksConfiguration(transaction: Transaction, configuration: LinksConfiguration) {
|
||||
if currentLinksConfiguration(transaction: transaction) != configuration {
|
||||
transaction.setPreferencesEntry(key: PreferencesKeys.linksConfiguration, value: PreferencesEntry(configuration))
|
||||
}
|
||||
}
|
@ -255,6 +255,7 @@ private enum PreferencesKeyValues: Int32 {
|
||||
case premiumPromo = 26
|
||||
case globalMessageAutoremoveTimeoutSettings = 27
|
||||
case accountSpecificCacheStorageSettings = 28
|
||||
case linksConfiguration = 29
|
||||
}
|
||||
|
||||
public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey {
|
||||
@ -395,6 +396,12 @@ public struct PreferencesKeys {
|
||||
key.setInt32(0, value: PreferencesKeyValues.accountSpecificCacheStorageSettings.rawValue)
|
||||
return key
|
||||
}()
|
||||
|
||||
public static let linksConfiguration: ValueBoxKey = {
|
||||
let key = ValueBoxKey(length: 4)
|
||||
key.setInt32(0, value: PreferencesKeyValues.linksConfiguration.rawValue)
|
||||
return key
|
||||
}()
|
||||
}
|
||||
|
||||
private enum SharedDataKeyValues: Int32 {
|
||||
|
@ -185,6 +185,26 @@ public extension EngineConfiguration.SearchBots {
|
||||
}
|
||||
}
|
||||
|
||||
public extension EngineConfiguration {
|
||||
struct Links {
|
||||
public var autologinToken: String?
|
||||
|
||||
public init(
|
||||
autologinToken: String?
|
||||
) {
|
||||
self.autologinToken = autologinToken
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension EngineConfiguration.Links {
|
||||
init(_ configuration: LinksConfiguration) {
|
||||
self.init(
|
||||
autologinToken: configuration.autologinToken
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public extension TelegramEngine.EngineData.Item {
|
||||
enum Configuration {
|
||||
public struct App: TelegramEngineDataItem, PostboxViewDataItem {
|
||||
@ -400,5 +420,26 @@ public extension TelegramEngine.EngineData.Item {
|
||||
return settings.messageAutoremoveTimeout
|
||||
}
|
||||
}
|
||||
|
||||
public struct Links: TelegramEngineDataItem, PostboxViewDataItem {
|
||||
public typealias Result = EngineConfiguration.Links
|
||||
|
||||
public init() {
|
||||
}
|
||||
|
||||
var key: PostboxViewKey {
|
||||
return .preferences(keys: Set([PreferencesKeys.linksConfiguration]))
|
||||
}
|
||||
|
||||
func extract(view: PostboxView) -> Result {
|
||||
guard let view = view as? PreferencesView else {
|
||||
preconditionFailure()
|
||||
}
|
||||
guard let value = view.values[PreferencesKeys.linksConfiguration]?.get(LinksConfiguration.self) else {
|
||||
return EngineConfiguration.Links(LinksConfiguration.defaultValue)
|
||||
}
|
||||
return EngineConfiguration.Links(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1628,6 +1628,7 @@ public final class EntityKeyboardTopPanelComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
private var didReorderItems = false
|
||||
private func beginReordering(itemView: ComponentHostView<EntityKeyboardTopPanelItemEnvironment>) {
|
||||
if let currentReorderingItemView = self.currentReorderingItemView {
|
||||
if let componentView = currentReorderingItemView.componentView {
|
||||
@ -1702,7 +1703,9 @@ public final class EntityKeyboardTopPanelComponent: Component {
|
||||
self.currentReorderingItemId = nil
|
||||
self.temporaryReorderingOrderIndex = nil
|
||||
|
||||
self.component?.reorderItems(self.items)
|
||||
if self.didReorderItems {
|
||||
self.component?.reorderItems(self.items)
|
||||
}
|
||||
//self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring)))
|
||||
}
|
||||
|
||||
@ -1721,14 +1724,14 @@ public final class EntityKeyboardTopPanelComponent: Component {
|
||||
let containerFrame = itemLayout.containerFrame(at: i)
|
||||
if containerFrame.intersects(localReorderingItemFrame) {
|
||||
let temporaryReorderingOrderIndex: (id: AnyHashable, index: Int) = (currentReorderingItemId, i)
|
||||
let hadPrevous = self.temporaryReorderingOrderIndex != nil
|
||||
let hadPrevious = self.temporaryReorderingOrderIndex != nil
|
||||
if self.temporaryReorderingOrderIndex?.id != temporaryReorderingOrderIndex.id || self.temporaryReorderingOrderIndex?.index != temporaryReorderingOrderIndex.index {
|
||||
self.temporaryReorderingOrderIndex = temporaryReorderingOrderIndex
|
||||
|
||||
if hadPrevous {
|
||||
if hadPrevious {
|
||||
self.reorderingHapticFeedback.tap()
|
||||
}
|
||||
|
||||
self.didReorderItems = true
|
||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring)))
|
||||
}
|
||||
break
|
||||
|
@ -3506,10 +3506,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
if let _ = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) {
|
||||
var messageId = message.id
|
||||
if let forwardInfo = message.forwardInfo, let sourceMessageId = forwardInfo.sourceMessageId, case let .replyThread(threadMessage) = strongSelf.chatLocation, threadMessage.isChannelPost {
|
||||
messageId = sourceMessageId
|
||||
}
|
||||
if let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId) {
|
||||
let _ = strongSelf.controllerInteraction?.openMessage(message, .timecode(Double(timestamp)))
|
||||
} else {
|
||||
strongSelf.navigateToMessage(messageLocation: .id(message.id, Double(timestamp)), animated: true, forceInCurrentChat: true)
|
||||
strongSelf.navigateToMessage(messageLocation: .id(messageId, Double(timestamp)), animated: true, forceInCurrentChat: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,10 @@ import ContextUI
|
||||
import SliderContextItem
|
||||
import UndoUI
|
||||
|
||||
private func normalizeValue(_ value: CGFloat) -> CGFloat {
|
||||
return round(value * 10.0) / 10.0
|
||||
}
|
||||
|
||||
private func generateBackground(theme: PresentationTheme) -> UIImage? {
|
||||
return generateImage(CGSize(width: 20.0, height: 10.0 + 8.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
@ -1030,7 +1034,8 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
||||
var presetItems: [ContextMenuItem] = []
|
||||
|
||||
let previousValue = self.currentRate?.doubleValue ?? 1.0
|
||||
let sliderItem: ContextMenuItem = .custom(SliderContextItem(minValue: 0.5, maxValue: 2.5, value: previousValue, valueChanged: { [weak self] newValue, finished in
|
||||
let sliderItem: ContextMenuItem = .custom(SliderContextItem(minValue: 0.2, maxValue: 2.5, value: previousValue, valueChanged: { [weak self] newValue, finished in
|
||||
let newValue = normalizeValue(newValue)
|
||||
self?.control?(.setBaseRate(AudioPlaybackRate(newValue)))
|
||||
if finished {
|
||||
scheduleTooltip(.sliderCommit(previousValue, newValue))
|
||||
@ -1082,7 +1087,21 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
||||
let presentationData = self.presentationData
|
||||
let text: String?
|
||||
let rate: CGFloat?
|
||||
if baseRate == .x1 {
|
||||
if case let .sliderCommit(previousValue, newValue) = changeType {
|
||||
let value = String(format: "%0.1f", baseRate.doubleValue)
|
||||
if baseRate == .x1 {
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipNormal
|
||||
} else {
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipCustom(value).string
|
||||
}
|
||||
if newValue > previousValue {
|
||||
rate = .infinity
|
||||
} else if newValue < previousValue {
|
||||
rate = -.infinity
|
||||
} else {
|
||||
rate = nil
|
||||
}
|
||||
} else if baseRate == .x1 {
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipNormal
|
||||
rate = 1.0
|
||||
} else if baseRate == .x1_5 {
|
||||
@ -1092,19 +1111,8 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipSpeedUp
|
||||
rate = 2.0
|
||||
} else {
|
||||
let value = String(format: "%0.1f", baseRate.doubleValue)
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipCustom(value).string
|
||||
if case let .sliderCommit(previousValue, newValue) = changeType {
|
||||
if newValue > previousValue {
|
||||
rate = .infinity
|
||||
} else if newValue < previousValue {
|
||||
rate = -.infinity
|
||||
} else {
|
||||
rate = nil
|
||||
}
|
||||
} else {
|
||||
rate = nil
|
||||
}
|
||||
text = nil
|
||||
rate = nil
|
||||
}
|
||||
var showTooltip = true
|
||||
if case .sliderChange = changeType {
|
||||
|
@ -293,7 +293,21 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let text: String?
|
||||
let rate: CGFloat?
|
||||
if baseRate == .x1 {
|
||||
if case let .sliderCommit(previousValue, newValue) = changeType {
|
||||
let value = String(format: "%0.1f", baseRate.doubleValue)
|
||||
if baseRate == .x1 {
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipNormal
|
||||
} else {
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipCustom(value).string
|
||||
}
|
||||
if newValue > previousValue {
|
||||
rate = .infinity
|
||||
} else if newValue < previousValue {
|
||||
rate = -.infinity
|
||||
} else {
|
||||
rate = nil
|
||||
}
|
||||
} else if baseRate == .x1 {
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipNormal
|
||||
rate = 1.0
|
||||
} else if baseRate == .x1_5 {
|
||||
@ -303,19 +317,8 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipSpeedUp
|
||||
rate = 2.0
|
||||
} else {
|
||||
let value = String(format: "%0.1f", baseRate.doubleValue)
|
||||
text = presentationData.strings.Conversation_AudioRateTooltipCustom(value).string
|
||||
if case let .sliderCommit(previousValue, newValue) = changeType {
|
||||
if newValue > previousValue {
|
||||
rate = .infinity
|
||||
} else if newValue < previousValue {
|
||||
rate = -.infinity
|
||||
} else {
|
||||
rate = nil
|
||||
}
|
||||
} else {
|
||||
rate = nil
|
||||
}
|
||||
text = nil
|
||||
rate = nil
|
||||
}
|
||||
var showTooltip = true
|
||||
if case .sliderChange = changeType {
|
||||
|
@ -11008,14 +11008,22 @@ private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustom
|
||||
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin), size: textSize)
|
||||
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
|
||||
|
||||
if case let .user(user) = self.item.peer, let emojiStatus = user.emojiStatus {
|
||||
var iconContent: EmojiStatusComponent.Content?
|
||||
if case let .user(user) = self.item.peer {
|
||||
if let emojiStatus = user.emojiStatus {
|
||||
iconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 28.0, height: 28.0), placeholderColor: self.presentationData.theme.list.mediaPlaceholderColor, themeColor: self.presentationData.theme.list.itemAccentColor, loopMode: .forever)
|
||||
} else if user.isPremium {
|
||||
iconContent = .premium(color: self.presentationData.theme.list.itemAccentColor)
|
||||
}
|
||||
}
|
||||
if let iconContent {
|
||||
let emojiStatusSize = self.emojiStatusView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(EmojiStatusComponent(
|
||||
context: self.item.context,
|
||||
animationCache: self.item.context.animationCache,
|
||||
animationRenderer: self.item.context.animationRenderer,
|
||||
content: .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 28.0, height: 28.0), placeholderColor: self.presentationData.theme.list.mediaPlaceholderColor, themeColor: self.presentationData.theme.list.itemAccentColor, loopMode: .forever),
|
||||
content: iconContent,
|
||||
isVisibleForAnimations: true,
|
||||
action: nil
|
||||
)),
|
||||
|
@ -925,15 +925,13 @@ public func parseWallpaperUrl(_ url: String) -> WallpaperUrlParameter? {
|
||||
|
||||
private struct UrlHandlingConfiguration {
|
||||
static var defaultValue: UrlHandlingConfiguration {
|
||||
return UrlHandlingConfiguration(token: nil, domains: [], urlAuthDomains: [])
|
||||
return UrlHandlingConfiguration(domains: [], urlAuthDomains: [])
|
||||
}
|
||||
|
||||
public let token: String?
|
||||
public let domains: [String]
|
||||
public let urlAuthDomains: [String]
|
||||
|
||||
fileprivate init(token: String?, domains: [String], urlAuthDomains: [String]) {
|
||||
self.token = token
|
||||
fileprivate init(domains: [String], urlAuthDomains: [String]) {
|
||||
self.domains = domains
|
||||
self.urlAuthDomains = urlAuthDomains
|
||||
}
|
||||
@ -941,11 +939,10 @@ private struct UrlHandlingConfiguration {
|
||||
static func with(appConfiguration: AppConfiguration) -> UrlHandlingConfiguration {
|
||||
if let data = appConfiguration.data {
|
||||
let urlAuthDomains = data["url_auth_domains"] as? [String] ?? []
|
||||
if let token = data["autologin_token"] as? String, let domains = data["autologin_domains"] as? [String] {
|
||||
return UrlHandlingConfiguration(token: token, domains: domains, urlAuthDomains: urlAuthDomains)
|
||||
if let domains = data["autologin_domains"] as? [String] {
|
||||
return UrlHandlingConfiguration(domains: domains, urlAuthDomains: urlAuthDomains)
|
||||
}
|
||||
}
|
||||
//TODO:loc move to getConfig
|
||||
return .defaultValue
|
||||
}
|
||||
}
|
||||
@ -955,8 +952,8 @@ public func resolveUrlImpl(context: AccountContext, peerId: PeerId?, url: String
|
||||
|
||||
return ApplicationSpecificNotice.getSecretChatLinkPreviews(accountManager: context.sharedContext.accountManager)
|
||||
|> mapToSignal { linkPreviews -> Signal<ResolvedUrl, NoError> in
|
||||
return context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.App())
|
||||
|> mapToSignal { appConfiguration -> Signal<ResolvedUrl, NoError> in
|
||||
return context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.App(), TelegramEngine.EngineData.Item.Configuration.Links())
|
||||
|> mapToSignal { appConfiguration, linksConfiguration -> Signal<ResolvedUrl, NoError> in
|
||||
let urlHandlingConfiguration = UrlHandlingConfiguration.with(appConfiguration: appConfiguration)
|
||||
|
||||
var skipUrlAuth = skipUrlAuth
|
||||
@ -978,7 +975,7 @@ public func resolveUrlImpl(context: AccountContext, peerId: PeerId?, url: String
|
||||
if urlHandlingConfiguration.domains.contains(host), var components = URLComponents(string: url) {
|
||||
components.scheme = "https"
|
||||
var queryItems = components.queryItems ?? []
|
||||
queryItems.append(URLQueryItem(name: "autologin_token", value: urlHandlingConfiguration.token))
|
||||
queryItems.append(URLQueryItem(name: "autologin_token", value: linksConfiguration.autologinToken))
|
||||
components.queryItems = queryItems
|
||||
url = components.url?.absoluteString ?? url
|
||||
} else if !skipUrlAuth && urlHandlingConfiguration.urlAuthDomains.contains(host) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user