Merge commit '10b679446e7c0560683ee80747e0ded7cce20976'

This commit is contained in:
Ali 2023-03-02 13:51:00 +04:00
commit f173aaa5e5
18 changed files with 430 additions and 270 deletions

View File

@ -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 {

View File

@ -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))
}

View File

@ -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
}

View File

@ -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))

View File

@ -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 {

View File

@ -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() {

View File

@ -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()
}
}

View File

@ -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

View File

@ -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

View File

@ -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))
}
}

View File

@ -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 {

View File

@ -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)
}
}
}
}

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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
)),

View File

@ -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) {