mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-04 21:41:45 +00:00
Merge commit '4c500ce3b2fb03264150c7f20d7d9927eb23d12f'
This commit is contained in:
commit
bea0076bf7
@ -8934,3 +8934,5 @@ Sorry for the inconvenience.";
|
||||
"MediaPicker.VoiceOver.Camera" = "Camera";
|
||||
|
||||
"ChatList.ReadAll" = "Read All";
|
||||
|
||||
"ChatList.ClearSavedMessagesConfirmation" = "Are you sure you want to delete all your saved messages?";
|
||||
|
||||
@ -168,13 +168,13 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
controller?.inProgress = true
|
||||
let authorizationPushConfiguration = self.sharedContext.authorizationPushConfiguration
|
||||
|> take(1)
|
||||
|> timeout(2.0, queue: .mainQueue(), alternate: .single(nil))
|
||||
let _ = (authorizationPushConfiguration
|
||||
|> deliverOnMainQueue).start(next: { [weak self] authorizationPushConfiguration in
|
||||
if let strongSelf = self {
|
||||
controller?.inProgress = true
|
||||
strongSelf.actionDisposable.set((sendAuthorizationCode(accountManager: strongSelf.sharedContext.accountManager, account: strongSelf.account, phoneNumber: number, apiId: strongSelf.apiId, apiHash: strongSelf.apiHash, pushNotificationConfiguration: authorizationPushConfiguration, firebaseSecretStream: strongSelf.sharedContext.firebaseSecretStream, syncContacts: syncContacts, forcedPasswordSetupNotice: { value in
|
||||
guard let entry = CodableEntry(ApplicationSpecificCounterNotice(value: value)) else {
|
||||
return nil
|
||||
|
||||
@ -2177,6 +2177,18 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
reorderedFilterIdsValue = reorderedFilterIds
|
||||
}
|
||||
|
||||
let completion = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.chatListDisplayNode.isReorderingFilters = false
|
||||
strongSelf.isReorderingTabsValue.set(false)
|
||||
(strongSelf.parent as? TabBarController)?.updateIsTabBarEnabled(true, transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
strongSelf.searchContentNode?.setIsEnabled(true, animated: true)
|
||||
if let layout = strongSelf.validLayout {
|
||||
strongSelf.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
}
|
||||
}
|
||||
if let reorderedFilterIds = reorderedFilterIdsValue {
|
||||
let _ = (self.context.engine.peers.updateChatListFiltersInteractively { stateFilters in
|
||||
var updatedFilters: [ChatListFilter] = []
|
||||
@ -2199,18 +2211,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
return
|
||||
}
|
||||
strongSelf.reloadFilters(firstUpdate: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.chatListDisplayNode.isReorderingFilters = false
|
||||
strongSelf.isReorderingTabsValue.set(false)
|
||||
(strongSelf.parent as? TabBarController)?.updateIsTabBarEnabled(true, transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
strongSelf.searchContentNode?.setIsEnabled(true, animated: true)
|
||||
if let layout = strongSelf.validLayout {
|
||||
strongSelf.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut))
|
||||
}
|
||||
completion()
|
||||
})
|
||||
})
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ func chatListSelectionOptions(context: AccountContext, peerIds: Set<PeerId>, fil
|
||||
return context.engine.data.subscribe(TelegramEngine.EngineData.Item.Messages.TotalReadCounters())
|
||||
|> map { readCounters -> ChatListSelectionOptions in
|
||||
var hasUnread = false
|
||||
if readCounters.count(for: .filtered, in: .chats, with: .all) != 0 {
|
||||
if readCounters.count(for: .raw, in: .chats, with: .all) != 0 {
|
||||
hasUnread = true
|
||||
}
|
||||
return ChatListSelectionOptions(read: .all(enabled: hasUnread), delete: false)
|
||||
|
||||
@ -129,7 +129,7 @@ private final class DeleteChatPeerActionSheetItemNode: ActionSheetItemNode {
|
||||
}
|
||||
case let .clearHistory(canClearCache):
|
||||
if peer.id == context.account.peerId {
|
||||
text = PresentationStrings.FormattedString(string: strings.ChatList_DeleteSavedMessagesConfirmation, ranges: [])
|
||||
text = PresentationStrings.FormattedString(string: strings.ChatList_ClearSavedMessagesConfirmation, ranges: [])
|
||||
} else if case .user = peer {
|
||||
text = strings.ChatList_ClearChatConfirmation(peer.displayTitle(strings: strings, displayOrder: nameOrder))
|
||||
} else {
|
||||
|
||||
@ -67,7 +67,6 @@ public final class ListViewBackingView: UIView {
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if !self.isHidden, let target = self.target {
|
||||
if target.bounds.contains(point) {
|
||||
target.scroller.forceDecelerating = false
|
||||
if target.decelerationAnimator != nil {
|
||||
target.decelerationAnimator?.isPaused = true
|
||||
target.decelerationAnimator = nil
|
||||
@ -836,6 +835,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
}
|
||||
self.scrolledToItem = nil
|
||||
|
||||
self.scroller.forceDecelerating = false
|
||||
self.isDragging = true
|
||||
|
||||
self.beganInteractiveDragging(self.touchesPosition)
|
||||
|
||||
@ -1514,7 +1514,7 @@ open class TextNode: ASDisplayNode {
|
||||
if glyphCount == 2, let font = attributes["NSFont"] as? UIFont, font.fontName.contains("ColorEmoji"), let string = layout.attributedString {
|
||||
let range = CTRunGetStringRange(run)
|
||||
|
||||
if range.location < string.length && (range.location + range.length) < string.length {
|
||||
if range.location < string.length && (range.location + range.length) <= string.length {
|
||||
let substring = string.attributedSubstring(from: NSMakeRange(range.location, range.length)).string
|
||||
|
||||
let heart = Unicode.Scalar(0x2764)!
|
||||
|
||||
@ -2619,15 +2619,15 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
c.setItems(strongSelf.contextMenuMainItems(dismiss: dismiss) |> map { ContextController.Items(content: .list($0)) }, minHeight: nil)
|
||||
})))
|
||||
|
||||
// items.append(.custom(SliderContextItem(minValue: 0.05, maxValue: 2.5, value: status.baseRate, valueChanged: { [weak self] newValue, finished in
|
||||
// guard let strongSelf = self, let videoNode = strongSelf.videoNode else {
|
||||
// return
|
||||
// }
|
||||
// videoNode.setBaseRate(newValue)
|
||||
// if finished {
|
||||
// dismiss()
|
||||
// }
|
||||
// }), true))
|
||||
items.append(.custom(SliderContextItem(minValue: 0.5, maxValue: 2.5, value: status.baseRate, valueChanged: { [weak self] newValue, finished in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updatePlaybackRate(newValue)
|
||||
if finished {
|
||||
dismiss()
|
||||
}
|
||||
}), true))
|
||||
|
||||
items.append(.separator)
|
||||
|
||||
|
||||
@ -192,17 +192,17 @@ open class ManagedAnimationNode: ASDisplayNode {
|
||||
|
||||
displayLinkUpdate = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let timestamp = CACurrentMediaTime()
|
||||
var delta: Double
|
||||
if let previousTimestamp = strongSelf.previousTimestamp {
|
||||
delta = min(timestamp - previousTimestamp, 1.0 / 60.0)
|
||||
if let currentDelta = strongSelf.delta, currentDelta < delta {
|
||||
delta = currentDelta
|
||||
}
|
||||
} else {
|
||||
delta = 1.0 / 60.0
|
||||
}
|
||||
strongSelf.previousTimestamp = timestamp
|
||||
// let timestamp = CACurrentMediaTime()
|
||||
// var delta: Double
|
||||
// if let previousTimestamp = strongSelf.previousTimestamp {
|
||||
// delta = min(timestamp - previousTimestamp, 1.0 / 60.0)
|
||||
// if let currentDelta = strongSelf.delta, currentDelta < delta {
|
||||
// delta = currentDelta
|
||||
// }
|
||||
// } else {
|
||||
let delta = 1.0 / 60.0
|
||||
// }
|
||||
// strongSelf.previousTimestamp = timestamp
|
||||
strongSelf.delta = delta
|
||||
|
||||
strongSelf.updateAnimation()
|
||||
|
||||
@ -1,435 +0,0 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import ItemListUI
|
||||
import PresentationDataUtils
|
||||
import AccountContext
|
||||
|
||||
private enum NetworkUsageControllerSection {
|
||||
case cellular
|
||||
case wifi
|
||||
}
|
||||
|
||||
private final class NetworkUsageStatsControllerArguments {
|
||||
let resetStatistics: (NetworkUsageControllerSection) -> Void
|
||||
|
||||
init(resetStatistics: @escaping (NetworkUsageControllerSection) -> Void) {
|
||||
self.resetStatistics = resetStatistics
|
||||
}
|
||||
}
|
||||
|
||||
private enum NetworkUsageStatsSection: Int32 {
|
||||
case messages
|
||||
case image
|
||||
case video
|
||||
case audio
|
||||
case file
|
||||
case call
|
||||
case total
|
||||
case reset
|
||||
}
|
||||
|
||||
private enum NetworkUsageStatsEntry: ItemListNodeEntry {
|
||||
case messagesHeader(PresentationTheme, String)
|
||||
case messagesSent(PresentationTheme, String, String)
|
||||
case messagesReceived(PresentationTheme, String, String)
|
||||
|
||||
case imageHeader(PresentationTheme, String)
|
||||
case imageSent(PresentationTheme, String, String)
|
||||
case imageReceived(PresentationTheme, String, String)
|
||||
|
||||
case videoHeader(PresentationTheme, String)
|
||||
case videoSent(PresentationTheme, String, String)
|
||||
case videoReceived(PresentationTheme, String, String)
|
||||
|
||||
case audioHeader(PresentationTheme, String)
|
||||
case audioSent(PresentationTheme, String, String)
|
||||
case audioReceived(PresentationTheme, String, String)
|
||||
|
||||
case fileHeader(PresentationTheme, String)
|
||||
case fileSent(PresentationTheme, String, String)
|
||||
case fileReceived(PresentationTheme, String, String)
|
||||
|
||||
case callHeader(PresentationTheme, String)
|
||||
case callSent(PresentationTheme, String, String)
|
||||
case callReceived(PresentationTheme, String, String)
|
||||
|
||||
case reset(PresentationTheme, NetworkUsageControllerSection, String)
|
||||
case resetTimestamp(PresentationTheme, String)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
case .messagesHeader, .messagesSent, .messagesReceived:
|
||||
return NetworkUsageStatsSection.messages.rawValue
|
||||
case .imageHeader, .imageSent, .imageReceived:
|
||||
return NetworkUsageStatsSection.image.rawValue
|
||||
case .videoHeader, .videoSent, .videoReceived:
|
||||
return NetworkUsageStatsSection.video.rawValue
|
||||
case .audioHeader, .audioSent, .audioReceived:
|
||||
return NetworkUsageStatsSection.audio.rawValue
|
||||
case .fileHeader, .fileSent, .fileReceived:
|
||||
return NetworkUsageStatsSection.file.rawValue
|
||||
case .callHeader, .callSent, .callReceived:
|
||||
return NetworkUsageStatsSection.call.rawValue
|
||||
case .reset, .resetTimestamp:
|
||||
return NetworkUsageStatsSection.reset.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: Int32 {
|
||||
switch self {
|
||||
case .messagesHeader:
|
||||
return 0
|
||||
case .messagesSent:
|
||||
return 1
|
||||
case .messagesReceived:
|
||||
return 2
|
||||
case .imageHeader:
|
||||
return 3
|
||||
case .imageSent:
|
||||
return 4
|
||||
case .imageReceived:
|
||||
return 5
|
||||
case .videoHeader:
|
||||
return 6
|
||||
case .videoSent:
|
||||
return 7
|
||||
case .videoReceived:
|
||||
return 8
|
||||
case .audioHeader:
|
||||
return 9
|
||||
case .audioSent:
|
||||
return 10
|
||||
case .audioReceived:
|
||||
return 11
|
||||
case .fileHeader:
|
||||
return 12
|
||||
case .fileSent:
|
||||
return 13
|
||||
case .fileReceived:
|
||||
return 14
|
||||
case .callHeader:
|
||||
return 15
|
||||
case .callSent:
|
||||
return 16
|
||||
case .callReceived:
|
||||
return 17
|
||||
case .reset:
|
||||
return 18
|
||||
case .resetTimestamp:
|
||||
return 19
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: NetworkUsageStatsEntry, rhs: NetworkUsageStatsEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .messagesHeader(lhsTheme, lhsText):
|
||||
if case let .messagesHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .messagesSent(lhsTheme, lhsText, lhsValue):
|
||||
if case let .messagesSent(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .messagesReceived(lhsTheme, lhsText, lhsValue):
|
||||
if case let .messagesReceived(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .imageHeader(lhsTheme, lhsText):
|
||||
if case let .imageHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .imageSent(lhsTheme, lhsText, lhsValue):
|
||||
if case let .imageSent(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .imageReceived(lhsTheme, lhsText, lhsValue):
|
||||
if case let .imageReceived(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .videoHeader(lhsTheme, lhsText):
|
||||
if case let .videoHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .videoSent(lhsTheme, lhsText, lhsValue):
|
||||
if case let .videoSent(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .videoReceived(lhsTheme, lhsText, lhsValue):
|
||||
if case let .videoReceived(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .audioHeader(lhsTheme, lhsText):
|
||||
if case let .audioHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .audioSent(lhsTheme, lhsText, lhsValue):
|
||||
if case let .audioSent(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .audioReceived(lhsTheme, lhsText, lhsValue):
|
||||
if case let .audioReceived(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .fileHeader(lhsTheme, lhsText):
|
||||
if case let .fileHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .fileSent(lhsTheme, lhsText, lhsValue):
|
||||
if case let .fileSent(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .fileReceived(lhsTheme, lhsText, lhsValue):
|
||||
if case let .fileReceived(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .callHeader(lhsTheme, lhsText):
|
||||
if case let .callHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .callSent(lhsTheme, lhsText, lhsValue):
|
||||
if case let .callSent(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .callReceived(lhsTheme, lhsText, lhsValue):
|
||||
if case let .callReceived(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .reset(lhsTheme, lhsSection, lhsText):
|
||||
if case let .reset(rhsTheme, rhsSection, rhsText) = rhs, lhsTheme === rhsTheme, lhsSection == rhsSection, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .resetTimestamp(lhsTheme, lhsText):
|
||||
if case let .resetTimestamp(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func <(lhs: NetworkUsageStatsEntry, rhs: NetworkUsageStatsEntry) -> Bool {
|
||||
return lhs.stableId < rhs.stableId
|
||||
}
|
||||
|
||||
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||
let arguments = arguments as! NetworkUsageStatsControllerArguments
|
||||
switch self {
|
||||
case let .messagesHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .messagesSent(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .none , action: nil)
|
||||
case let .messagesReceived(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .none , action: nil)
|
||||
case let .imageHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .imageSent(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .none , action: nil)
|
||||
case let .imageReceived(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .none , action: nil)
|
||||
case let .videoHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .videoSent(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .none , action: nil)
|
||||
case let .videoReceived(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .none , action: nil)
|
||||
case let .audioHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .audioSent(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .none , action: nil)
|
||||
case let .audioReceived(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .none , action: nil)
|
||||
case let .fileHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .fileSent(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .none , action: nil)
|
||||
case let .fileReceived(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .none , action: nil)
|
||||
case let .callHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .callSent(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .none , action: nil)
|
||||
case let .callReceived(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .none , action: nil)
|
||||
case let .reset(_, section, text):
|
||||
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.resetStatistics(section)
|
||||
})
|
||||
case let .resetTimestamp(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func networkUsageStatsControllerEntries(presentationData: PresentationData, section: NetworkUsageControllerSection, stats: NetworkUsageStats) -> [NetworkUsageStatsEntry] {
|
||||
var entries: [NetworkUsageStatsEntry] = []
|
||||
|
||||
let formatting = DataSizeStringFormatting(presentationData: presentationData)
|
||||
switch section {
|
||||
case .cellular:
|
||||
entries.append(.messagesHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_GeneralDataSection))
|
||||
entries.append(.messagesSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.generic.cellular.outgoing, formatting: formatting)))
|
||||
entries.append(.messagesReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.generic.cellular.incoming, formatting: formatting)))
|
||||
|
||||
entries.append(.imageHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_MediaImageDataSection))
|
||||
entries.append(.imageSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.image.cellular.outgoing, formatting: formatting)))
|
||||
entries.append(.imageReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.image.cellular.incoming, formatting: formatting)))
|
||||
|
||||
entries.append(.videoHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_MediaVideoDataSection))
|
||||
entries.append(.videoSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.video.cellular.outgoing, formatting: formatting)))
|
||||
entries.append(.videoReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.video.cellular.incoming, formatting: formatting)))
|
||||
|
||||
entries.append(.audioHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_MediaAudioDataSection))
|
||||
entries.append(.audioSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.audio.cellular.outgoing, formatting: formatting)))
|
||||
entries.append(.audioReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.audio.cellular.incoming, formatting: formatting)))
|
||||
|
||||
entries.append(.fileHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_MediaDocumentDataSection))
|
||||
entries.append(.fileSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.file.cellular.outgoing, formatting: formatting)))
|
||||
entries.append(.fileReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.file.cellular.incoming, formatting: formatting)))
|
||||
|
||||
entries.append(.callHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_CallDataSection))
|
||||
entries.append(.callSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.call.cellular.outgoing, formatting: formatting)))
|
||||
entries.append(.callReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.call.cellular.incoming, formatting: formatting)))
|
||||
|
||||
entries.append(.reset(presentationData.theme, section, presentationData.strings.NetworkUsageSettings_ResetStats))
|
||||
|
||||
if stats.resetCellularTimestamp != 0 {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "E, d MMM yyyy HH:mm"
|
||||
let dateStringPlain = formatter.string(from: Date(timeIntervalSince1970: Double(stats.resetCellularTimestamp)))
|
||||
|
||||
entries.append(.resetTimestamp(presentationData.theme, presentationData.strings.NetworkUsageSettings_CellularUsageSince(dateStringPlain).string))
|
||||
}
|
||||
case .wifi:
|
||||
entries.append(.messagesHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_GeneralDataSection))
|
||||
entries.append(.messagesSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.generic.wifi.outgoing, formatting: formatting)))
|
||||
entries.append(.messagesReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.generic.wifi.incoming, formatting: formatting)))
|
||||
|
||||
entries.append(.imageHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_MediaImageDataSection))
|
||||
entries.append(.imageSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.image.wifi.outgoing, formatting: formatting)))
|
||||
entries.append(.imageReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.image.wifi.incoming, formatting: formatting)))
|
||||
|
||||
entries.append(.videoHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_MediaVideoDataSection))
|
||||
entries.append(.videoSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.video.wifi.outgoing, formatting: formatting)))
|
||||
entries.append(.videoReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.video.wifi.incoming, formatting: formatting)))
|
||||
|
||||
entries.append(.audioHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_MediaAudioDataSection))
|
||||
entries.append(.audioSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.audio.wifi.outgoing, formatting: formatting)))
|
||||
entries.append(.audioReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.audio.wifi.incoming, formatting: formatting)))
|
||||
|
||||
entries.append(.fileHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_MediaDocumentDataSection))
|
||||
entries.append(.fileSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.file.wifi.outgoing, formatting: formatting)))
|
||||
entries.append(.fileReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.file.wifi.incoming, formatting: formatting)))
|
||||
|
||||
entries.append(.callHeader(presentationData.theme, presentationData.strings.NetworkUsageSettings_CallDataSection))
|
||||
entries.append(.callSent(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesSent, dataSizeString(stats.call.wifi.outgoing, formatting: formatting)))
|
||||
entries.append(.callReceived(presentationData.theme, presentationData.strings.NetworkUsageSettings_BytesReceived, dataSizeString(stats.call.wifi.incoming, formatting: formatting)))
|
||||
|
||||
entries.append(.reset(presentationData.theme, section, presentationData.strings.NetworkUsageSettings_ResetStats))
|
||||
if stats.resetWifiTimestamp != 0 {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "E, d MMM yyyy HH:mm"
|
||||
let dateStringPlain = formatter.string(from: Date(timeIntervalSince1970: Double(stats.resetWifiTimestamp)))
|
||||
|
||||
entries.append(.resetTimestamp(presentationData.theme, presentationData.strings.NetworkUsageSettings_WifiUsageSince(dateStringPlain).string))
|
||||
}
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
func networkUsageStatsController(context: AccountContext) -> ViewController {
|
||||
let section = ValuePromise<NetworkUsageControllerSection>(.cellular)
|
||||
let stats = Promise<NetworkUsageStats>()
|
||||
stats.set(accountNetworkUsageStats(account: context.account, reset: []))
|
||||
|
||||
var presentControllerImpl: ((ViewController) -> Void)?
|
||||
|
||||
let arguments = NetworkUsageStatsControllerArguments(resetStatistics: { [weak stats] section in
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = ActionSheetController(presentationData: presentationData)
|
||||
let dismissAction: () -> Void = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
controller.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: presentationData.strings.NetworkUsageSettings_ResetStats, color: .destructive, action: {
|
||||
dismissAction()
|
||||
|
||||
let reset: ResetNetworkUsageStats
|
||||
switch section {
|
||||
case .wifi:
|
||||
reset = .wifi
|
||||
case .cellular:
|
||||
reset = .cellular
|
||||
}
|
||||
stats?.set(accountNetworkUsageStats(account: context.account, reset: reset))
|
||||
}),
|
||||
]),
|
||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||
])
|
||||
presentControllerImpl?(controller)
|
||||
})
|
||||
|
||||
let signal = combineLatest(context.sharedContext.presentationData, section.get(), stats.get()) |> deliverOnMainQueue
|
||||
|> map { presentationData, section, stats -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .sectionControl([presentationData.strings.NetworkUsageSettings_Cellular, presentationData.strings.NetworkUsageSettings_Wifi], 0), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: networkUsageStatsControllerEntries(presentationData: presentationData, section: section, stats: stats), style: .blocks, emptyStateItem: nil, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
controller.titleControlValueChanged = { [weak section] index in
|
||||
section?.set(index == 0 ? .cellular : .wifi)
|
||||
}
|
||||
|
||||
presentControllerImpl = { [weak controller] c in
|
||||
controller?.present(c, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}
|
||||
|
||||
return controller
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -16,6 +16,7 @@ import UrlHandling
|
||||
import AccountUtils
|
||||
import PremiumUI
|
||||
import PasswordSetupUI
|
||||
import StorageUsageScreen
|
||||
|
||||
private struct DeleteAccountOptionsArguments {
|
||||
let changePhoneNumber: () -> Void
|
||||
@ -276,7 +277,9 @@ public func deleteAccountOptionsController(context: AccountContext, navigationCo
|
||||
}, clearCache: {
|
||||
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.options_clear_cache_tap")
|
||||
|
||||
pushControllerImpl?(storageUsageController(context: context))
|
||||
pushControllerImpl?(StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { category in
|
||||
return storageUsageExceptionsScreen(context: context, category: category)
|
||||
}))
|
||||
dismissImpl?()
|
||||
}, clearSyncedContacts: {
|
||||
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.options_clear_contacts_tap")
|
||||
|
||||
@ -15,6 +15,7 @@ import PresentationDataUtils
|
||||
import UrlHandling
|
||||
import AccountUtils
|
||||
import PremiumUI
|
||||
import StorageUsageScreen
|
||||
|
||||
private struct LogoutOptionsItemArguments {
|
||||
let addAccount: () -> Void
|
||||
@ -181,7 +182,9 @@ public func logoutOptionsController(context: AccountContext, navigationControlle
|
||||
})
|
||||
dismissImpl?()
|
||||
}, clearCache: {
|
||||
pushControllerImpl?(storageUsageController(context: context))
|
||||
pushControllerImpl?(StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { category in
|
||||
return storageUsageExceptionsScreen(context: context, category: category)
|
||||
}))
|
||||
dismissImpl?()
|
||||
}, changePhoneNumber: {
|
||||
let introController = PrivacyIntroController(context: context, mode: .changePhoneNumber(phoneNumber), proceedAction: {
|
||||
|
||||
@ -20,6 +20,7 @@ import InstantPageCache
|
||||
import NotificationPeerExceptionController
|
||||
import QrCodeUI
|
||||
import PremiumUI
|
||||
import StorageUsageScreen
|
||||
|
||||
enum SettingsSearchableItemIcon {
|
||||
case profile
|
||||
@ -681,16 +682,54 @@ private func dataSearchableItems(context: AccountContext) -> [SettingsSearchable
|
||||
presentDataSettings(context, present, nil)
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(1), title: strings.ChatSettings_Cache, alternate: synonyms(strings.SettingsSearch_Synonyms_Data_Storage_Title), icon: icon, breadcrumbs: [strings.Settings_ChatSettings], present: { context, _, present in
|
||||
present(.push, storageUsageController(context: context))
|
||||
let controller = StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { category in
|
||||
return storageUsageExceptionsScreen(context: context, category: category)
|
||||
})
|
||||
present(.push, controller)
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(2), title: strings.Cache_KeepMedia, alternate: synonyms(strings.SettingsSearch_Synonyms_Data_Storage_KeepMedia), icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.ChatSettings_Cache], present: { context, _, present in
|
||||
present(.push, storageUsageController(context: context))
|
||||
let controller = StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { category in
|
||||
return storageUsageExceptionsScreen(context: context, category: category)
|
||||
})
|
||||
present(.push, controller)
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(3), title: strings.Cache_ClearCache, alternate: synonyms(strings.SettingsSearch_Synonyms_Data_Storage_ClearCache), icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.ChatSettings_Cache], present: { context, _, present in
|
||||
present(.push, storageUsageController(context: context))
|
||||
let controller = StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { category in
|
||||
return storageUsageExceptionsScreen(context: context, category: category)
|
||||
})
|
||||
present(.push, controller)
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(4), title: strings.NetworkUsageSettings_Title, alternate: synonyms(strings.SettingsSearch_Synonyms_Data_NetworkUsage), icon: icon, breadcrumbs: [strings.Settings_ChatSettings], present: { context, _, present in
|
||||
present(.push, networkUsageStatsController(context: context))
|
||||
let mediaAutoDownloadSettings = context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings])
|
||||
|> map { sharedData -> MediaAutoDownloadSettings in
|
||||
var automaticMediaDownloadSettings: MediaAutoDownloadSettings
|
||||
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings]?.get(MediaAutoDownloadSettings.self) {
|
||||
automaticMediaDownloadSettings = value
|
||||
} else {
|
||||
automaticMediaDownloadSettings = .defaultSettings
|
||||
}
|
||||
return automaticMediaDownloadSettings
|
||||
}
|
||||
|
||||
let _ = (combineLatest(
|
||||
accountNetworkUsageStats(account: context.account, reset: []),
|
||||
mediaAutoDownloadSettings
|
||||
)
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { stats, mediaAutoDownloadSettings in
|
||||
var stats = stats
|
||||
|
||||
if stats.resetWifiTimestamp == 0 {
|
||||
var value = stat()
|
||||
if stat(context.account.basePath, &value) == 0 {
|
||||
stats.resetWifiTimestamp = Int32(value.st_ctimespec.tv_sec)
|
||||
}
|
||||
}
|
||||
|
||||
present(.push, DataUsageScreen(context: context, stats: stats, mediaAutoDownloadSettings: mediaAutoDownloadSettings, makeAutodownloadSettingsController: { isCellular in
|
||||
return autodownloadMediaConnectionTypeController(context: context, connectionType: isCellular ? .cellular : .wifi)
|
||||
}))
|
||||
})
|
||||
}),
|
||||
SettingsSearchableItem(id: .data(5), title: strings.ChatSettings_AutoDownloadUsingCellular, alternate: synonyms(strings.SettingsSearch_Synonyms_Data_AutoDownloadUsingCellular), icon: icon, breadcrumbs: [strings.Settings_ChatSettings, strings.ChatSettings_AutoDownloadTitle], present: { context, _, present in
|
||||
present(.push, autodownloadMediaConnectionTypeController(context: context, connectionType: .cellular))
|
||||
|
||||
@ -527,14 +527,14 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
|
||||
private func contextMenuSpeedItems(dismiss: @escaping () -> Void) -> Signal<[ContextMenuItem], NoError> {
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
// items.append(.custom(SliderContextItem(minValue: 0.05, maxValue: 2.5, value: self.playbackBaseRate?.doubleValue ?? 1.0, valueChanged: { [weak self] newValue, finished in
|
||||
// self?.setRate?(AudioPlaybackRate(newValue), true)
|
||||
// if finished {
|
||||
// dismiss()
|
||||
// }
|
||||
// }), true))
|
||||
items.append(.custom(SliderContextItem(minValue: 0.5, maxValue: 2.5, value: self.playbackBaseRate?.doubleValue ?? 1.0, valueChanged: { [weak self] newValue, finished in
|
||||
self?.setRate?(AudioPlaybackRate(newValue), true)
|
||||
if finished {
|
||||
dismiss()
|
||||
}
|
||||
}), true))
|
||||
|
||||
// items.append(.separator)
|
||||
items.append(.separator)
|
||||
|
||||
for (text, _, rate) in self.speedList(strings: self.strings) {
|
||||
let isSelected = self.playbackBaseRate == rate
|
||||
|
||||
@ -15,6 +15,7 @@ swift_library(
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/ContextUI:ContextUI",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@ -4,6 +4,7 @@ import Display
|
||||
import AsyncDisplayKit
|
||||
import ContextUI
|
||||
import TelegramPresentationData
|
||||
import AnimatedCountLabelNode
|
||||
|
||||
public final class SliderContextItem: ContextMenuCustomItem {
|
||||
private let minValue: CGFloat
|
||||
@ -23,15 +24,15 @@ public final class SliderContextItem: ContextMenuCustomItem {
|
||||
}
|
||||
}
|
||||
|
||||
private let textFont = Font.regular(17.0)
|
||||
private let textFont = Font.with(size: 17.0, design: .regular, traits: .monospacedNumbers)
|
||||
|
||||
private final class SliderContextItemNode: ASDisplayNode, ContextMenuCustomNode {
|
||||
private var presentationData: PresentationData
|
||||
|
||||
private let backgroundTextNode: ImmediateTextNode
|
||||
private let backgroundTextNode: ImmediateAnimatedCountLabelNode
|
||||
|
||||
private let foregroundNode: ASDisplayNode
|
||||
private let foregroundTextNode: ImmediateTextNode
|
||||
private let foregroundTextNode: ImmediateAnimatedCountLabelNode
|
||||
|
||||
let minValue: CGFloat
|
||||
let maxValue: CGFloat
|
||||
@ -51,24 +52,18 @@ private final class SliderContextItemNode: ASDisplayNode, ContextMenuCustomNode
|
||||
self.maxValue = maxValue
|
||||
self.value = value
|
||||
self.valueChanged = valueChanged
|
||||
|
||||
self.backgroundTextNode = ImmediateTextNode()
|
||||
self.backgroundTextNode.isAccessibilityElement = false
|
||||
self.backgroundTextNode.isUserInteractionEnabled = false
|
||||
self.backgroundTextNode.displaysAsynchronously = false
|
||||
self.backgroundTextNode.textAlignment = .left
|
||||
|
||||
self.backgroundTextNode = ImmediateAnimatedCountLabelNode()
|
||||
self.backgroundTextNode.alwaysOneDirection = true
|
||||
|
||||
self.foregroundNode = ASDisplayNode()
|
||||
self.foregroundNode.clipsToBounds = true
|
||||
self.foregroundNode.isAccessibilityElement = false
|
||||
self.foregroundNode.backgroundColor = UIColor(rgb: 0xffffff)
|
||||
self.foregroundNode.isUserInteractionEnabled = false
|
||||
|
||||
self.foregroundTextNode = ImmediateTextNode()
|
||||
self.foregroundTextNode.isAccessibilityElement = false
|
||||
self.foregroundTextNode.isUserInteractionEnabled = false
|
||||
self.foregroundTextNode.displaysAsynchronously = false
|
||||
self.foregroundTextNode.textAlignment = .left
|
||||
self.foregroundTextNode = ImmediateAnimatedCountLabelNode()
|
||||
self.foregroundTextNode.alwaysOneDirection = true
|
||||
|
||||
super.init()
|
||||
|
||||
@ -77,6 +72,27 @@ private final class SliderContextItemNode: ASDisplayNode, ContextMenuCustomNode
|
||||
self.addSubnode(self.backgroundTextNode)
|
||||
self.addSubnode(self.foregroundNode)
|
||||
self.foregroundNode.addSubnode(self.foregroundTextNode)
|
||||
|
||||
let stringValue = "1.0x"
|
||||
|
||||
let backgroundTextColor = self.presentationData.theme.contextMenu.secondaryColor
|
||||
let foregroundTextColor = UIColor.black
|
||||
|
||||
var backgroundSegments: [AnimatedCountLabelNode.Segment] = []
|
||||
var foregroundSegments: [AnimatedCountLabelNode.Segment] = []
|
||||
var textCount = 0
|
||||
for char in stringValue {
|
||||
if let intValue = Int(String(char)) {
|
||||
backgroundSegments.append(.number(intValue, NSAttributedString(string: String(char), font: textFont, textColor: backgroundTextColor)))
|
||||
foregroundSegments.append(.number(intValue, NSAttributedString(string: String(char), font: textFont, textColor: foregroundTextColor)))
|
||||
} else {
|
||||
backgroundSegments.append(.text(textCount, NSAttributedString(string: String(char), font: textFont, textColor: backgroundTextColor)))
|
||||
foregroundSegments.append(.text(textCount, NSAttributedString(string: String(char), font: textFont, textColor: foregroundTextColor)))
|
||||
textCount += 1
|
||||
}
|
||||
}
|
||||
self.backgroundTextNode.segments = backgroundSegments
|
||||
self.foregroundTextNode.segments = foregroundSegments
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
@ -101,28 +117,43 @@ private final class SliderContextItemNode: ASDisplayNode, ContextMenuCustomNode
|
||||
let value = (self.value - self.minValue) / range
|
||||
transition.updateFrameAdditive(node: self.foregroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: value * width, height: self.frame.height)))
|
||||
|
||||
var stringValue = String(format: "%.1fx", self.value)
|
||||
if stringValue.hasSuffix(".0x") {
|
||||
stringValue = stringValue.replacingOccurrences(of: ".0x", with: "x")
|
||||
}
|
||||
self.backgroundTextNode.attributedText = NSAttributedString(string: stringValue, font: textFont, textColor: UIColor(rgb: 0xffffff))
|
||||
self.foregroundTextNode.attributedText = NSAttributedString(string: stringValue, font: textFont, textColor: UIColor(rgb: 0x000000))
|
||||
let stringValue = String(format: "%.1fx", self.value)
|
||||
|
||||
let _ = self.backgroundTextNode.updateLayout(CGSize(width: 70.0, height: .greatestFiniteMagnitude))
|
||||
let _ = self.foregroundTextNode.updateLayout(CGSize(width: 70.0, height: .greatestFiniteMagnitude))
|
||||
let backgroundTextColor = self.presentationData.theme.contextMenu.secondaryColor
|
||||
let foregroundTextColor = UIColor.black
|
||||
|
||||
var backgroundSegments: [AnimatedCountLabelNode.Segment] = []
|
||||
var foregroundSegments: [AnimatedCountLabelNode.Segment] = []
|
||||
var textCount = 0
|
||||
for char in stringValue {
|
||||
if let intValue = Int(String(char)) {
|
||||
backgroundSegments.append(.number(intValue, NSAttributedString(string: String(char), font: textFont, textColor: backgroundTextColor)))
|
||||
foregroundSegments.append(.number(intValue, NSAttributedString(string: String(char), font: textFont, textColor: foregroundTextColor)))
|
||||
} else {
|
||||
backgroundSegments.append(.text(textCount, NSAttributedString(string: String(char), font: textFont, textColor: backgroundTextColor)))
|
||||
foregroundSegments.append(.text(textCount, NSAttributedString(string: String(char), font: textFont, textColor: foregroundTextColor)))
|
||||
textCount += 1
|
||||
}
|
||||
}
|
||||
self.backgroundTextNode.segments = backgroundSegments
|
||||
self.foregroundTextNode.segments = foregroundSegments
|
||||
|
||||
let _ = self.backgroundTextNode.updateLayout(size: CGSize(width: 70.0, height: .greatestFiniteMagnitude), animated: true)
|
||||
let _ = self.foregroundTextNode.updateLayout(size: CGSize(width: 70.0, height: .greatestFiniteMagnitude), animated: true)
|
||||
}
|
||||
|
||||
func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) {
|
||||
let valueWidth: CGFloat = 70.0
|
||||
let height: CGFloat = 45.0
|
||||
|
||||
var textSize = self.backgroundTextNode.updateLayout(CGSize(width: valueWidth, height: .greatestFiniteMagnitude))
|
||||
textSize.width = valueWidth
|
||||
|
||||
var backgroundTextSize = self.backgroundTextNode.updateLayout(size: CGSize(width: 70.0, height: .greatestFiniteMagnitude), animated: true)
|
||||
backgroundTextSize.width = valueWidth
|
||||
let _ = self.foregroundTextNode.updateLayout(size: CGSize(width: 70.0, height: .greatestFiniteMagnitude), animated: true)
|
||||
|
||||
return (CGSize(width: height * 3.0, height: height), { size, transition in
|
||||
let leftInset: CGFloat = 17.0
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((size.height - textSize.height) / 2.0)), size: textSize)
|
||||
let textFrame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - backgroundTextSize.height) / 2.0)), size: backgroundTextSize)
|
||||
transition.updateFrameAdditive(node: self.backgroundTextNode, frame: textFrame)
|
||||
transition.updateFrameAdditive(node: self.foregroundTextNode, frame: textFrame)
|
||||
|
||||
|
||||
@ -89,6 +89,7 @@ import ChatListHeaderComponent
|
||||
import ChatControllerInteraction
|
||||
import FeaturedStickersScreen
|
||||
import ChatEntityKeyboardInputNode
|
||||
import StorageUsageScreen
|
||||
|
||||
#if DEBUG
|
||||
import os.signpost
|
||||
@ -525,6 +526,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
private var translationStateDisposable: Disposable?
|
||||
|
||||
private var nextChannelToReadDisposable: Disposable?
|
||||
private var offerNextChannelToRead = false
|
||||
|
||||
private var inviteRequestsContext: PeerInvitationImportersContext?
|
||||
private var inviteRequestsDisposable = MetaDisposable()
|
||||
@ -5010,7 +5012,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.historyNode.offerNextChannelToRead = true
|
||||
strongSelf.offerNextChannelToRead = true
|
||||
strongSelf.chatDisplayNode.historyNode.nextChannelToRead = nextPeer.flatMap { nextPeer -> (peer: EnginePeer, unreadCount: Int, location: TelegramEngine.NextUnreadChannelLocation) in
|
||||
return (peer: nextPeer.peer, unreadCount: nextPeer.unreadCount, location: nextPeer.location)
|
||||
}
|
||||
@ -5029,6 +5031,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.preloadNextChatPeerIdDisposable.set(nil)
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.updateNextChannelToReadVisibility()
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -11459,6 +11463,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
if (self.presentationInterfaceState.interfaceState.selectionState == nil) != (updatedChatPresentationInterfaceState.interfaceState.selectionState == nil) {
|
||||
self.isSelectingMessagesUpdated?(updatedChatPresentationInterfaceState.interfaceState.selectionState != nil)
|
||||
self.updateNextChannelToReadVisibility()
|
||||
}
|
||||
|
||||
self.presentationInterfaceState = updatedChatPresentationInterfaceState
|
||||
@ -11700,6 +11705,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self?.beginClearHistory(type: type)
|
||||
}
|
||||
|
||||
let context = self.context
|
||||
let _ = (self.context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.ParticipantCount(id: peerId),
|
||||
TelegramEngine.EngineData.Item.Peer.CanDeleteHistory(id: peerId)
|
||||
@ -11886,9 +11892,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
default:
|
||||
text = strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser
|
||||
}
|
||||
items.append(ActionSheetButtonItem(title: text, color: .destructive, action: { [weak actionSheet] in
|
||||
items.append(ActionSheetButtonItem(title: text, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
beginClear(.forLocalPeer)
|
||||
if mainPeer.id == context.account.peerId, let strongSelf = self {
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
}),
|
||||
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: {
|
||||
beginClear(.forLocalPeer)
|
||||
})
|
||||
], parseMarkdown: true), in: .window(.root))
|
||||
} else {
|
||||
beginClear(.forLocalPeer)
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
@ -12156,7 +12172,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) })
|
||||
|
||||
if let strongSelf = self {
|
||||
let controller = storageUsageController(context: strongSelf.context, isModal: true)
|
||||
let context = strongSelf.context
|
||||
let controller = StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { category in
|
||||
return storageUsageExceptionsScreen(context: context, category: category)
|
||||
})
|
||||
strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}
|
||||
}))
|
||||
@ -16970,7 +16989,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
actionSheet?.dismissAnimated()
|
||||
if let strongSelf = self, !presented {
|
||||
presented = true
|
||||
strongSelf.push(storageUsageController(context: strongSelf.context, isModal: true))
|
||||
let context = strongSelf.context
|
||||
strongSelf.push(StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { category in
|
||||
return storageUsageExceptionsScreen(context: context, category: category)
|
||||
}))
|
||||
}
|
||||
}))
|
||||
|
||||
@ -18179,6 +18201,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
return self.presentationData.strings.Chat_SendAllowedContentText(itemListString).string
|
||||
}
|
||||
|
||||
private func updateNextChannelToReadVisibility() {
|
||||
self.chatDisplayNode.historyNode.offerNextChannelToRead = self.offerNextChannelToRead && self.presentationInterfaceState.interfaceState.selectionState == nil
|
||||
}
|
||||
}
|
||||
|
||||
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
||||
|
||||
@ -1426,6 +1426,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
isSelectionEnabled = false
|
||||
} else if case .pinnedMessages = self.chatPresentationInterfaceState.subject {
|
||||
isSelectionEnabled = false
|
||||
} else if self.chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState != nil {
|
||||
isSelectionEnabled = false
|
||||
}
|
||||
self.historyNode.isSelectionGestureEnabled = isSelectionEnabled
|
||||
|
||||
@ -2459,8 +2461,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
if textView.inputView !== updatedInputView {
|
||||
textView.inputView = updatedInputView
|
||||
if textView.isFirstResponder {
|
||||
if self.chatPresentationInterfaceStateRequiresInputFocus(chatPresentationInterfaceState) {
|
||||
if let validLayout = self.validLayout, let inputHeight = validLayout.0.inputHeight, inputHeight > 100.0 {
|
||||
if self.chatPresentationInterfaceStateRequiresInputFocus(chatPresentationInterfaceState), let validLayout = self.validLayout {
|
||||
if case .compact = validLayout.0.metrics.widthClass {
|
||||
waitForKeyboardLayout = true
|
||||
} else if let inputHeight = validLayout.0.inputHeight, inputHeight > 100.0 {
|
||||
waitForKeyboardLayout = true
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import AccountContext
|
||||
import AlertUI
|
||||
import PresentationDataUtils
|
||||
import SettingsUI
|
||||
import StorageUsageScreen
|
||||
|
||||
private func totalDiskSpace() -> Int64 {
|
||||
do {
|
||||
@ -31,7 +32,9 @@ func checkAvailableDiskSpace(context: AccountContext, threshold: Int64 = 100 * 1
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = textAlertController(context: context, title: nil, text: presentationData.strings.Cache_LowDiskSpaceText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: {
|
||||
push(storageUsageController(context: context, isModal: true))
|
||||
push(StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { category in
|
||||
return storageUsageExceptionsScreen(context: context, category: category)
|
||||
}))
|
||||
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||
push(controller)
|
||||
|
||||
|
||||
@ -199,7 +199,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
||||
let rightNavigationButton = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.rightNavigationButtonPressed))
|
||||
self.rightNavigationButton = rightNavigationButton
|
||||
self.navigationItem.rightBarButtonItem = self.rightNavigationButton
|
||||
rightNavigationButton.isEnabled = count != 0 || self.params.alwaysEnabled
|
||||
rightNavigationButton.isEnabled = true //count != 0 || self.params.alwaysEnabled
|
||||
case .channelCreation:
|
||||
self.titleView.title = CounterContollerTitle(title: self.presentationData.strings.GroupInfo_AddParticipantTitle, counter: "")
|
||||
let rightNavigationButton = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.rightNavigationButtonPressed))
|
||||
|
||||
@ -907,7 +907,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
||||
let _ = (settings
|
||||
|> deliverOnMainQueue).start(next: { settings in
|
||||
if settings.defaultWebBrowser == nil {
|
||||
if isCompact {
|
||||
if !"".isEmpty && isCompact {
|
||||
let controller = BrowserScreen(context: context, subject: .webPage(url: parsedUrl.absoluteString))
|
||||
navigationController?.pushViewController(controller)
|
||||
} else {
|
||||
|
||||
@ -4564,7 +4564,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
canSetupAutoremoveTimeout = true
|
||||
}
|
||||
} else if let user = chatPeer as? TelegramUser {
|
||||
if user.id != strongSelf.context.account.peerId && user.botInfo == nil {
|
||||
if user.id != strongSelf.context.account.peerId {
|
||||
canSetupAutoremoveTimeout = true
|
||||
}
|
||||
} else if let channel = chatPeer as? TelegramChannel {
|
||||
|
||||
@ -1239,8 +1239,9 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
guard let navigationController = self.mainWindow?.viewController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
|
||||
let controller = storageUsageController(context: context, isModal: true)
|
||||
let controller = StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { category in
|
||||
return storageUsageExceptionsScreen(context: context, category: category)
|
||||
})
|
||||
navigationController.pushViewController(controller)
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user