From ae9eb245c4fc7f052c06bc7838fbd45040df00df Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sat, 24 Dec 2022 22:34:27 +0400 Subject: [PATCH] Implement max storage size slider --- .../Components/StorageUsageScreen/BUILD | 1 + .../StorageCategoryItemCompoment.swift | 12 +- .../Sources/StorageKeepSizeComponent.swift | 208 ++++++++++++++++++ .../Sources/StorageUsageScreen.swift | 89 ++++++++ 4 files changed, 304 insertions(+), 6 deletions(-) create mode 100644 submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageKeepSizeComponent.swift diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/BUILD b/submodules/TelegramUI/Components/StorageUsageScreen/BUILD index 3d20c87422..1d94ad70e4 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/BUILD +++ b/submodules/TelegramUI/Components/StorageUsageScreen/BUILD @@ -37,6 +37,7 @@ swift_library( "//submodules/UndoUI", "//submodules/AnimatedStickerNode", "//submodules/TelegramAnimatedStickerNode", + "//submodules/LegacyComponents", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageCategoryItemCompoment.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageCategoryItemCompoment.swift index 0e56f8b6a4..b08dedad44 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageCategoryItemCompoment.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageCategoryItemCompoment.swift @@ -181,7 +181,7 @@ final class StorageCategoryItemComponent: Component { let rightInset: CGFloat = 16.0 - var availableWidth: CGFloat = availableSize.width - leftInset + var availableWidth: CGFloat = availableSize.width - leftInset - rightInset if !component.category.subcategories.isEmpty { let iconView: UIImageView @@ -218,23 +218,23 @@ final class StorageCategoryItemComponent: Component { let labelSize = self.label.update( transition: transition, - component: AnyComponent(Text(text: dataSizeString(Int(component.category.size), formatting: DataSizeStringFormatting(strings: component.strings, decimalSeparator: ".")), font: Font.regular(17.0), color: component.theme.list.itemSecondaryTextColor)), + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: dataSizeString(Int(component.category.size), formatting: DataSizeStringFormatting(strings: component.strings, decimalSeparator: ".")), font: Font.regular(17.0), textColor: component.theme.list.itemSecondaryTextColor)))), environment: {}, containerSize: CGSize(width: availableWidth, height: 100.0) ) - availableWidth = max(1.0, availableWidth - labelSize.width - 4.0) + availableWidth = max(1.0, availableWidth - labelSize.width - 1.0) let titleValueSize = self.titleValue.update( transition: transition, - component: AnyComponent(Text(text: "\(fractionString)%", font: Font.regular(17.0), color: component.theme.list.itemSecondaryTextColor)), + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: "\(fractionString)%", font: Font.regular(17.0), textColor: component.theme.list.itemSecondaryTextColor)))), environment: {}, containerSize: CGSize(width: availableWidth, height: 100.0) ) - availableWidth = max(1.0, availableWidth - titleValueSize.width - 1.0) + availableWidth = max(1.0, availableWidth - titleValueSize.width - 4.0) let titleSize = self.title.update( transition: transition, - component: AnyComponent(Text(text: component.category.title, font: Font.regular(17.0), color: component.theme.list.itemPrimaryTextColor)), + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: component.category.title, font: Font.regular(17.0), textColor: component.theme.list.itemPrimaryTextColor)))), environment: {}, containerSize: CGSize(width: availableWidth, height: 100.0) ) diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageKeepSizeComponent.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageKeepSizeComponent.swift new file mode 100644 index 0000000000..95c6c5623e --- /dev/null +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageKeepSizeComponent.swift @@ -0,0 +1,208 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import ComponentFlow +import SwiftSignalKit +import ViewControllerComponent +import ComponentDisplayAdapters +import TelegramPresentationData +import AccountContext +import TelegramCore +import MultilineTextComponent +import EmojiStatusComponent +import Postbox +import CheckNode +import SolidRoundedButtonComponent +import LegacyComponents + +private func stringForCacheSize(strings: PresentationStrings, size: Int32) -> String { + if size > 100 { + return strings.Cache_NoLimit + } else { + return dataSizeString(Int64(size) * 1024 * 1024 * 1024, formatting: DataSizeStringFormatting(strings: strings, decimalSeparator: ".")) + } +} + +private func totalDiskSpace() -> Int64 { + do { + let systemAttributes = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String) + return (systemAttributes[FileAttributeKey.systemSize] as? NSNumber)?.int64Value ?? 0 + } catch { + return 0 + } +} + +private let maximumCacheSizeValues: [Int32] = { + let diskSpace = totalDiskSpace() + if diskSpace > 100 * 1024 * 1024 * 1024 { + return [5, 20, 50, Int32.max] + } else if diskSpace > 50 * 1024 * 1024 * 1024 { + return [5, 16, 32, Int32.max] + } else if diskSpace > 24 * 1024 * 1024 * 1024 { + return [2, 8, 16, Int32.max] + } else { + return [1, 4, 8, Int32.max] + } +}() + +final class StorageKeepSizeComponent: Component { + let theme: PresentationTheme + let strings: PresentationStrings + let value: Int32 + let updateValue: (Int32) -> Void + + init( + theme: PresentationTheme, + strings: PresentationStrings, + value: Int32, + updateValue: @escaping (Int32) -> Void + ) { + self.theme = theme + self.strings = strings + self.value = value + self.updateValue = updateValue + } + + static func ==(lhs: StorageKeepSizeComponent, rhs: StorageKeepSizeComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.value != rhs.value { + return false + } + return true + } + + class View: UIView { + private let titles: [ComponentView] + private var sliderView: TGPhotoEditorSliderView? + + private var component: StorageKeepSizeComponent? + private weak var state: EmptyComponentState? + + override init(frame: CGRect) { + self.titles = (0 ..< 4).map { _ in ComponentView() } + + super.init(frame: frame) + + self.clipsToBounds = true + self.layer.cornerRadius = 10.0 + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: StorageKeepSizeComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + let themeUpdated = self.component?.theme !== component.theme + + self.component = component + self.state = state + + if themeUpdated { + self.backgroundColor = component.theme.list.itemBlocksBackgroundColor + } + + let height: CGFloat = 88.0 + + var titleSizes: [CGSize] = [] + for i in 0 ..< self.titles.count { + let titleSize = self.titles[i].update( + transition: .immediate, + component: AnyComponent(Text(text: stringForCacheSize(strings: component.strings, size: maximumCacheSizeValues[i]), font: Font.regular(13.0), color: component.theme.list.itemSecondaryTextColor)), + environment: {}, + containerSize: CGSize(width: 100.0, height: 100.0) + ) + titleSizes.append(titleSize) + } + + let delta = (availableSize.width - 18.0 * 2.0) / CGFloat(titleSizes.count - 1) + for i in 0 ..< titleSizes.count { + let titleSize = titleSizes[i] + if let titleView = self.titles[i].view { + if titleView.superview == nil { + self.addSubview(titleView) + } + + var position: CGFloat = 18.0 + delta * CGFloat(i) + if i == titleSizes.count - 1 { + position -= titleSize.width + } else if i > 0 { + position -= titleSize.width / 2.0 + } + transition.setFrame(view: titleView, frame: CGRect(origin: CGPoint(x: position, y: 15.0), size: titleSize)) + } + } + + var sliderFirstTime = false + let sliderView: TGPhotoEditorSliderView + if let current = self.sliderView { + sliderView = current + } else { + sliderFirstTime = true + sliderView = TGPhotoEditorSliderView() + sliderView.enablePanHandling = true + sliderView.trackCornerRadius = 2.0 + sliderView.lineSize = 4.0 + sliderView.dotSize = 5.0 + sliderView.minimumValue = 0.0 + sliderView.maximumValue = 3.0 + sliderView.startValue = 0.0 + sliderView.disablesInteractiveTransitionGestureRecognizer = true + sliderView.positionsCount = 4 + sliderView.useLinesForPositions = true + sliderView.addTarget(self, action: #selector(self.sliderValueChanged), for: .valueChanged) + self.sliderView = sliderView + self.addSubview(sliderView) + } + + if sliderFirstTime || themeUpdated { + sliderView.backgroundColor = component.theme.list.itemBlocksBackgroundColor + sliderView.backColor = component.theme.list.itemSwitchColors.frameColor + sliderView.startColor = component.theme.list.itemSwitchColors.frameColor + sliderView.trackColor = component.theme.list.itemAccentColor + sliderView.knobImage = PresentationResourcesItemList.knobImage(component.theme) + } + + transition.setFrame(view: sliderView, frame: CGRect(origin: CGPoint(x: 15.0, y: 37.0), size: CGSize(width: availableSize.width - 15.0 * 2.0, height: 44.0))) + sliderView.hitTestEdgeInsets = UIEdgeInsets(top: -sliderView.frame.minX, left: 0.0, bottom: 0.0, right: -sliderView.frame.minX) + + self.updateSliderView() + + return CGSize(width: availableSize.width, height: height) + } + + private func updateSliderView() { + guard let sliderView = self.sliderView, let component = self.component else { + return + } + sliderView.maximumValue = 3.0 + sliderView.positionsCount = 4 + + let value = maximumCacheSizeValues.firstIndex(where: { $0 == component.value }) ?? 0 + sliderView.value = CGFloat(value) + } + + @objc private func sliderValueChanged() { + guard let component = self.component, let sliderView = self.sliderView else { + return + } + + let position = Int(sliderView.value) + let value = maximumCacheSizeValues[position] + component.updateValue(value) + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift index efa7fa4def..93d1911323 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift @@ -262,6 +262,10 @@ final class StorageUsageScreenComponent: Component { private var keepDurationSectionContainerView: UIView private var keepDurationItems: [AnyHashable: ComponentView] = [:] + private let keepSizeTitleView = ComponentView() + private let keepSizeView = ComponentView() + private let keepSizeDescriptionView = ComponentView() + private let panelContainer = ComponentView() private var selectionPanel: ComponentView? @@ -1261,6 +1265,91 @@ final class StorageUsageScreenComponent: Component { } contentHeight += keepDurationDescriptionSize.height contentHeight += 40.0 + + //TODO:localize + let keepSizeTitleSize = self.keepSizeTitleView.update( + transition: transition, + component: AnyComponent(MultilineTextComponent( + text: .markdown( + text: "MAXIMUM CACHE SIZE", attributes: MarkdownAttributes( + body: body, + bold: bold, + link: body, + linkAttribute: { _ in nil } + ) + ), + maximumNumberOfLines: 0 + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0 - 15.0 * 2.0, height: 10000.0) + ) + let keepSizeTitleFrame = CGRect(origin: CGPoint(x: sideInset + 15.0, y: contentHeight), size: keepSizeTitleSize) + if let keepSizeTitleComponentView = self.keepSizeTitleView.view { + if keepSizeTitleComponentView.superview == nil { + self.scrollView.addSubview(keepSizeTitleComponentView) + } + transition.setFrame(view: keepSizeTitleComponentView, frame: keepSizeTitleFrame) + } + contentHeight += keepSizeTitleSize.height + contentHeight += 8.0 + + let keepSizeSize = self.keepSizeView.update( + transition: transition, + component: AnyComponent(StorageKeepSizeComponent( + theme: environment.theme, + strings: environment.strings, + value: cacheSettings?.defaultCacheStorageLimitGigabytes ?? 32, + updateValue: { [weak self] value in + guard let self, let component = self.component else { + return + } + let value = max(5, value) + let _ = updateCacheStorageSettingsInteractively(accountManager: component.context.sharedContext.accountManager, { current in + var current = current + current.defaultCacheStorageLimitGigabytes = value + return current + }).start() + } + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0) + ) + let keepSizeFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: keepSizeSize) + if let keepSizeComponentView = self.keepSizeView.view { + if keepSizeComponentView.superview == nil { + self.scrollView.addSubview(keepSizeComponentView) + } + transition.setFrame(view: keepSizeComponentView, frame: keepSizeFrame) + } + contentHeight += keepSizeSize.height + contentHeight += 8.0 + + //TODO:localize + let keepSizeDescriptionSize = self.keepSizeDescriptionView.update( + transition: transition, + component: AnyComponent(MultilineTextComponent( + text: .markdown( + text: "If your cache size exceeds this limit, the oldest media will be deleted.", attributes: MarkdownAttributes( + body: body, + bold: bold, + link: body, + linkAttribute: { _ in nil } + ) + ), + maximumNumberOfLines: 0 + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0 - 15.0 * 2.0, height: 10000.0) + ) + let keepSizeDescriptionFrame = CGRect(origin: CGPoint(x: sideInset + 15.0, y: contentHeight), size: keepSizeDescriptionSize) + if let keepSizeDescriptionComponentView = self.keepSizeDescriptionView.view { + if keepSizeDescriptionComponentView.superview == nil { + self.scrollView.addSubview(keepSizeDescriptionComponentView) + } + transition.setFrame(view: keepSizeDescriptionComponentView, frame: keepSizeDescriptionFrame) + } + contentHeight += keepSizeDescriptionSize.height + contentHeight += 40.0 } var panelItems: [StorageUsagePanelContainerComponent.Item] = []