mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +00:00
358 lines
17 KiB
Swift
358 lines
17 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import Display
|
|
import SwiftSignalKit
|
|
import Postbox
|
|
import TelegramCore
|
|
|
|
enum AutomaticDownloadConnectionType {
|
|
case cellular
|
|
case wifi
|
|
|
|
var automaticDownloadNetworkType: MediaAutoDownloadNetworkType {
|
|
switch self {
|
|
case .cellular:
|
|
return .cellular
|
|
case .wifi:
|
|
return .wifi
|
|
}
|
|
}
|
|
}
|
|
|
|
private final class AutodownloadMediaConnectionTypeControllerArguments {
|
|
let toggleMaster: (Bool) -> Void
|
|
let changePreset: (AutomaticDownloadDataUsage) -> Void
|
|
let customize: (AutomaticDownloadCategory) -> Void
|
|
|
|
init(toggleMaster: @escaping (Bool) -> Void, changePreset: @escaping (AutomaticDownloadDataUsage) -> Void, customize: @escaping (AutomaticDownloadCategory) -> Void) {
|
|
self.toggleMaster = toggleMaster
|
|
self.changePreset = changePreset
|
|
self.customize = customize
|
|
}
|
|
}
|
|
|
|
private enum AutodownloadMediaCategorySection: Int32 {
|
|
case master
|
|
case dataUsage
|
|
case types
|
|
}
|
|
|
|
private enum AutodownloadMediaCategoryEntry: ItemListNodeEntry {
|
|
case master(PresentationTheme, String, Bool)
|
|
case dataUsageHeader(PresentationTheme, String)
|
|
case dataUsageItem(PresentationTheme, PresentationStrings, AutomaticDownloadDataUsage, Int?, Bool)
|
|
case typesHeader(PresentationTheme, String)
|
|
case photos(PresentationTheme, String, String, Bool)
|
|
case videos(PresentationTheme, String, String, Bool)
|
|
case files(PresentationTheme, String, String, Bool)
|
|
case voiceMessagesInfo(PresentationTheme, String)
|
|
|
|
var section: ItemListSectionId {
|
|
switch self {
|
|
case .master:
|
|
return AutodownloadMediaCategorySection.master.rawValue
|
|
case .dataUsageHeader, .dataUsageItem:
|
|
return AutodownloadMediaCategorySection.dataUsage.rawValue
|
|
case .typesHeader, .photos, .videos, .files, .voiceMessagesInfo:
|
|
return AutodownloadMediaCategorySection.types.rawValue
|
|
}
|
|
}
|
|
|
|
var stableId: Int32 {
|
|
switch self {
|
|
case .master:
|
|
return 0
|
|
case .dataUsageHeader:
|
|
return 1
|
|
case .dataUsageItem:
|
|
return 2
|
|
case .typesHeader:
|
|
return 3
|
|
case .photos:
|
|
return 4
|
|
case .videos:
|
|
return 5
|
|
case .files:
|
|
return 6
|
|
case .voiceMessagesInfo:
|
|
return 7
|
|
}
|
|
}
|
|
|
|
static func ==(lhs: AutodownloadMediaCategoryEntry, rhs: AutodownloadMediaCategoryEntry) -> Bool {
|
|
switch lhs {
|
|
case let .master(lhsTheme, lhsText, lhsValue):
|
|
if case let .master(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .dataUsageHeader(lhsTheme, lhsText):
|
|
if case let .dataUsageHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .dataUsageItem(lhsTheme, lhsStrings, lhsValue, lhsCustomPosition, lhsEnabled):
|
|
if case let .dataUsageItem(rhsTheme, rhsStrings, rhsValue, rhsCustomPosition, rhsEnabled) = rhs, lhsTheme === rhsTheme, lhsStrings == rhsStrings, lhsValue == rhsValue, lhsCustomPosition == rhsCustomPosition, lhsEnabled == rhsEnabled {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .typesHeader(lhsTheme, lhsText):
|
|
if case let .typesHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .photos(lhsTheme, lhsText, lhsValue, lhsEnabled):
|
|
if case let .photos(rhsTheme, rhsText, rhsValue, rhsEnabled) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsEnabled == rhsEnabled {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .videos(lhsTheme, lhsText, lhsValue, lhsEnabled):
|
|
if case let .videos(rhsTheme, rhsText, rhsValue, rhsEnabled) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsEnabled == rhsEnabled {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .files(lhsTheme, lhsText, lhsValue, lhsEnabled):
|
|
if case let .files(rhsTheme, rhsText, rhsValue, rhsEnabled) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsEnabled == rhsEnabled {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .voiceMessagesInfo(lhsTheme, lhsText):
|
|
if case let .voiceMessagesInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
static func <(lhs: AutodownloadMediaCategoryEntry, rhs: AutodownloadMediaCategoryEntry) -> Bool {
|
|
return lhs.stableId < rhs.stableId
|
|
}
|
|
|
|
func item(_ arguments: AutodownloadMediaConnectionTypeControllerArguments) -> ListViewItem {
|
|
switch self {
|
|
case let .master(theme, text, value):
|
|
return ItemListSwitchItem(theme: theme, title: text, value: value, enableInteractiveChanges: true, enabled: true, sectionId: self.section, style: .blocks, updated: { value in
|
|
arguments.toggleMaster(value)
|
|
})
|
|
case let .dataUsageHeader(theme, text):
|
|
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
|
case let .dataUsageItem(theme, strings, value, customPosition, enabled):
|
|
return AutodownloadDataUsagePickerItem(theme: theme, strings: strings, value: value, customPosition: customPosition, enabled: enabled, sectionId: self.section, updated: { preset in
|
|
arguments.changePreset(preset)
|
|
})
|
|
case let .typesHeader(theme, text):
|
|
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
|
case let .photos(theme, text, value, enabled):
|
|
return ItemListDisclosureItem(theme: theme, title: text, enabled: enabled, label: value, labelStyle: .detailText, sectionId: self.section, style: .blocks, action: {
|
|
arguments.customize(.photo)
|
|
})
|
|
case let .videos(theme, text, value, enabled):
|
|
return ItemListDisclosureItem(theme: theme, title: text, enabled: enabled, label: value, labelStyle: .detailText, sectionId: self.section, style: .blocks, action: {
|
|
arguments.customize(.video)
|
|
})
|
|
case let .files(theme, text, value, enabled):
|
|
return ItemListDisclosureItem(theme: theme, title: text, enabled: enabled, label: value, labelStyle: .detailText, sectionId: self.section, style: .blocks, action: {
|
|
arguments.customize(.file)
|
|
})
|
|
case let .voiceMessagesInfo(theme, text):
|
|
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct AutomaticDownloadPeers {
|
|
let contacts: Bool
|
|
let otherPrivate: Bool
|
|
let groups: Bool
|
|
let channels: Bool
|
|
let size: Int32?
|
|
|
|
init(category: MediaAutoDownloadCategory) {
|
|
self.contacts = category.contacts
|
|
self.otherPrivate = category.otherPrivate
|
|
self.groups = category.groups
|
|
self.channels = category.channels
|
|
self.size = category.sizeLimit
|
|
}
|
|
}
|
|
|
|
private func stringForAutomaticDownloadPeers(strings: PresentationStrings, decimalSeparator: String, peers: AutomaticDownloadPeers, category: AutomaticDownloadCategory) -> String {
|
|
var size: String?
|
|
if var peersSize = peers.size, category == .video || category == .file {
|
|
if peersSize == Int32.max {
|
|
peersSize = 1536 * 1024 * 1024
|
|
}
|
|
size = autodownloadDataSizeString(Int64(peersSize), decimalSeparator: decimalSeparator)
|
|
}
|
|
|
|
if peers.contacts && peers.otherPrivate && peers.groups && peers.channels {
|
|
if let size = size {
|
|
return strings.AutoDownloadSettings_UpToForAll(size).0
|
|
} else {
|
|
return strings.AutoDownloadSettings_OnForAll
|
|
}
|
|
} else {
|
|
var types: [String] = []
|
|
if peers.contacts {
|
|
types.append(strings.AutoDownloadSettings_TypeContacts)
|
|
}
|
|
if peers.otherPrivate {
|
|
types.append(strings.AutoDownloadSettings_TypePrivateChats)
|
|
}
|
|
if peers.groups {
|
|
types.append(strings.AutoDownloadSettings_TypeGroupChats)
|
|
}
|
|
if peers.channels {
|
|
types.append(strings.AutoDownloadSettings_TypeChannels)
|
|
}
|
|
|
|
if types.isEmpty {
|
|
return strings.AutoDownloadSettings_OffForAll
|
|
}
|
|
|
|
var string: String = ""
|
|
for i in 0 ..< types.count {
|
|
if !string.isEmpty {
|
|
if i == types.count - 1 {
|
|
string.append(strings.AutoDownloadSettings_LastDelimeter)
|
|
} else {
|
|
string.append(strings.AutoDownloadSettings_Delimeter)
|
|
}
|
|
}
|
|
string.append(types[i])
|
|
}
|
|
|
|
if let size = size {
|
|
return strings.AutoDownloadSettings_UpToFor(size, string).0
|
|
} else {
|
|
return strings.AutoDownloadSettings_OnFor(string).0
|
|
}
|
|
}
|
|
}
|
|
|
|
private func autodownloadMediaConnectionTypeControllerEntries(presentationData: PresentationData, connectionType: AutomaticDownloadConnectionType, settings: MediaAutoDownloadSettings) -> [AutodownloadMediaCategoryEntry] {
|
|
var entries: [AutodownloadMediaCategoryEntry] = []
|
|
|
|
let connection = settings.connectionSettings(for: connectionType.automaticDownloadNetworkType)
|
|
let categories = effectiveAutodownloadCategories(settings: settings, networkType: connectionType.automaticDownloadNetworkType)
|
|
|
|
let master = connection.enabled
|
|
let photo = AutomaticDownloadPeers(category: categories.photo)
|
|
let video = AutomaticDownloadPeers(category: categories.video)
|
|
let file = AutomaticDownloadPeers(category: categories.file)
|
|
|
|
entries.append(.master(presentationData.theme, presentationData.strings.AutoDownloadSettings_AutoDownload, master))
|
|
|
|
entries.append(.dataUsageHeader(presentationData.theme, presentationData.strings.AutoDownloadSettings_DataUsage))
|
|
|
|
var customPosition: Int?
|
|
if let custom = connection.custom {
|
|
let sortedPresets = [settings.presets.low, settings.presets.medium, settings.presets.high, custom].sorted()
|
|
customPosition = sortedPresets.firstIndex(of: custom) ?? 0
|
|
}
|
|
|
|
entries.append(.dataUsageItem(presentationData.theme, presentationData.strings, AutomaticDownloadDataUsage(preset: connection.preset), customPosition, master))
|
|
|
|
entries.append(.typesHeader(presentationData.theme, presentationData.strings.AutoDownloadSettings_MediaTypes))
|
|
entries.append(.photos(presentationData.theme, presentationData.strings.AutoDownloadSettings_Photos, stringForAutomaticDownloadPeers(strings: presentationData.strings, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator, peers: photo, category: .photo), master))
|
|
entries.append(.videos(presentationData.theme, presentationData.strings.AutoDownloadSettings_Videos, stringForAutomaticDownloadPeers(strings: presentationData.strings, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator, peers: video, category: .video), master))
|
|
entries.append(.files(presentationData.theme, presentationData.strings.AutoDownloadSettings_Files, stringForAutomaticDownloadPeers(strings: presentationData.strings, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator, peers: file, category: .file), master))
|
|
entries.append(.voiceMessagesInfo(presentationData.theme, presentationData.strings.AutoDownloadSettings_VoiceMessagesInfo))
|
|
|
|
return entries
|
|
}
|
|
|
|
func autodownloadMediaConnectionTypeController(context: AccountContext, connectionType: AutomaticDownloadConnectionType) -> ViewController {
|
|
var pushControllerImpl: ((ViewController) -> Void)?
|
|
var presentControllerImpl: ((ViewController) -> Void)?
|
|
|
|
let arguments = AutodownloadMediaConnectionTypeControllerArguments(toggleMaster: { value in
|
|
let _ = updateMediaDownloadSettingsInteractively(accountManager: context.sharedContext.accountManager, { settings in
|
|
var settings = settings
|
|
switch connectionType {
|
|
case .cellular:
|
|
settings.cellular.enabled = value
|
|
case .wifi:
|
|
settings.wifi.enabled = value
|
|
}
|
|
return settings
|
|
}).start()
|
|
}, changePreset: { value in
|
|
let _ = updateMediaDownloadSettingsInteractively(accountManager: context.sharedContext.accountManager, { settings in
|
|
var settings = settings
|
|
let preset: MediaAutoDownloadPreset
|
|
switch value {
|
|
case .low:
|
|
preset = .low
|
|
case .medium:
|
|
preset = .medium
|
|
case .high:
|
|
preset = .high
|
|
case .custom:
|
|
preset = .custom
|
|
}
|
|
switch connectionType {
|
|
case .cellular:
|
|
settings.cellular.preset = preset
|
|
case .wifi:
|
|
settings.wifi.preset = preset
|
|
}
|
|
return settings
|
|
}).start()
|
|
}, customize: { category in
|
|
let controller = autodownloadMediaCategoryController(context: context, connectionType: connectionType, category: category)
|
|
pushControllerImpl?(controller)
|
|
})
|
|
|
|
let signal = combineLatest(context.sharedContext.presentationData, context.sharedContext.accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings]))
|
|
|> deliverOnMainQueue
|
|
|> map { presentationData, sharedData -> (ItemListControllerState, (ItemListNodeState<AutodownloadMediaCategoryEntry>, AutodownloadMediaCategoryEntry.ItemGenerationArguments)) in
|
|
var automaticMediaDownloadSettings: MediaAutoDownloadSettings
|
|
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.automaticMediaDownloadSettings] as? MediaAutoDownloadSettings {
|
|
automaticMediaDownloadSettings = value
|
|
} else {
|
|
automaticMediaDownloadSettings = MediaAutoDownloadSettings.defaultSettings
|
|
}
|
|
|
|
var autodownloadSettings: AutodownloadSettings
|
|
if let value = sharedData.entries[SharedDataKeys.autodownloadSettings] as? AutodownloadSettings {
|
|
autodownloadSettings = value
|
|
automaticMediaDownloadSettings = automaticMediaDownloadSettings.updatedWithAutodownloadSettings(autodownloadSettings)
|
|
} else {
|
|
autodownloadSettings = .defaultSettings
|
|
}
|
|
|
|
let title: String
|
|
switch connectionType {
|
|
case .cellular:
|
|
title = presentationData.strings.AutoDownloadSettings_CellularTitle
|
|
case .wifi:
|
|
title = presentationData.strings.AutoDownloadSettings_WifiTitle
|
|
}
|
|
|
|
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
|
let listState = ItemListNodeState(entries: autodownloadMediaConnectionTypeControllerEntries(presentationData: presentationData, connectionType: connectionType, settings: automaticMediaDownloadSettings), style: .blocks, emptyStateItem: nil, animateChanges: false)
|
|
|
|
return (controllerState, (listState, arguments))
|
|
}
|
|
|
|
let controller = ItemListController(context: context, state: signal)
|
|
pushControllerImpl = { [weak controller] c in
|
|
if let controller = controller {
|
|
(controller.navigationController as? NavigationController)?.pushViewController(c)
|
|
}
|
|
}
|
|
presentControllerImpl = { [weak controller] c in
|
|
controller?.present(c, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
}
|
|
return controller
|
|
}
|