add cache photo
This commit is contained in:
parent
ff2061f86e
commit
aa157031a1
@ -1,5 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import SwiftUICore
|
||||||
import Security
|
import Security
|
||||||
|
import UIKit
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
|
||||||
//let username = "user1"
|
//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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1035,28 +1035,13 @@ private struct ChatRowView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: 12) {
|
HStack(spacing: 12) {
|
||||||
if #available(iOS 15.0, *) {
|
if let url = avatarUrl, let fileId = chat.chatData?.avatars?.current?.fileId, let loggedInUserId = currentUserId {
|
||||||
if let url = avatarUrl {
|
CachedAvatarView(url: url, fileId: fileId, userId: loggedInUserId) {
|
||||||
AsyncImage(url: url) { phase in
|
|
||||||
switch phase {
|
|
||||||
case .empty:
|
|
||||||
placeholderAvatar
|
placeholderAvatar
|
||||||
case .success(let image):
|
}
|
||||||
image
|
|
||||||
.resizable()
|
|
||||||
.aspectRatio(contentMode: .fill)
|
.aspectRatio(contentMode: .fill)
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
.frame(width: avatarSize, height: avatarSize)
|
||||||
.clipShape(Circle())
|
.clipShape(Circle())
|
||||||
case .failure:
|
|
||||||
placeholderAvatar
|
|
||||||
@unknown default:
|
|
||||||
placeholderAvatar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
} else {
|
|
||||||
placeholderAvatar
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
placeholderAvatar
|
placeholderAvatar
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user