add cache photo

This commit is contained in:
cheykrym 2025-12-09 23:17:34 +03:00
parent ff2061f86e
commit aa157031a1
2 changed files with 122 additions and 20 deletions

View File

@ -1,5 +1,8 @@
import Foundation
import SwiftUICore
import Security
import UIKit
import Combine
//let username = "user1"
@ -111,3 +114,117 @@ class KeychainService {
}
}
}
class AvatarCacheService {
static let shared = AvatarCacheService()
private let fileManager = FileManager.default
private var baseCacheDirectory: URL? {
fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first?.appendingPathComponent("avatar_cache")
}
private init() {}
private func cacheDirectory(for userId: String) -> URL? {
baseCacheDirectory?.appendingPathComponent(userId)
}
private func filePath(forKey key: String, userId: String) -> URL? {
cacheDirectory(for: userId)?.appendingPathComponent(key)
}
func getImage(forKey key: String, userId: String) -> UIImage? {
guard let url = filePath(forKey: key, userId: userId),
fileManager.fileExists(atPath: url.path),
let data = try? Data(contentsOf: url) else {
return nil
}
return UIImage(data: data)
}
func saveImage(_ image: UIImage, forKey key: String, userId: String) {
guard let directory = cacheDirectory(for: userId),
let url = filePath(forKey: key, userId: userId),
let data = image.jpegData(compressionQuality: 0.8) else {
return
}
if !fileManager.fileExists(atPath: directory.path) {
try? fileManager.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil)
}
try? data.write(to: url)
}
func clearCache(forUserId userId: String) {
guard let directory = cacheDirectory(for: userId) else { return }
try? fileManager.removeItem(at: directory)
}
func clearAllCache() {
guard let directory = baseCacheDirectory else { return }
try? fileManager.removeItem(at: directory)
}
}
class ImageLoader: ObservableObject {
@Published var image: UIImage?
private let url: URL
private let fileId: String
private let userId: String
private var cancellable: AnyCancellable?
private let cache = AvatarCacheService.shared
init(url: URL, fileId: String, userId: String) {
self.url = url
self.fileId = fileId
self.userId = userId
}
deinit {
cancellable?.cancel()
}
func load() {
if let cachedImage = cache.getImage(forKey: fileId, userId: userId) {
self.image = cachedImage
return
}
cancellable = URLSession.shared.dataTaskPublisher(for: url)
.map { UIImage(data: $0.data) }
.replaceError(with: nil)
.receive(on: DispatchQueue.main)
.sink { [weak self] loadedImage in
guard let self = self, let image = loadedImage else { return }
self.image = image
self.cache.saveImage(image, forKey: self.fileId, userId: self.userId)
}
}
}
struct CachedAvatarView<Placeholder: View>: View {
@StateObject private var loader: ImageLoader
private let placeholder: Placeholder
init(url: URL, fileId: String, userId: String, @ViewBuilder placeholder: () -> Placeholder) {
self.placeholder = placeholder()
_loader = StateObject(wrappedValue: ImageLoader(url: url, fileId: fileId, userId: userId))
}
var body: some View {
content
.onAppear(perform: loader.load)
}
private var content: some View {
Group {
if let image = loader.image {
Image(uiImage: image)
.resizable()
} else {
placeholder
}
}
}
}

View File

@ -1035,28 +1035,13 @@ private struct ChatRowView: View {
var body: some View {
HStack(spacing: 12) {
if #available(iOS 15.0, *) {
if let url = avatarUrl {
AsyncImage(url: url) { phase in
switch phase {
case .empty:
if let url = avatarUrl, let fileId = chat.chatData?.avatars?.current?.fileId, let loggedInUserId = currentUserId {
CachedAvatarView(url: url, fileId: fileId, userId: loggedInUserId) {
placeholderAvatar
case .success(let image):
image
.resizable()
}
.aspectRatio(contentMode: .fill)
.frame(width: avatarSize, height: avatarSize)
.clipShape(Circle())
case .failure:
placeholderAvatar
@unknown default:
placeholderAvatar
}
}
.frame(width: avatarSize, height: avatarSize)
} else {
placeholderAvatar
}
} else {
placeholderAvatar
}