mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-08 19:10:53 +00:00
Storage management improvements
This commit is contained in:
parent
3566377d68
commit
bf5382c9b4
@ -791,6 +791,7 @@ public protocol SharedAccountContext: AnyObject {
|
||||
func makeCreateGroupController(context: AccountContext, peerIds: [PeerId], initialTitle: String?, mode: CreateGroupMode, completion: ((PeerId, @escaping () -> Void) -> Void)?) -> ViewController
|
||||
func makeChatRecentActionsController(context: AccountContext, peer: Peer, adminPeerId: PeerId?) -> ViewController
|
||||
func makePrivacyAndSecurityController(context: AccountContext) -> ViewController
|
||||
func makeStorageManagementController(context: AccountContext) -> ViewController
|
||||
func navigateToChatController(_ params: NavigateToChatControllerParams)
|
||||
func navigateToForumChannel(context: AccountContext, peerId: EnginePeer.Id, navigationController: NavigationController)
|
||||
func navigateToForumThread(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64, messageId: EngineMessage.Id?, navigationController: NavigationController, activateInput: ChatControllerActivateInput?, keepStack: NavigateToChatKeepStack) -> Signal<Never, NoError>
|
||||
|
@ -185,7 +185,7 @@ private final class ChatListShimmerNode: ASDisplayNode {
|
||||
let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in }, openForumThread: { _, _ in })
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {})
|
||||
interaction.isInlineMode = isInlineMode
|
||||
|
||||
let items = (0 ..< 2).map { _ -> ChatListItem in
|
||||
|
@ -2004,6 +2004,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
interaction.openPeer(peer, peer, threadId, false)
|
||||
self.listNode.clearHighlightAnimated(true)
|
||||
})
|
||||
}, openStorageManagement: {
|
||||
})
|
||||
chatListInteraction.isSearchMode = true
|
||||
|
||||
@ -3199,7 +3200,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
|
||||
let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in }, openForumThread: { _, _ in })
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {})
|
||||
var isInlineMode = false
|
||||
if case .topics = key {
|
||||
isInlineMode = false
|
||||
|
@ -91,6 +91,7 @@ public final class ChatListNodeInteraction {
|
||||
let activateChatPreview: (ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void
|
||||
let present: (ViewController) -> Void
|
||||
let openForumThread: (EnginePeer.Id, Int64) -> Void
|
||||
let openStorageManagement: () -> Void
|
||||
|
||||
public var searchTextHighightState: String?
|
||||
var highlightedChatLocation: ChatListHighlightedLocation?
|
||||
@ -132,7 +133,8 @@ public final class ChatListNodeInteraction {
|
||||
hidePsa: @escaping (EnginePeer.Id) -> Void,
|
||||
activateChatPreview: @escaping (ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void,
|
||||
present: @escaping (ViewController) -> Void,
|
||||
openForumThread: @escaping (EnginePeer.Id, Int64) -> Void
|
||||
openForumThread: @escaping (EnginePeer.Id, Int64) -> Void,
|
||||
openStorageManagement: @escaping () -> Void
|
||||
) {
|
||||
self.activateSearch = activateSearch
|
||||
self.peerSelected = peerSelected
|
||||
@ -162,6 +164,7 @@ public final class ChatListNodeInteraction {
|
||||
self.animationCache = animationCache
|
||||
self.animationRenderer = animationRenderer
|
||||
self.openForumThread = openForumThread
|
||||
self.openStorageManagement = openStorageManagement
|
||||
}
|
||||
}
|
||||
|
||||
@ -567,6 +570,10 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
), directionHint: entry.directionHint)
|
||||
case let .ArchiveIntro(presentationData):
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListArchiveInfoItem(theme: presentationData.theme, strings: presentationData.strings), directionHint: entry.directionHint)
|
||||
case let .StorageInfo(presentationData, sizeFraction):
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListStorageInfoItem(theme: presentationData.theme, strings: presentationData.strings, sizeFraction: sizeFraction, action: { [weak nodeInteraction] in
|
||||
nodeInteraction?.openStorageManagement()
|
||||
}), directionHint: entry.directionHint)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -778,6 +785,10 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
), directionHint: entry.directionHint)
|
||||
case let .ArchiveIntro(presentationData):
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListArchiveInfoItem(theme: presentationData.theme, strings: presentationData.strings), directionHint: entry.directionHint)
|
||||
case let .StorageInfo(presentationData, sizeFraction):
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListStorageInfoItem(theme: presentationData.theme, strings: presentationData.strings, sizeFraction: sizeFraction, action: { [weak nodeInteraction] in
|
||||
nodeInteraction?.openStorageManagement()
|
||||
}), directionHint: entry.directionHint)
|
||||
case .HeaderEntry:
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListEmptyHeaderItem(), directionHint: entry.directionHint)
|
||||
case let .AdditionalCategory(index: _, id, title, image, appearance, selected, presentationData):
|
||||
@ -1258,6 +1269,12 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
self.peerSelected?(peer, threadId, true, true, nil)
|
||||
})
|
||||
}, openStorageManagement: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let controller = self.context.sharedContext.makeStorageManagementController(context: self.context)
|
||||
self.push?(controller)
|
||||
})
|
||||
nodeInteraction.isInlineMode = isInlineMode
|
||||
|
||||
@ -1323,15 +1340,94 @@ public final class ChatListNode: ListView {
|
||||
displayArchiveIntro = .single(false)
|
||||
}
|
||||
|
||||
let storageInfo: Signal<Double?, NoError>
|
||||
if case .chatList(groupId: .root) = location, chatListFilter == nil {
|
||||
let storageBox = context.account.postbox.mediaBox.storageBox
|
||||
storageInfo = storageBox.totalSize()
|
||||
|> take(1)
|
||||
|> mapToSignal { initialSize -> Signal<Double?, NoError> in
|
||||
let fractionLimit: Double = 0.3
|
||||
|
||||
let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String)
|
||||
let deviceFreeSpace = (systemAttributes?[FileAttributeKey.systemFreeSize] as? NSNumber)?.int64Value ?? 0
|
||||
|
||||
let initialFraction: Double
|
||||
if deviceFreeSpace != 0 && initialSize != 0 {
|
||||
initialFraction = Double(initialSize) / Double(deviceFreeSpace + initialSize)
|
||||
} else {
|
||||
initialFraction = 0.0
|
||||
}
|
||||
|
||||
let initialReportSize: Double?
|
||||
if initialFraction > fractionLimit {
|
||||
initialReportSize = Double(initialSize)
|
||||
} else {
|
||||
initialReportSize = nil
|
||||
}
|
||||
|
||||
final class ReportState {
|
||||
var lastSize: Int64
|
||||
|
||||
init(lastSize: Int64) {
|
||||
self.lastSize = lastSize
|
||||
}
|
||||
}
|
||||
|
||||
let state = Atomic(value: ReportState(lastSize: initialSize))
|
||||
let updatedReportSize: Signal<Double?, NoError> = Signal { subscriber in
|
||||
let disposable = storageBox.totalSize().start(next: { size in
|
||||
let updatedSize = state.with { state -> Int64 in
|
||||
if abs(initialSize - size) > 50 * 1024 * 1024 {
|
||||
state.lastSize = size
|
||||
return size
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
if updatedSize >= 0 {
|
||||
let deviceFreeSpace = (systemAttributes?[FileAttributeKey.systemFreeSize] as? NSNumber)?.int64Value ?? 0
|
||||
|
||||
let updatedFraction: Double
|
||||
if deviceFreeSpace != 0 && updatedSize != 0 {
|
||||
updatedFraction = Double(updatedSize) / Double(deviceFreeSpace + updatedSize)
|
||||
} else {
|
||||
updatedFraction = 0.0
|
||||
}
|
||||
|
||||
let updatedReportSize: Double?
|
||||
if updatedFraction > fractionLimit {
|
||||
updatedReportSize = Double(updatedSize)
|
||||
} else {
|
||||
updatedReportSize = nil
|
||||
}
|
||||
|
||||
subscriber.putNext(updatedReportSize)
|
||||
}
|
||||
})
|
||||
|
||||
return ActionDisposable {
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
return .single(initialReportSize)
|
||||
|> then(
|
||||
updatedReportSize
|
||||
)
|
||||
}
|
||||
} else {
|
||||
storageInfo = .single(nil)
|
||||
}
|
||||
|
||||
let currentPeerId: EnginePeer.Id = context.account.peerId
|
||||
|
||||
let chatListNodeViewTransition = combineLatest(queue: viewProcessingQueue, hideArchivedFolderByDefault, displayArchiveIntro, savedMessagesPeer, chatListViewUpdate, self.statePromise.get())
|
||||
|> mapToQueue { (hideArchivedFolderByDefault, displayArchiveIntro, savedMessagesPeer, updateAndFilter, state) -> Signal<ChatListNodeListViewTransition, NoError> in
|
||||
let chatListNodeViewTransition = combineLatest(queue: viewProcessingQueue, hideArchivedFolderByDefault, displayArchiveIntro, storageInfo, savedMessagesPeer, chatListViewUpdate, self.statePromise.get())
|
||||
|> mapToQueue { (hideArchivedFolderByDefault, displayArchiveIntro, storageInfo, savedMessagesPeer, updateAndFilter, state) -> Signal<ChatListNodeListViewTransition, NoError> in
|
||||
let (update, filter) = updateAndFilter
|
||||
|
||||
let previousHideArchivedFolderByDefaultValue = previousHideArchivedFolderByDefault.swap(hideArchivedFolderByDefault)
|
||||
|
||||
let (rawEntries, isLoading) = chatListNodeEntriesForView(update.list, state: state, savedMessagesPeer: savedMessagesPeer, foundPeers: state.foundPeers, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, mode: mode, chatListLocation: location)
|
||||
let (rawEntries, isLoading) = chatListNodeEntriesForView(update.list, state: state, savedMessagesPeer: savedMessagesPeer, foundPeers: state.foundPeers, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, storageInfo: storageInfo, mode: mode, chatListLocation: location)
|
||||
let entries = rawEntries.filter { entry in
|
||||
switch entry {
|
||||
case let .PeerEntry(peerEntry):
|
||||
@ -2361,7 +2457,7 @@ public final class ChatListNode: ListView {
|
||||
} else {
|
||||
break loop
|
||||
}
|
||||
case .ArchiveIntro, .HeaderEntry, .AdditionalCategory:
|
||||
case .ArchiveIntro, .StorageInfo, .HeaderEntry, .AdditionalCategory:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ enum ChatListNodeEntryId: Hashable {
|
||||
case ThreadId(Int64)
|
||||
case GroupId(EngineChatList.Group)
|
||||
case ArchiveIntro
|
||||
case StorageInfo
|
||||
case additionalCategory(Int)
|
||||
}
|
||||
|
||||
@ -234,6 +235,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
case HoleEntry(EngineMessage.Index, theme: PresentationTheme)
|
||||
case GroupReferenceEntry(index: EngineChatList.Item.Index, presentationData: ChatListPresentationData, groupId: EngineChatList.Group, peers: [EngineChatList.GroupItem.Item], message: EngineMessage?, editing: Bool, unreadCount: Int, revealed: Bool, hiddenByDefault: Bool)
|
||||
case ArchiveIntro(presentationData: ChatListPresentationData)
|
||||
case StorageInfo(presentationData: ChatListPresentationData, sizeFraction: Double)
|
||||
case AdditionalCategory(index: Int, id: Int, title: String, image: UIImage?, appearance: ChatListNodeAdditionalCategory.Appearance, selected: Bool, presentationData: ChatListPresentationData)
|
||||
|
||||
var sortIndex: ChatListNodeEntrySortIndex {
|
||||
@ -248,6 +250,8 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
return .index(index)
|
||||
case .ArchiveIntro:
|
||||
return .index(.chatList(EngineChatList.Item.Index.ChatList.absoluteUpperBound.successor))
|
||||
case .StorageInfo:
|
||||
return .index(.chatList(EngineChatList.Item.Index.ChatList.absoluteUpperBound.successor.successor))
|
||||
case let .AdditionalCategory(index, _, _, _, _, _, _):
|
||||
return .additionalCategory(index)
|
||||
}
|
||||
@ -270,6 +274,8 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
return .GroupId(groupId)
|
||||
case .ArchiveIntro:
|
||||
return .ArchiveIntro
|
||||
case .StorageInfo:
|
||||
return .StorageInfo
|
||||
case let .AdditionalCategory(_, id, _, _, _, _, _):
|
||||
return .additionalCategory(id)
|
||||
}
|
||||
@ -342,6 +348,18 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .StorageInfo(lhsPresentationData, lhsInfo):
|
||||
if case let .StorageInfo(rhsPresentationData, rhsInfo) = rhs {
|
||||
if lhsPresentationData !== rhsPresentationData {
|
||||
return false
|
||||
}
|
||||
if lhsInfo != rhsInfo {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .AdditionalCategory(lhsIndex, lhsId, lhsTitle, lhsImage, lhsAppearance, lhsSelected, lhsPresentationData):
|
||||
if case let .AdditionalCategory(rhsIndex, rhsId, rhsTitle, rhsImage, rhsAppearance, rhsSelected, rhsPresentationData) = rhs {
|
||||
if lhsIndex != rhsIndex {
|
||||
@ -381,7 +399,7 @@ private func offsetPinnedIndex(_ index: EngineChatList.Item.Index, offset: UInt1
|
||||
}
|
||||
}
|
||||
|
||||
func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState, savedMessagesPeer: EnginePeer?, foundPeers: [(EnginePeer, EnginePeer?)], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, mode: ChatListNodeMode, chatListLocation: ChatListControllerLocation) -> (entries: [ChatListNodeEntry], loading: Bool) {
|
||||
func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState, savedMessagesPeer: EnginePeer?, foundPeers: [(EnginePeer, EnginePeer?)], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, storageInfo: Double?, mode: ChatListNodeMode, chatListLocation: ChatListControllerLocation) -> (entries: [ChatListNodeEntry], loading: Bool) {
|
||||
var result: [ChatListNodeEntry] = []
|
||||
|
||||
var pinnedIndexOffset: UInt16 = 0
|
||||
@ -643,6 +661,9 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState
|
||||
if displayArchiveIntro {
|
||||
result.append(.ArchiveIntro(presentationData: state.presentationData))
|
||||
}
|
||||
if let storageInfo {
|
||||
result.append(.StorageInfo(presentationData: state.presentationData, sizeFraction: storageInfo))
|
||||
}
|
||||
|
||||
result.append(.HeaderEntry)
|
||||
}
|
||||
|
164
submodules/ChatListUI/Sources/Node/ChatListStorageInfoItem.swift
Normal file
164
submodules/ChatListUI/Sources/Node/ChatListStorageInfoItem.swift
Normal file
@ -0,0 +1,164 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Postbox
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import ListSectionHeaderNode
|
||||
import AppBundle
|
||||
|
||||
class ChatListStorageInfoItem: ListViewItem {
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let sizeFraction: Double
|
||||
let action: () -> Void
|
||||
|
||||
let selectable: Bool = true
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, sizeFraction: Double, action: @escaping () -> Void) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.sizeFraction = sizeFraction
|
||||
self.action = action
|
||||
}
|
||||
|
||||
func selected(listView: ListView) {
|
||||
listView.clearHighlightAnimated(true)
|
||||
|
||||
self.action()
|
||||
}
|
||||
|
||||
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
async {
|
||||
let node = ChatListStorageInfoItemNode()
|
||||
|
||||
let (nodeLayout, apply) = node.asyncLayout()(self, params, false)
|
||||
|
||||
node.insets = nodeLayout.insets
|
||||
node.contentSize = nodeLayout.contentSize
|
||||
|
||||
Queue.mainQueue().async {
|
||||
completion(node, {
|
||||
return (nil, { _ in
|
||||
apply()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||
Queue.mainQueue().async {
|
||||
assert(node() is ChatListStorageInfoItemNode)
|
||||
if let nodeValue = node() as? ChatListStorageInfoItemNode {
|
||||
|
||||
let layout = nodeValue.asyncLayout()
|
||||
async {
|
||||
let (nodeLayout, apply) = layout(self, params, nextItem == nil)
|
||||
Queue.mainQueue().async {
|
||||
completion(nodeLayout, { _ in
|
||||
apply()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let separatorHeight = 1.0 / UIScreen.main.scale
|
||||
|
||||
private let titleFont = Font.semibold(15.0)
|
||||
private let textFont = Font.regular(15.0)
|
||||
|
||||
class ChatListStorageInfoItemNode: ListViewItemNode {
|
||||
private let titleNode: TextNode
|
||||
private let textNode: TextNode
|
||||
private let arrowNode: ASImageNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
|
||||
private var item: ChatListStorageInfoItem?
|
||||
|
||||
required init() {
|
||||
self.titleNode = TextNode()
|
||||
self.textNode = TextNode()
|
||||
self.arrowNode = ASImageNode()
|
||||
self.separatorNode = ASDisplayNode()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
self.addSubnode(self.separatorNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.arrowNode)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
}
|
||||
|
||||
override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
|
||||
let layout = self.asyncLayout()
|
||||
let (_, apply) = layout(item as! ChatListStorageInfoItem, params, nextItem == nil)
|
||||
apply()
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: ChatListStorageInfoItem, _ params: ListViewItemLayoutParams, _ isLast: Bool) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let previousItem = self.item
|
||||
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let makeTextLayout = TextNode.asyncLayout(self.textNode)
|
||||
|
||||
return { item, params, last in
|
||||
let baseWidth = params.width - params.leftInset - params.rightInset
|
||||
let _ = baseWidth
|
||||
|
||||
let sideInset: CGFloat = params.leftInset + 16.0
|
||||
let height: CGFloat = 54.0
|
||||
let rightInset: CGFloat = sideInset + 24.0
|
||||
|
||||
let themeUpdated = item.theme !== previousItem?.theme
|
||||
|
||||
//TODO:localize
|
||||
let titleString = NSMutableAttributedString()
|
||||
titleString.append(NSAttributedString(string: "Free up to ", font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor))
|
||||
let sizeString = dataSizeString(Int64(item.sizeFraction), formatting: DataSizeStringFormatting(strings: item.strings, decimalSeparator: "."))
|
||||
titleString.append(NSAttributedString(string: sizeString, font: titleFont, textColor: item.theme.rootController.navigationBar.accentTextColor))
|
||||
|
||||
let titleLayout = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0)))
|
||||
|
||||
//TODO:localize
|
||||
let textLayout = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "Clear storage space on your iPhone", font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor), maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0)))
|
||||
|
||||
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: height), insets: UIEdgeInsets())
|
||||
|
||||
return (layout, { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
|
||||
if themeUpdated {
|
||||
strongSelf.backgroundColor = item.theme.chatList.pinnedItemBackgroundColor
|
||||
strongSelf.separatorNode.backgroundColor = item.theme.chatList.itemSeparatorColor
|
||||
strongSelf.arrowNode.image = PresentationResourcesItemList.disclosureArrowImage(item.theme)
|
||||
}
|
||||
|
||||
strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - UIScreenPixel), size: CGSize(width: layout.size.width, height: UIScreenPixel))
|
||||
|
||||
let _ = titleLayout.1()
|
||||
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: sideInset, y: 9.0), size: titleLayout.0.size)
|
||||
|
||||
let _ = textLayout.1()
|
||||
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: sideInset, y: strongSelf.titleNode.frame.maxY - 0.0), size: textLayout.0.size)
|
||||
|
||||
if let image = strongSelf.arrowNode.image {
|
||||
strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: layout.size.width - sideInset - image.size.width + 8.0, y: floor((layout.size.height - image.size.height) / 2.0)), size: image.size)
|
||||
}
|
||||
|
||||
strongSelf.contentSize = layout.contentSize
|
||||
strongSelf.insets = layout.insets
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -91,6 +91,7 @@ public final class HashtagSearchController: TelegramBaseController {
|
||||
gesture?.cancel()
|
||||
}, present: { _ in
|
||||
}, openForumThread: { _, _ in
|
||||
}, openStorageManagement: {
|
||||
})
|
||||
|
||||
let previousSearchItems = Atomic<[ChatListSearchEntry]?>(value: nil)
|
||||
|
@ -158,6 +158,9 @@ public final class StorageBox {
|
||||
let contentTypeStatsTable: ValueBoxTable
|
||||
let metadataTable: ValueBoxTable
|
||||
|
||||
let totalSizeSubscribers = Bag<(Int64) -> Void>()
|
||||
private var totalSize: Int64 = 0
|
||||
|
||||
init(queue: Queue, logger: StorageBox.Logger, basePath: String) {
|
||||
self.queue = queue
|
||||
self.logger = logger
|
||||
@ -183,6 +186,8 @@ public final class StorageBox {
|
||||
self.metadataTable = ValueBoxTable(id: 21, keyType: .binary, compactValuesOnCreation: true)
|
||||
|
||||
self.performUpdatesIfNeeded()
|
||||
|
||||
self.updateTotalSize()
|
||||
}
|
||||
|
||||
private func performUpdatesIfNeeded() {
|
||||
@ -230,6 +235,35 @@ public final class StorageBox {
|
||||
})
|
||||
}
|
||||
|
||||
func updateTotalSize() {
|
||||
self.valueBox.begin()
|
||||
|
||||
var totalSize: Int64 = 0
|
||||
self.valueBox.scan(self.contentTypeStatsTable, values: { key, value in
|
||||
var size: Int64 = 0
|
||||
value.read(&size, offset: 0, length: 8)
|
||||
totalSize += size
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
self.valueBox.commit()
|
||||
|
||||
if self.totalSize != totalSize {
|
||||
self.totalSize = totalSize
|
||||
|
||||
for f in self.totalSizeSubscribers.copyItems() {
|
||||
f(totalSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func incrementalUpdateTotalSize() {
|
||||
for f in self.totalSizeSubscribers.copyItems() {
|
||||
f(totalSize)
|
||||
}
|
||||
}
|
||||
|
||||
func reset() {
|
||||
self.valueBox.begin()
|
||||
|
||||
@ -241,6 +275,8 @@ public final class StorageBox {
|
||||
self.valueBox.removeAllFromTable(self.metadataTable)
|
||||
|
||||
self.valueBox.commit()
|
||||
|
||||
self.updateTotalSize()
|
||||
}
|
||||
|
||||
private func internalAddSize(contentType: UInt8, delta: Int64) {
|
||||
@ -260,6 +296,8 @@ public final class StorageBox {
|
||||
}
|
||||
|
||||
self.valueBox.set(self.contentTypeStatsTable, key: key, value: MemoryBuffer(memory: ¤tSize, capacity: 8, length: 8, freeWhenDone: false))
|
||||
|
||||
self.totalSize += delta
|
||||
}
|
||||
|
||||
private func internalAddSize(peerId: Int64, contentType: UInt8, delta: Int64) {
|
||||
@ -390,6 +428,8 @@ public final class StorageBox {
|
||||
}
|
||||
|
||||
self.valueBox.commit()
|
||||
|
||||
self.incrementalUpdateTotalSize()
|
||||
}
|
||||
|
||||
private func peerIdsReferencing(hashId: HashId) -> Set<Int64> {
|
||||
@ -435,6 +475,8 @@ public final class StorageBox {
|
||||
}
|
||||
|
||||
self.valueBox.commit()
|
||||
|
||||
self.incrementalUpdateTotalSize()
|
||||
}
|
||||
|
||||
func addEmptyReferencesIfNotReferenced(ids: [(id: Data, size: Int64)], contentType: UInt8) -> Int {
|
||||
@ -508,6 +550,8 @@ public final class StorageBox {
|
||||
}
|
||||
|
||||
self.valueBox.commit()
|
||||
|
||||
self.incrementalUpdateTotalSize()
|
||||
}
|
||||
|
||||
func allPeerIds() -> [PeerId] {
|
||||
@ -615,6 +659,22 @@ public final class StorageBox {
|
||||
return (ids, nextId)
|
||||
}
|
||||
|
||||
func subscribeTotalSize(next: @escaping (Int64) -> Void) -> Disposable {
|
||||
let index = self.totalSizeSubscribers.add(next)
|
||||
|
||||
next(self.totalSize)
|
||||
|
||||
let queue = self.queue
|
||||
return ActionDisposable { [weak self] in
|
||||
queue.async {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.totalSizeSubscribers.remove(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func all() -> [Entry] {
|
||||
var result: [Entry] = []
|
||||
|
||||
@ -825,6 +885,8 @@ public final class StorageBox {
|
||||
|
||||
self.valueBox.commit()
|
||||
|
||||
self.incrementalUpdateTotalSize()
|
||||
|
||||
return Array(resultIds)
|
||||
}
|
||||
|
||||
@ -854,6 +916,8 @@ public final class StorageBox {
|
||||
|
||||
self.valueBox.commit()
|
||||
|
||||
self.incrementalUpdateTotalSize()
|
||||
|
||||
return Array(resultIds)
|
||||
}
|
||||
}
|
||||
@ -973,4 +1037,12 @@ public final class StorageBox {
|
||||
return EmptyDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public func totalSize() -> Signal<Int64, NoError> {
|
||||
return self.impl.signalWith { impl, subscriber in
|
||||
return impl.subscribeTotalSize(next: { value in
|
||||
subscriber.putNext(value)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -598,7 +598,6 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da
|
||||
pushControllerImpl?(StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { category in
|
||||
return storageUsageExceptionsScreen(context: context, category: category)
|
||||
}))
|
||||
//pushControllerImpl?(storageUsageController(context: context, cacheUsagePromise: cacheUsagePromise))
|
||||
}, openNetworkUsage: {
|
||||
pushControllerImpl?(networkUsageStatsController(context: context))
|
||||
}, openProxy: {
|
||||
|
@ -222,7 +222,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
||||
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in
|
||||
}, activateChatPreview: { _, _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in }, openForumThread: { _, _ in })
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {})
|
||||
|
||||
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
|
||||
|
||||
|
@ -843,7 +843,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
}, activateChatPreview: { _, _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in
|
||||
}, openForumThread: { _, _ in })
|
||||
}, openForumThread: { _, _ in },
|
||||
openStorageManagement: {})
|
||||
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
|
||||
|
||||
func makeChatListItem(
|
||||
|
@ -367,7 +367,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}, activateChatPreview: { _, _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in
|
||||
}, openForumThread: { _, _ in })
|
||||
}, openForumThread: { _, _ in }, openStorageManagement: {})
|
||||
|
||||
func makeChatListItem(
|
||||
peer: EnginePeer,
|
||||
|
@ -351,6 +351,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/ChatListHeaderComponent",
|
||||
"//submodules/TelegramUI/Components/ChatInputNode",
|
||||
"//submodules/TelegramUI/Components/ChatEntityKeyboardInputNode",
|
||||
"//submodules/TelegramUI/Components/StorageUsageScreen",
|
||||
"//submodules/MediaPasteboardUI:MediaPasteboardUI",
|
||||
"//submodules/DrawingUI:DrawingUI",
|
||||
"//submodules/FeaturedStickersScreen:FeaturedStickersScreen",
|
||||
|
@ -1163,11 +1163,11 @@ final class PieChartComponent: Component {
|
||||
let fractionValue: Double = floor(item.displayValue * 100.0 * 10.0) / 10.0
|
||||
let fractionString: String
|
||||
if fractionValue < 0.1 {
|
||||
fractionString = "<0.1"
|
||||
fractionString = "<0.1%"
|
||||
} else if abs(Double(Int(fractionValue)) - fractionValue) < 0.001 {
|
||||
fractionString = "\(Int(fractionValue))"
|
||||
fractionString = "\(Int(fractionValue))%"
|
||||
} else {
|
||||
fractionString = "\(fractionValue)"
|
||||
fractionString = "\(fractionValue)%"
|
||||
}
|
||||
|
||||
let tooltipSize = tooltip.update(
|
||||
|
@ -262,6 +262,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
|
||||
}
|
||||
}, present: { _ in
|
||||
}, openForumThread: { _, _ in
|
||||
}, openStorageManagement: {
|
||||
})
|
||||
interaction.searchTextHighightState = searchQuery
|
||||
self.interaction = interaction
|
||||
|
@ -29,6 +29,7 @@ import PremiumUI
|
||||
import StickerPackPreviewUI
|
||||
import ChatControllerInteraction
|
||||
import ChatPresentationInterfaceState
|
||||
import StorageUsageScreen
|
||||
|
||||
private final class AccountUserInterfaceInUseContext {
|
||||
let subscribers = Bag<(Bool) -> Void>()
|
||||
@ -1424,6 +1425,15 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return SettingsUI.makePrivacyAndSecurityController(context: context)
|
||||
}
|
||||
|
||||
public func makeStorageManagementController(context: AccountContext) -> ViewController {
|
||||
return StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { [weak context] category in
|
||||
guard let context else {
|
||||
return nil
|
||||
}
|
||||
return storageUsageExceptionsScreen(context: context, category: category)
|
||||
})
|
||||
}
|
||||
|
||||
public func makePremiumIntroController(context: AccountContext, source: PremiumIntroSource) -> ViewController {
|
||||
let mappedSource: PremiumSource
|
||||
switch source {
|
||||
|
Loading…
x
Reference in New Issue
Block a user