feat: display clean cache result
This commit is contained in:
parent
6211663508
commit
70b75c8eba
|
@ -43,6 +43,10 @@
|
||||||
"delete_post": {
|
"delete_post": {
|
||||||
"title": "Are you sure you want to delete this post?",
|
"title": "Are you sure you want to delete this post?",
|
||||||
"delete": "Delete"
|
"delete": "Delete"
|
||||||
|
},
|
||||||
|
"clean_cache": {
|
||||||
|
"title": "Clean Cache",
|
||||||
|
"message": "Successfully clean %s cache."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"controls": {
|
"controls": {
|
||||||
|
|
|
@ -21,6 +21,14 @@ internal enum L10n {
|
||||||
return L10n.tr("Localizable", "Common.Alerts.BlockDomain.Title", String(describing: p1))
|
return L10n.tr("Localizable", "Common.Alerts.BlockDomain.Title", String(describing: p1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
internal enum CleanCache {
|
||||||
|
/// Successfully clean %@ cache.
|
||||||
|
internal static func message(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Localizable", "Common.Alerts.CleanCache.Message", String(describing: p1))
|
||||||
|
}
|
||||||
|
/// Clean Cache
|
||||||
|
internal static let title = L10n.tr("Localizable", "Common.Alerts.CleanCache.Title")
|
||||||
|
}
|
||||||
internal enum Common {
|
internal enum Common {
|
||||||
/// Please try again.
|
/// Please try again.
|
||||||
internal static let pleaseTryAgain = L10n.tr("Localizable", "Common.Alerts.Common.PleaseTryAgain")
|
internal static let pleaseTryAgain = L10n.tr("Localizable", "Common.Alerts.Common.PleaseTryAgain")
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
"Common.Alerts.BlockDomain.BlockEntireDomain" = "Block entire domain";
|
"Common.Alerts.BlockDomain.BlockEntireDomain" = "Block entire domain";
|
||||||
"Common.Alerts.BlockDomain.Title" = "Are you really, really sure you want to block the entire %@? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.";
|
"Common.Alerts.BlockDomain.Title" = "Are you really, really sure you want to block the entire %@? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.";
|
||||||
|
"Common.Alerts.CleanCache.Message" = "Successfully clean %@ cache.";
|
||||||
|
"Common.Alerts.CleanCache.Title" = "Clean Cache";
|
||||||
"Common.Alerts.Common.PleaseTryAgain" = "Please try again.";
|
"Common.Alerts.Common.PleaseTryAgain" = "Please try again.";
|
||||||
"Common.Alerts.Common.PleaseTryAgainLater" = "Please try again later.";
|
"Common.Alerts.Common.PleaseTryAgainLater" = "Please try again later.";
|
||||||
"Common.Alerts.DeletePost.Delete" = "Delete";
|
"Common.Alerts.DeletePost.Delete" = "Delete";
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
"Common.Alerts.BlockDomain.BlockEntireDomain" = "Block entire domain";
|
"Common.Alerts.BlockDomain.BlockEntireDomain" = "Block entire domain";
|
||||||
"Common.Alerts.BlockDomain.Title" = "Are you really, really sure you want to block the entire %@? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.";
|
"Common.Alerts.BlockDomain.Title" = "Are you really, really sure you want to block the entire %@? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.";
|
||||||
|
"Common.Alerts.CleanCache.Message" = "Successfully clean %@ cache.";
|
||||||
|
"Common.Alerts.CleanCache.Title" = "Clean Cache";
|
||||||
"Common.Alerts.Common.PleaseTryAgain" = "Please try again.";
|
"Common.Alerts.Common.PleaseTryAgain" = "Please try again.";
|
||||||
"Common.Alerts.Common.PleaseTryAgainLater" = "Please try again later.";
|
"Common.Alerts.Common.PleaseTryAgainLater" = "Please try again later.";
|
||||||
"Common.Alerts.DeletePost.Delete" = "Delete";
|
"Common.Alerts.DeletePost.Delete" = "Delete";
|
||||||
|
|
|
@ -12,8 +12,7 @@ import ActiveLabel
|
||||||
import CoreData
|
import CoreData
|
||||||
import CoreDataStack
|
import CoreDataStack
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
import AlamofireImage
|
|
||||||
import Kingfisher
|
|
||||||
|
|
||||||
class SettingsViewController: UIViewController, NeedsDependency {
|
class SettingsViewController: UIViewController, NeedsDependency {
|
||||||
|
|
||||||
|
@ -319,36 +318,44 @@ extension SettingsViewController: UITableViewDelegate {
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
guard let dataSource = viewModel.dataSource else { return }
|
guard let dataSource = viewModel.dataSource else { return }
|
||||||
let item = dataSource.itemIdentifier(for: indexPath)
|
guard let item = dataSource.itemIdentifier(for: indexPath) else { return }
|
||||||
|
|
||||||
switch item {
|
switch item {
|
||||||
case .boringZone:
|
case .apperance:
|
||||||
guard let url = viewModel.privacyURL else { break }
|
// do nothing
|
||||||
coordinator.present(
|
break
|
||||||
scene: .safari(url: url),
|
case .notification:
|
||||||
from: self,
|
// do nothing
|
||||||
transition: .safariPresent(animated: true, completion: nil)
|
break
|
||||||
)
|
case .boringZone(let link), .spicyZone(let link):
|
||||||
case .spicyZone(let link):
|
switch link {
|
||||||
// clear media cache
|
case .termsOfService, .privacyPolicy:
|
||||||
if link.title == L10n.Scene.Settings.Section.Spicyzone.clear {
|
// same URL
|
||||||
// clean image cache for AlamofireImage
|
guard let url = viewModel.privacyURL else { break }
|
||||||
let diskBytes = ImageDownloader.defaultURLCache().currentDiskUsage
|
coordinator.present(
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: diskBytes %d", ((#file as NSString).lastPathComponent), #line, #function, diskBytes)
|
scene: .safari(url: url),
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: clean image cache", ((#file as NSString).lastPathComponent), #line, #function)
|
from: self,
|
||||||
ImageDownloader.defaultURLCache().removeAllCachedResponses()
|
transition: .safariPresent(animated: true, completion: nil)
|
||||||
let cleanedDiskBytes = ImageDownloader.defaultURLCache().currentDiskUsage
|
)
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: diskBytes %d", ((#file as NSString).lastPathComponent), #line, #function, cleanedDiskBytes)
|
case .clearMediaCache:
|
||||||
|
context.purgeCache()
|
||||||
// clean Kingfisher Cache
|
.receive(on: RunLoop.main)
|
||||||
KingfisherManager.shared.cache.clearDiskCache()
|
.sink { [weak self] byteCount in
|
||||||
}
|
guard let self = self else { return }
|
||||||
// logout
|
let byteCountformatted = AppContext.byteCountFormatter.string(fromByteCount: Int64(byteCount))
|
||||||
if link.title == L10n.Scene.Settings.Section.Spicyzone.signout {
|
let alertController = UIAlertController(
|
||||||
|
title: L10n.Common.Alerts.CleanCache.title,
|
||||||
|
message: L10n.Common.Alerts.CleanCache.message(byteCountformatted),
|
||||||
|
preferredStyle: .alert
|
||||||
|
)
|
||||||
|
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
|
||||||
|
alertController.addAction(okAction)
|
||||||
|
self.coordinator.present(scene: .alertController(alertController: alertController), from: nil, transition: .alertController(animated: true, completion: nil))
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
case .signOut:
|
||||||
alertToSignout()
|
alertToSignout()
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ import UIKit
|
||||||
import Combine
|
import Combine
|
||||||
import CoreData
|
import CoreData
|
||||||
import CoreDataStack
|
import CoreDataStack
|
||||||
|
import AlamofireImage
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
class AppContext: ObservableObject {
|
class AppContext: ObservableObject {
|
||||||
|
|
||||||
|
@ -99,3 +101,107 @@ class AppContext: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension AppContext {
|
||||||
|
|
||||||
|
typealias ByteCount = Int
|
||||||
|
|
||||||
|
static let byteCountFormatter: ByteCountFormatter = {
|
||||||
|
let formatter = ByteCountFormatter()
|
||||||
|
return formatter
|
||||||
|
}()
|
||||||
|
|
||||||
|
private static let purgeCacheWorkingQueue = DispatchQueue(label: "org.joinmastodon.Mastodon.AppContext.purgeCacheWorkingQueue")
|
||||||
|
|
||||||
|
func purgeCache() -> AnyPublisher<ByteCount, Never> {
|
||||||
|
Publishers.MergeMany([
|
||||||
|
AppContext.purgeAlamofireImageCache(),
|
||||||
|
AppContext.purgeKingfisherCache(),
|
||||||
|
AppContext.purgeTemporaryDirectory(),
|
||||||
|
])
|
||||||
|
.reduce(0, +)
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func purgeAlamofireImageCache() -> AnyPublisher<ByteCount, Never> {
|
||||||
|
Future<ByteCount, Never> { promise in
|
||||||
|
AppContext.purgeCacheWorkingQueue.async {
|
||||||
|
// clean image cache for AlamofireImage
|
||||||
|
let diskBytes = ImageDownloader.defaultURLCache().currentDiskUsage
|
||||||
|
ImageDownloader.defaultURLCache().removeAllCachedResponses()
|
||||||
|
let currentDiskBytes = ImageDownloader.defaultURLCache().currentDiskUsage
|
||||||
|
let purgedDiskBytes = max(0, diskBytes - currentDiskBytes)
|
||||||
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: purge AlamofireImage cache bytes: %ld -> %ld (%ld)", ((#file as NSString).lastPathComponent), #line, #function, diskBytes, currentDiskBytes, purgedDiskBytes)
|
||||||
|
promise(.success(purgedDiskBytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func purgeKingfisherCache() -> AnyPublisher<ByteCount, Never> {
|
||||||
|
Future<ByteCount, Never> { promise in
|
||||||
|
KingfisherManager.shared.cache.calculateDiskStorageSize { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let diskBytes):
|
||||||
|
KingfisherManager.shared.cache.clearCache()
|
||||||
|
KingfisherManager.shared.cache.calculateDiskStorageSize { currentResult in
|
||||||
|
switch currentResult {
|
||||||
|
case .success(let currentDiskBytes):
|
||||||
|
let purgedDiskBytes = max(0, Int(diskBytes) - Int(currentDiskBytes))
|
||||||
|
promise(.success(purgedDiskBytes))
|
||||||
|
case .failure:
|
||||||
|
promise(.success(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .failure:
|
||||||
|
promise(.success(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func purgeTemporaryDirectory() -> AnyPublisher<ByteCount, Never> {
|
||||||
|
Future<ByteCount, Never> { promise in
|
||||||
|
AppContext.purgeCacheWorkingQueue.async {
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
let temporaryDirectoryURL = fileManager.temporaryDirectory
|
||||||
|
|
||||||
|
let resourceKeys = Set<URLResourceKey>([.fileSizeKey, .isDirectoryKey])
|
||||||
|
guard let directoryEnumerator = fileManager.enumerator(
|
||||||
|
at: temporaryDirectoryURL,
|
||||||
|
includingPropertiesForKeys: Array(resourceKeys),
|
||||||
|
options: .skipsHiddenFiles
|
||||||
|
) else {
|
||||||
|
promise(.success(0))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileURLs: [URL] = []
|
||||||
|
var totalFileSizeInBytes = 0
|
||||||
|
for case let fileURL as URL in directoryEnumerator {
|
||||||
|
guard let resourceValues = try? fileURL.resourceValues(forKeys: resourceKeys),
|
||||||
|
let isDirectory = resourceValues.isDirectory else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
guard !isDirectory else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fileURLs.append(fileURL)
|
||||||
|
totalFileSizeInBytes += resourceValues.fileSize ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for fileURL in fileURLs {
|
||||||
|
try? fileManager.removeItem(at: fileURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
promise(.success(totalFileSizeInBytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: purge temporary directory success", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue