Swiftgram/TelegramUI/AutodownloadConnectionTypeController.swift
2019-06-06 17:43:48 +01:00

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
}