diff --git a/StickerSlack.xcodeproj/project.pbxproj b/StickerSlack.xcodeproj/project.pbxproj index e80737f..d583bdc 100644 --- a/StickerSlack.xcodeproj/project.pbxproj +++ b/StickerSlack.xcodeproj/project.pbxproj @@ -65,7 +65,6 @@ A9BBC51A2EB8FA4500FFE82F /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BBC5172EB8FA4500FFE82F /* ViewModifiers.swift */; }; A9C172DC2EB8C9AC008A7885 /* Trie.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C172DB2EB8C9AC008A7885 /* Trie.swift */; }; A9C172DD2EB8C9AC008A7885 /* Trie.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C172DB2EB8C9AC008A7885 /* Trie.swift */; }; - A9C7892D2ED2005000D954F5 /* StickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C7892C2ED2005000D954F5 /* StickerView.swift */; }; A9D15B8B2EB1142C00404792 /* EmojiPack.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D15B892EB1142C00404792 /* EmojiPack.swift */; }; A9EB72392EB93FDB00658CEB /* EmojiCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB72382EB93FDB00658CEB /* EmojiCollectionView.swift */; }; A9EB72472EB948C400658CEB /* EmojiRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB72462EB948C400658CEB /* EmojiRow.swift */; }; @@ -524,7 +523,6 @@ A9EB724B2EB94A5700658CEB /* Trie.swift in Sources */, A986A6CD2EB659E000B6E0FA /* MessagesViewController.swift in Sources */, A986A6CE2EB659E000B6E0FA /* StickerBrowserDataSource.swift in Sources */, - A9C7892D2ED2005000D954F5 /* StickerView.swift in Sources */, A957C17E2ECFAA1100EA3EE9 /* GifView.swift in Sources */, A9BBC5192EB8FA4500FFE82F /* ViewModifiers.swift in Sources */, A986A6C42EB6598500B6E0FA /* SlackResponse.swift in Sources */, diff --git a/StickerSlack/Emoji/EmojiHoarder.swift b/StickerSlack/Emoji/EmojiHoarder.swift index ae73a45..e89c2ee 100644 --- a/StickerSlack/Emoji/EmojiHoarder.swift +++ b/StickerSlack/Emoji/EmojiHoarder.swift @@ -51,9 +51,15 @@ class EmojiHoarder: ObservableObject { } } - func storeStickers(_ toStore: [UUID]) { - for stickerId in toStore { - print(stickerId) + @MainActor + func downloadAllStickers() { + for emoji in emojis { + guard !downloadedEmojis.contains(emoji.name) else { continue } + download(emoji: emoji, skipStoreIndex: true) + downloadedEmojis.insert(emoji.name) + } + DispatchQueue.main.asyncAfter(deadline: .now()+1) { + self.storeDownloadedIndexes() } } @@ -61,26 +67,34 @@ class EmojiHoarder: ObservableObject { func deleteAllStickers() { for i in emojis.indices { guard downloadedEmojis.contains(emojis[i].name) else { continue } - delete(emoji: emojis[i]) + delete(emoji: emojis[i], skipStoreIndex: true) } + storeDownloadedIndexes() } - func storeDB() { + private func storeDB() { try! encoder.encode(emojis).write(to: EmojiHoarder.localEmojiDB) } - func storeDB(data: Data) { + private func storeDB(data: Data) { try! data.write(to: EmojiHoarder.localEmojiDB) } - func resetTrie() { + func resetAllIndexes() { trie.root = TrieNode() trie.dict = [:] + try? FileManager.default.removeItem(at: EmojiHoarder.localTrieDict) + downloadedEmojis = [] + downloadedEmojisArr = [] + UserDefaults.standard.removeObject(forKey: "downloadedEmojis") + UserDefaults.standard.removeObject(forKey: "downloadedEmojisArr") + searchTerm = "" + letterStats = [] } //cl i disabled ts cos its quicker to rebuild it then to load ts - func saveTrie() { + private func saveTrie() { // return // guard let data = try? encoder.encode(trie.root) else { // fatalError("failed to encode trie") @@ -93,7 +107,7 @@ class EmojiHoarder: ObservableObject { try! dataDict.write(to: EmojiHoarder.localTrieDict) } - func loadTrie() { + private func loadTrie() { // return // guard FileManager.default.fileExists(atPath: EmojiHoarder.localTrie.path) else { return } // guard let data = try? Data(contentsOf: EmojiHoarder.localTrie) else { return } @@ -121,18 +135,18 @@ class EmojiHoarder: ObservableObject { print("done building trie in", Date().timeIntervalSince1970-start) } - func buildTrieDict() { + private func buildTrieDict() { for emoji in emojis { trie.dict[emoji.name] = emoji } buildDownloadedEmojis() } - func buildDownloadedEmojis() { + private func buildDownloadedEmojis() { downloadedEmojis = [] downloadedEmojisArr = [] - downloadedEmojisArr = (try? JSONDecoder().decode([String].self, from: UserDefaults.standard.data(forKey: "downloadedEmojisArr") ?? Data())) ?? [] - downloadedEmojis = (try? JSONDecoder().decode(Set.self, from: UserDefaults.standard.data(forKey: "downloadedEmojis") ?? Data())) ?? [] + downloadedEmojisArr = (try? decoder.decode([String].self, from: UserDefaults.standard.data(forKey: "downloadedEmojisArr") ?? Data())) ?? [] + downloadedEmojis = (try? decoder.decode(Set.self, from: UserDefaults.standard.data(forKey: "downloadedEmojis") ?? Data())) ?? [] if downloadedEmojis.isEmpty || downloadedEmojisArr.isEmpty { for emoji in emojis { @@ -141,13 +155,15 @@ class EmojiHoarder: ObservableObject { downloadedEmojisArr.append(emoji.name) } } - - UserDefaults.standard.set(try! JSONEncoder().encode(downloadedEmojis), forKey: "downloadedEmojis") - UserDefaults.standard.set(try! JSONEncoder().encode(downloadedEmojisArr), forKey: "downloadedEmojisArr") + } + + func storeDownloadedIndexes() { + UserDefaults.standard.set(try! encoder.encode(downloadedEmojis), forKey: "downloadedEmojis") + UserDefaults.standard.set(try! encoder.encode(downloadedEmojisArr), forKey: "downloadedEmojisArr") } nonisolated - func loadLocalDB() -> [Emoji] { + private func loadLocalDB() -> [Emoji] { if let localEmojiDB = try? Data(contentsOf: EmojiHoarder.localEmojiDB) { let decoded = try! decoder.decode([Emoji].self, from: localEmojiDB) return decoded @@ -155,7 +171,7 @@ class EmojiHoarder: ObservableObject { return [] } - func loadRemoteDB() async { + private func loadRemoteDB() async { async let fetched = self.fetchRemoteDB() if let fetched = await fetched { withAnimation { self.emojis = fetched } @@ -163,7 +179,7 @@ class EmojiHoarder: ObservableObject { } nonisolated - func fetchRemoteDB() async -> [Emoji]? { + private func fetchRemoteDB() async -> [Emoji]? { do { async let (data, _) = try URLSession.shared.data(from: endpoint) decoder.dateDecodingStrategy = .iso8601 @@ -176,7 +192,7 @@ class EmojiHoarder: ObservableObject { } } - func refreshDB() async { + private func refreshDB() async { guard let fetched = await self.fetchRemoteDB() else { let local = loadLocalDB() await MainActor.run { @@ -191,24 +207,26 @@ class EmojiHoarder: ObservableObject { } } - func download(emoji: Emoji) { + nonisolated func download(emoji: Emoji, skipStoreIndex: Bool = false) { Task.detached { try? await emoji.downloadImage() await MainActor.run { self.downloadedEmojis.insert(emoji.name) self.downloadedEmojisArr.append(emoji.name) self.trie.dict[emoji.name]?.refresh() + if !skipStoreIndex { self.storeDownloadedIndexes() } Haptic.success.trigger() } } } @MainActor - func delete(emoji: Emoji) { + func delete(emoji: Emoji, skipStoreIndex: Bool = false) { emoji.deleteImage() downloadedEmojis.remove(emoji.name) downloadedEmojisArr.removeAll(where: { $0 == emoji.name }) self.trie.dict[emoji.name]?.refresh() + if !skipStoreIndex { storeDownloadedIndexes() } Haptic.heavy.trigger() } @@ -263,22 +281,4 @@ class EmojiHoarder: ObservableObject { var by: EmojiHoarder.SortLetterStatsBy = .count var ascending: Bool = true } - -// func filterEmojis(byCategory category: FilterCategory, searchTerm: String) { -// guard category != .none else { -// filterEmojis(by: searchTerm) -// return -// } -// self.filterEmojis(by: searchTerm) -// DispatchQueue.main.async { -// switch category { -// case .none: -// fallthrough -// case .downloaded: -// withAnimation(.interactiveSpring) { self.filteredEmojis = self.filteredEmojis.filter { $0.isLocal } } -// case .notDownloaded: -// withAnimation(.interactiveSpring) { self.filteredEmojis = self.filteredEmojis.filter { !$0.isLocal } } -// } -// } -// } } diff --git a/StickerSlack/SwiftUI/SettingsView.swift b/StickerSlack/SwiftUI/SettingsView.swift index d96793a..82548bc 100644 --- a/StickerSlack/SwiftUI/SettingsView.swift +++ b/StickerSlack/SwiftUI/SettingsView.swift @@ -24,7 +24,7 @@ struct SettingsView: View { .frame(width: 100, height: 100) .clipShape(RoundedRectangle(cornerRadius: 24)) .foregroundStyle(.purple) - .shadow(color: isDark ? .white : .purple, radius: 5) + .shadow(color: isDark ? .white : .purple, radius: 2) .padding(.trailing, 10) VStack(alignment: .leading) { Text("StickerSlack") @@ -46,6 +46,10 @@ struct SettingsView: View { Section { Text("\(hoarder.emojis.count) total Emoji") Text("\(hoarder.downloadedEmojis.count) downloaded Emoji") + if hoarder.downloadedEmojis.count == hoarder.emojis.count { + Text("🎉") + .font(.largeTitle) + } NavigationLink { List { Picker(selection: $hoarder.letterStatsSorting.by) { @@ -55,16 +59,20 @@ struct SettingsView: View { } label: { Label("Sort by", systemImage: "arrow.up.arrow.down") } + .onChange(of: hoarder.letterStatsSorting.by) { _ in + hoarder.sortLetterStats(by: hoarder.letterStatsSorting) + } + Picker(selection: $hoarder.letterStatsSorting.ascending) { Text("Ascending").tag(true) Text("Descending").tag(false) } label: { Label("Order", systemImage: "greaterthan") } - .onChange(of: hoarder.letterStatsSorting) { _ in + .onChange(of: hoarder.letterStatsSorting.ascending) { _ in hoarder.sortLetterStats(by: hoarder.letterStatsSorting) } - + ForEach(hoarder.letterStats, id: \.self) { stat in HStack { Text("\(stat.char)") @@ -82,12 +90,28 @@ struct SettingsView: View { hoarder.setShowWelcome(to: true) } + Section("Use with Caution") { + Button("download all", role: .destructive) { + hoarder.downloadAllStickers() + } + Button("delete all", role: .destructive) { + hoarder.deleteAllStickers() + } + } + Section("Debug") { NavigationLink { TrieTestingView(hoarder: hoarder) } label: { Label("Tree", systemImage: "tree") } + + Button(role: .destructive) { + hoarder.resetAllIndexes() + hoarder.buildTrie() + } label: { + Label("Reindex", systemImage: "list.bullet.clipboard.fill") + } } // Section(content: <#T##() -> View#>, header: <#T##() -> View#>, footer: <#T##() -> View#>) } diff --git a/StickerSlack/Trie/TrieTestingView.swift b/StickerSlack/Trie/TrieTestingView.swift index 9b8439f..cab7531 100644 --- a/StickerSlack/Trie/TrieTestingView.swift +++ b/StickerSlack/Trie/TrieTestingView.swift @@ -26,7 +26,7 @@ struct TrieTestingView: View { .foregroundStyle(.blue) Button("reset", role: .destructive) { - hoarder.resetTrie() + hoarder.resetAllIndexes() } Button("add emojis!") { hoarder.buildTrie()