feat: display clean cache result

This commit is contained in:
CMK 2021-06-01 14:07:44 +08:00
parent 6211663508
commit 70b75c8eba
6 changed files with 157 additions and 28 deletions

View File

@ -43,6 +43,10 @@
"delete_post": {
"title": "Are you sure you want to delete this post?",
"delete": "Delete"
},
"clean_cache": {
"title": "Clean Cache",
"message": "Successfully clean %s cache."
}
},
"controls": {

View File

@ -21,6 +21,14 @@ internal enum L10n {
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 {
/// Please try again.
internal static let pleaseTryAgain = L10n.tr("Localizable", "Common.Alerts.Common.PleaseTryAgain")

View File

@ -1,5 +1,7 @@
"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.CleanCache.Message" = "Successfully clean %@ cache.";
"Common.Alerts.CleanCache.Title" = "Clean Cache";
"Common.Alerts.Common.PleaseTryAgain" = "Please try again.";
"Common.Alerts.Common.PleaseTryAgainLater" = "Please try again later.";
"Common.Alerts.DeletePost.Delete" = "Delete";

View File

@ -1,5 +1,7 @@
"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.CleanCache.Message" = "Successfully clean %@ cache.";
"Common.Alerts.CleanCache.Title" = "Clean Cache";
"Common.Alerts.Common.PleaseTryAgain" = "Please try again.";
"Common.Alerts.Common.PleaseTryAgainLater" = "Please try again later.";
"Common.Alerts.DeletePost.Delete" = "Delete";

View File

@ -12,8 +12,7 @@ import ActiveLabel
import CoreData
import CoreDataStack
import MastodonSDK
import AlamofireImage
import Kingfisher
class SettingsViewController: UIViewController, NeedsDependency {
@ -319,36 +318,44 @@ extension SettingsViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let dataSource = viewModel.dataSource else { return }
let item = dataSource.itemIdentifier(for: indexPath)
guard let item = dataSource.itemIdentifier(for: indexPath) else { return }
switch item {
case .boringZone:
guard let url = viewModel.privacyURL else { break }
coordinator.present(
scene: .safari(url: url),
from: self,
transition: .safariPresent(animated: true, completion: nil)
)
case .spicyZone(let link):
// clear media cache
if link.title == L10n.Scene.Settings.Section.Spicyzone.clear {
// clean image cache for AlamofireImage
let diskBytes = ImageDownloader.defaultURLCache().currentDiskUsage
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: diskBytes %d", ((#file as NSString).lastPathComponent), #line, #function, diskBytes)
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: clean image cache", ((#file as NSString).lastPathComponent), #line, #function)
ImageDownloader.defaultURLCache().removeAllCachedResponses()
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)
// clean Kingfisher Cache
KingfisherManager.shared.cache.clearDiskCache()
}
// logout
if link.title == L10n.Scene.Settings.Section.Spicyzone.signout {
case .apperance:
// do nothing
break
case .notification:
// do nothing
break
case .boringZone(let link), .spicyZone(let link):
switch link {
case .termsOfService, .privacyPolicy:
// same URL
guard let url = viewModel.privacyURL else { break }
coordinator.present(
scene: .safari(url: url),
from: self,
transition: .safariPresent(animated: true, completion: nil)
)
case .clearMediaCache:
context.purgeCache()
.receive(on: RunLoop.main)
.sink { [weak self] byteCount in
guard let self = self else { return }
let byteCountformatted = AppContext.byteCountFormatter.string(fromByteCount: Int64(byteCount))
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()
}
default:
break
}
}
}

View File

@ -10,6 +10,8 @@ import UIKit
import Combine
import CoreData
import CoreDataStack
import AlamofireImage
import Kingfisher
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)
// }
}