async loading of the remote emoji database

- load the local one first
 - async task to load the remote db and if it succeeds overwrite the local db and load the remote one

emoiji.localimageurl is now a computed propery
 - need to fix the fact that the file extension is not known 😭

fetchRemoteDB automatically stores the db lcoally after fetching
add storeDB(data: Data) to store the db from a data form
Emoji.uiID is generated automatically
fix hoarder.localEmojiDB path

imessage extension scheme??
This commit is contained in:
neon443
2025-10-29 16:26:57 +00:00
parent 20e4e8a2c7
commit 002fc21308
4 changed files with 31 additions and 21 deletions

View File

@@ -74,6 +74,7 @@
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
<RemoteRunnable
runnableDebuggingMode = "1"

View File

@@ -11,9 +11,11 @@ import UniformTypeIdentifiers
struct Emoji: Codable, Identifiable, Hashable {
var id: UUID
var uiID: UUID
var uiID: UUID = UUID()
var name: String
var localImageURL: URL
var localImageURL: URL {
return EmojiHoarder.container.appendingPathComponent(id.uuidString, conformingTo: .image)
}
var remoteImageURL: URL
var isLocal: Bool {
@@ -32,16 +34,13 @@ struct Emoji: Codable, Identifiable, Hashable {
enum CodingKeys: String, CodingKey {
case id = "id"
case name = "name"
case localImageURL = "localImageURL"
case remoteImageURL = "remoteImageURL"
case remoteImageURL = "imageUrl"
}
init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(UUID.self, forKey: .id)
self.uiID = UUID()
self.name = try container.decode(String.self, forKey: .name)
self.localImageURL = try container.decode(URL.self, forKey: .localImageURL)
self.remoteImageURL = try container.decode(URL.self, forKey: .remoteImageURL)
}
@@ -50,12 +49,11 @@ struct Emoji: Codable, Identifiable, Hashable {
id: UUID = UUID()
) {
self.id = id
self.uiID = id
self.name = apiEmoji.name
self.remoteImageURL = apiEmoji.url
let fileExtension = String(apiEmoji.urlString.split(separator: ".").last ?? "png")
self.localImageURL = EmojiHoarder.container.appendingPathComponent(id.uuidString+"."+fileExtension, conformingTo: .image)
// self.localImageURL = EmojiHoarder.container.appendingPathComponent(id.uuidString+"."+fileExtension, conformingTo: .image)
// Task { [weak self] in
// let (data, response) = try await URLSession.shared.data(from: apiEmoji.url)

View File

@@ -12,7 +12,7 @@ import UniformTypeIdentifiers
class EmojiHoarder: ObservableObject {
static let container = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.neon443.StickerSlack")!.appendingPathComponent("Library", conformingTo: .directory)
static let localEmojiDB: URL = EmojiHoarder.container.appendingPathExtension("localEmojiDB.json")
static let localEmojiDB: URL = EmojiHoarder.container.appendingPathComponent("_localEmojiDB.json", conformingTo: .fileURL)
private let endpoint: URL = URL(string: "https://cachet.dunkirk.sh/emojis")!
private let encoder = JSONEncoder()
private let decoder = JSONDecoder()
@@ -22,13 +22,15 @@ class EmojiHoarder: ObservableObject {
@Published var prefix: Int = 100
init() {
guard let fetched = fetchRemoteDB() else {
self.emojis = loadLocalDB()
return
}
self.emojis = loadLocalDB()
self.filteredEmojis = self.emojis
self.emojis = fetched
self.filteredEmojis = fetched
Task(priority: .high) {
if let fetched = await self.fetchRemoteDB() {
self.emojis = fetched
self.filteredEmojis = fetched
}
}
}
func storeStickers(_ toStore: [UUID]) {
@@ -41,6 +43,10 @@ class EmojiHoarder: ObservableObject {
try! encoder.encode(emojis).write(to: EmojiHoarder.localEmojiDB)
}
func storeDB(data: Data) {
try! data.write(to: EmojiHoarder.localEmojiDB)
}
func loadLocalDB() -> [Emoji] {
if let localEmojiDB = try? Data(contentsOf: EmojiHoarder.localEmojiDB) {
let decoded = try! decoder.decode([Emoji].self, from: localEmojiDB)
@@ -49,11 +55,17 @@ class EmojiHoarder: ObservableObject {
return []
}
func fetchRemoteDB() -> [Emoji]? {
guard let data = try? Data(contentsOf: endpoint) else { return nil }
decoder.dateDecodingStrategy = .iso8601
let decoded: [SlackResponse] = try! decoder.decode([SlackResponse].self, from: data)
return SlackResponse.toEmojis(from: decoded)
func fetchRemoteDB() async -> [Emoji]? {
do {
let (data, _) = try await URLSession.shared.data(from: endpoint)
decoder.dateDecodingStrategy = .iso8601
let decoded: [SlackResponse] = try! decoder.decode([SlackResponse].self, from: data)
storeDB(data: data)
return SlackResponse.toEmojis(from: decoded)
} catch {
print(error.localizedDescription)
fatalError()
}
}
func setPrefix(to: Int) {

View File

@@ -35,7 +35,6 @@ struct EmojiPreview: View {
.resizable().scaledToFit()
} else if phase.error != nil {
ZStack {
Image(systemName: "xmark.app.fill")
.resizable().scaledToFit()
.padding()