diff --git a/yobble/Resources/Localizable.xcstrings b/yobble/Resources/Localizable.xcstrings index 8c7410b..349e631 100644 --- a/yobble/Resources/Localizable.xcstrings +++ b/yobble/Resources/Localizable.xcstrings @@ -2243,9 +2243,6 @@ }, "Разблокировать" : { "comment" : "Unblock confirmation action" - }, - "Размер кэша аватаров на диске." : { - }, "Разрешить пересылку сообщений" : { "localizations" : { diff --git a/yobble/Services/KeychainService.swift b/yobble/Services/KeychainService.swift index 0e9eb14..2719436 100644 --- a/yobble/Services/KeychainService.swift +++ b/yobble/Services/KeychainService.swift @@ -157,6 +157,15 @@ class AvatarCacheService { func clearCache(forUserId userId: String) { guard let directory = cacheDirectory(for: userId) else { return } + + // Try to delete files inside first, ignoring errors + if let fileUrls = try? fileManager.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil, options: []) { + for fileUrl in fileUrls { + try? fileManager.removeItem(at: fileUrl) + } + } + + // Then try to delete the directory itself try? fileManager.removeItem(at: directory) } diff --git a/yobble/Views/Tab/Settings/DataSettingsView.swift b/yobble/Views/Tab/Settings/DataSettingsView.swift new file mode 100644 index 0000000..5b0b0a3 --- /dev/null +++ b/yobble/Views/Tab/Settings/DataSettingsView.swift @@ -0,0 +1,141 @@ +// +// DataSettingsView.swift +// yobble +// +// Created by cheykrym on 10.12.2025. +// +import SwiftUI + + +struct DataSettingsView: View { + let currentUserId: String + private let cacheService = AvatarCacheService.shared + + @State private var cachedUsers: [CachedUserInfo] = [] + @State private var totalCacheSize: Int64 = 0 + @State private var showClearAllConfirmation = false + @State private var showClearOthersConfirmation = false + @State private var showClearCurrentConfirmation = false + + var body: some View { + Form { + Section(header: Text("Общая информация")) { + HStack { + Text("Общий размер") + Spacer() + Text(format(bytes: totalCacheSize)) + .foregroundColor(.secondary) + } + } + + Section(header: Text("Массовая отчистка")) { + Button("Очистить кэш текущего пользователя", role: .destructive) { + showClearCurrentConfirmation = true + } + .confirmationDialog( + "Вы уверены, что хотите очистить кэш для текущего пользователя?", + isPresented: $showClearCurrentConfirmation, + titleVisibility: .visible + ) { + Button("Очистить", role: .destructive) { + clearCache(for: currentUserId) + } + } + + Button("Очистить кэш (кроме текущего)", role: .destructive) { + showClearOthersConfirmation = true + } + .confirmationDialog( + "Вы уверены, что хотите очистить кэш для всех, кроме текущего пользователя?", + isPresented: $showClearOthersConfirmation, + titleVisibility: .visible + ) { + Button("Очистить", role: .destructive, action: clearOtherUsersCache) + } + + Button("Очистить весь кэш", role: .destructive) { + showClearAllConfirmation = true + } + .confirmationDialog( + "Вы уверены, что хотите очистить весь кэш аватаров? Это действие необратимо.", + isPresented: $showClearAllConfirmation, + titleVisibility: .visible + ) { + Button("Очистить всё", role: .destructive, action: clearAllCache) + } + } + + Section(header: Text("Кэш по пользователям")) { + if cachedUsers.isEmpty { + Text("Кэш пуст") + .foregroundColor(.secondary) + } else { + ForEach(cachedUsers) { user in + HStack { + VStack(alignment: .leading) { + Text(user.id) + .font(.system(.body, design: .monospaced)) + .lineLimit(1) + .truncationMode(.middle) + if user.id == currentUserId { + Text("Текущий") + .font(.caption) + .foregroundColor(.accentColor) + } + } + Spacer() + Text(format(bytes: user.size)) + .foregroundColor(.secondary) + Button("Очистить") { + clearCache(for: user.id) + } + .buttonStyle(.borderless) + } + } + } + } + } + .navigationTitle("Данные и кэш") + .onAppear(perform: refreshCacheStats) + } + + private func refreshCacheStats() { + let userIds = cacheService.getAllCachedUserIds() + self.cachedUsers = userIds.map { id in + let size = cacheService.sizeOfCache(forUserId: id) + return CachedUserInfo(id: id, size: size) + }.sorted { $0.size > $1.size } + + self.totalCacheSize = cacheService.sizeOfAllCache() + } + + private func clearCache(for userId: String) { + cacheService.clearCache(forUserId: userId) + refreshCacheStats() + } + + private func clearAllCache() { + cacheService.clearAllCache() + refreshCacheStats() + } + + private func clearOtherUsersCache() { + let otherUsers = cachedUsers.filter { $0.id != currentUserId } + for user in otherUsers { + cacheService.clearCache(forUserId: user.id) + } + refreshCacheStats() + } + + private func format(bytes: Int64) -> String { + let formatter = ByteCountFormatter() + formatter.allowedUnits = [.useAll] + formatter.countStyle = .file + return formatter.string(fromByteCount: bytes) + } +} + +struct CachedUserInfo: Identifiable { + let id: String + let size: Int64 +} diff --git a/yobble/Views/Tab/Settings/SettingsView.swift b/yobble/Views/Tab/Settings/SettingsView.swift index 1a59598..670e902 100644 --- a/yobble/Views/Tab/Settings/SettingsView.swift +++ b/yobble/Views/Tab/Settings/SettingsView.swift @@ -172,136 +172,3 @@ struct SettingsView: View { } } - -struct DataSettingsView: View { - let currentUserId: String - private let cacheService = AvatarCacheService.shared - - @State private var cachedUsers: [CachedUserInfo] = [] - @State private var totalCacheSize: Int64 = 0 - @State private var showClearAllConfirmation = false - @State private var showClearOthersConfirmation = false - @State private var showClearCurrentConfirmation = false - - var body: some View { - Form { - Section(header: Text("Общая информация"), footer: Text("Размер кэша аватаров на диске.")) { - HStack { - Text("Общий размер") - Spacer() - Text(format(bytes: totalCacheSize)) - .foregroundColor(.secondary) - } - } - - Section(header: Text("Массовая отчистка")) { - Button("Очистить кэш текущего пользователя", role: .destructive) { - showClearCurrentConfirmation = true - } - .confirmationDialog( - "Вы уверены, что хотите очистить кэш для текущего пользователя?", - isPresented: $showClearCurrentConfirmation, - titleVisibility: .visible - ) { - Button("Очистить", role: .destructive) { - clearCache(for: currentUserId) - } - } - - Button("Очистить кэш (кроме текущего)", role: .destructive) { - showClearOthersConfirmation = true - } - .confirmationDialog( - "Вы уверены, что хотите очистить кэш для всех, кроме текущего пользователя?", - isPresented: $showClearOthersConfirmation, - titleVisibility: .visible - ) { - Button("Очистить", role: .destructive, action: clearOtherUsersCache) - } - - Button("Очистить весь кэш", role: .destructive) { - showClearAllConfirmation = true - } - .confirmationDialog( - "Вы уверены, что хотите очистить весь кэш аватаров? Это действие необратимо.", - isPresented: $showClearAllConfirmation, - titleVisibility: .visible - ) { - Button("Очистить всё", role: .destructive, action: clearAllCache) - } - } - - Section(header: Text("Кэш по пользователям")) { - if cachedUsers.isEmpty { - Text("Кэш пуст") - .foregroundColor(.secondary) - } else { - ForEach(cachedUsers) { user in - HStack { - VStack(alignment: .leading) { - Text(user.id) - .font(.system(.body, design: .monospaced)) - .lineLimit(1) - .truncationMode(.middle) - if user.id == currentUserId { - Text("Текущий") - .font(.caption) - .foregroundColor(.accentColor) - } - } - Spacer() - Text(format(bytes: user.size)) - .foregroundColor(.secondary) - Button("Очистить") { - clearCache(for: user.id) - } - .buttonStyle(.borderless) - } - } - } - } - } - .navigationTitle("Данные и кэш") - .onAppear(perform: refreshCacheStats) - } - - private func refreshCacheStats() { - let userIds = cacheService.getAllCachedUserIds() - self.cachedUsers = userIds.map { id in - let size = cacheService.sizeOfCache(forUserId: id) - return CachedUserInfo(id: id, size: size) - }.sorted { $0.size > $1.size } - - self.totalCacheSize = cacheService.sizeOfAllCache() - } - - private func clearCache(for userId: String) { - cacheService.clearCache(forUserId: userId) - refreshCacheStats() - } - - private func clearAllCache() { - cacheService.clearAllCache() - refreshCacheStats() - } - - private func clearOtherUsersCache() { - let otherUsers = cachedUsers.filter { $0.id != currentUserId } - for user in otherUsers { - cacheService.clearCache(forUserId: user.id) - } - refreshCacheStats() - } - - private func format(bytes: Int64) -> String { - let formatter = ByteCountFormatter() - formatter.allowedUnits = [.useAll] - formatter.countStyle = .file - return formatter.string(fromByteCount: bytes) - } -} - -struct CachedUserInfo: Identifiable { - let id: String - let size: Int64 -}