diff --git a/StickerSlack.xcodeproj/project.pbxproj b/StickerSlack.xcodeproj/project.pbxproj index 18bb288..11f9bc1 100644 --- a/StickerSlack.xcodeproj/project.pbxproj +++ b/StickerSlack.xcodeproj/project.pbxproj @@ -14,9 +14,9 @@ A9104C7C2EB3AE6300D160EA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A949B1EF2EA04E8200215164 /* Assets.xcassets */; }; A9104C7F2EB4022500D160EA /* MSSticker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9104C7D2EB4022500D160EA /* MSSticker.swift */; }; A9104C802EB4022500D160EA /* MSSticker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9104C7D2EB4022500D160EA /* MSSticker.swift */; }; - A9112EAC2EAFFDB0006739E2 /* Haptics in Frameworks */ = {isa = PBXBuildFile; productRef = A9112EAB2EAFFDB0006739E2 /* Haptics */; }; A924C3732EA9127200F20781 /* Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = A924C3712EA9127200F20781 /* Emoji.swift */; }; A924C3782EA9225800F20781 /* Haptics in Frameworks */ = {isa = PBXBuildFile; productRef = A924C3772EA9225800F20781 /* Haptics */; }; + A931D4082EBC9646007BC75B /* Haptics in Frameworks */ = {isa = PBXBuildFile; productRef = A931D4072EBC9646007BC75B /* Haptics */; }; A935437B2EB2A3C800BB80A4 /* FilterCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A935437A2EB2A3C800BB80A4 /* FilterCategory.swift */; }; A949B1F32EA04E8200215164 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A949B1EF2EA04E8200215164 /* Assets.xcassets */; }; A949B1F42EA04E8200215164 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949B1F02EA04E8200215164 /* ContentView.swift */; }; @@ -126,7 +126,6 @@ buildActionMask = 2147483647; files = ( A924C3782EA9225800F20781 /* Haptics in Frameworks */, - A9112EAC2EAFFDB0006739E2 /* Haptics in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -135,6 +134,7 @@ buildActionMask = 2147483647; files = ( A986A6AE2EB658DF00B6E0FA /* Messages.framework in Frameworks */, + A931D4082EBC9646007BC75B /* Haptics in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -337,6 +337,7 @@ ); name = StickerSlackiMessageApp; packageProductDependencies = ( + A931D4072EBC9646007BC75B /* Haptics */, ); productName = StickerSlackiMessageApp; productReference = A986A6AD2EB658DF00B6E0FA /* StickerSlackiMessageApp.appex */; @@ -909,6 +910,11 @@ isa = XCSwiftPackageProductDependency; productName = Haptics; }; + A931D4072EBC9646007BC75B /* Haptics */ = { + isa = XCSwiftPackageProductDependency; + package = A9112EAA2EAFFDB0006739E2 /* XCRemoteSwiftPackageReference "Haptics" */; + productName = Haptics; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = A949B1D72EA04C0B00215164 /* Project object */; diff --git a/StickerSlack/Emoji/EmojiHoarder.swift b/StickerSlack/Emoji/EmojiHoarder.swift index e270a91..8a2a406 100644 --- a/StickerSlack/Emoji/EmojiHoarder.swift +++ b/StickerSlack/Emoji/EmojiHoarder.swift @@ -12,8 +12,9 @@ import UniformTypeIdentifiers import Haptics class EmojiHoarder: ObservableObject { + static let shared: EmojiHoarder = EmojiHoarder() static let container = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.neon443.StickerSlack")!.appendingPathComponent("Library", conformingTo: .directory) - static let localEmojiDB: URL = EmojiHoarder.container.appendingPathComponent("_localEmojiDB.json", conformingTo: .fileURL) + nonisolated 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() @@ -46,6 +47,7 @@ class EmojiHoarder: ObservableObject { } } + @MainActor func deleteAllStickers() { for i in emojis.indices { guard downloadedEmojis.contains(emojis[i].name) else { continue } diff --git a/StickerSlack/SwiftUI/ContentView.swift b/StickerSlack/SwiftUI/ContentView.swift index b231902..0363315 100644 --- a/StickerSlack/SwiftUI/ContentView.swift +++ b/StickerSlack/SwiftUI/ContentView.swift @@ -9,20 +9,55 @@ import SwiftUI import Haptics struct ContentView: View { - @StateObject var hoarder: EmojiHoarder = EmojiHoarder() + @StateObject var hoarder: EmojiHoarder = .shared @State var searchTerm: String = "" - var col: GridItem = GridItem(.fixed(100), spacing: 0, alignment: .center) + var minColWidth: CGFloat { 75 } + var spacing: CGFloat { 10 } + var col: GridItem { + GridItem( + .flexible(minimum: minColWidth, maximum: 125), + spacing: spacing, + alignment: .center + ) + } + + @Environment(\.colorScheme) var colorScheme + var isDark: Bool { colorScheme == .dark } var body: some View { TabView { - LazyHGrid(rows: Array(repeating: col, count: 4), spacing: 10) { - ForEach(hoarder.downloadedEmojis.sorted(by: <), id: \.self) { name in - if let emoji = hoarder.trie.dict[name] { - EmojiPreview(hoarder: hoarder, emoji: emoji) + ScrollView { + let columns: Int = max(1, Int((UIScreen.main.bounds.width - 2*spacing) / (minColWidth + spacing))) + let layout = Array(repeating: col, count: columns) + LazyVGrid(columns: layout, spacing: 10) { + ForEach(hoarder.emojis, id: \.self) { emoji in + if hoarder.downloadedEmojis.contains(emoji.name) { + ZStack { + Rectangle() + .foregroundStyle(isDark ? .black : .white) + EmojiPreview(emoji: emoji) + RoundedRectangle(cornerRadius: 15) + .stroke(.gray, lineWidth: 1) + } .aspectRatio(1, contentMode: .fit) + .clipShape(RoundedRectangle(cornerRadius: 15)) + .contextMenu { + Text(emoji.name) + Button("Share", systemImage: "square.and.arrow.up") { + + } + ShareLink("Share", item: emoji.localImageURL, subject: nil, message: nil) + ShareLink("Share", item: emoji.remoteImageURL, subject: nil, message: nil) + Divider() + Button("Delete", systemImage: "trash.fill", role: .destructive) { + hoarder.delete(emoji: emoji) + } + } + } } } + .padding(.horizontal, spacing) } .tabItem { Label("Downloaded", systemImage: "arrow.down.circle.fill") @@ -30,7 +65,7 @@ struct ContentView: View { List { ForEach(hoarder.emojis, id: \.self) { emoji in - EmojiRow(hoarder: hoarder, emoji: emoji) + EmojiRow(emoji: emoji) } } .tabItem { @@ -42,7 +77,7 @@ struct ContentView: View { ForEach(hoarder.filteredEmojis, id: \.self) { name in if let emoji = hoarder.trie.dict[name] { - EmojiRow(hoarder: hoarder, emoji: emoji) + EmojiRow(emoji: emoji) } } } @@ -59,12 +94,10 @@ struct ContentView: View { Label("Search", systemImage: "magnifyingglass") } - TrieTestingView( - hoarder: hoarder, - ) - .tabItem { - Label("Tree", systemImage: "tree.fill") - } + TrieTestingView() + .tabItem { + Label("Tree", systemImage: "tree.fill") + } } .searchable(text: $searchTerm, placement: .automatic) } diff --git a/StickerSlack/SwiftUI/Emoji/EmojiPreview.swift b/StickerSlack/SwiftUI/Emoji/EmojiPreview.swift index e3fb623..faf0284 100644 --- a/StickerSlack/SwiftUI/Emoji/EmojiPreview.swift +++ b/StickerSlack/SwiftUI/Emoji/EmojiPreview.swift @@ -9,7 +9,7 @@ import SwiftUI import Haptics struct EmojiPreview: View { - @ObservedObject var hoarder: EmojiHoarder + @ObservedObject var hoarder: EmojiHoarder = .shared @State var emoji: Emoji @State private var id: UUID = UUID() @@ -20,13 +20,6 @@ struct EmojiPreview: View { if let image = emoji.image { Image(uiImage: image) .resizable().scaledToFit() - .border(.orange) - .overlay(alignment: .bottomLeading) { - Image(systemName: "arrow.down.circle.fill") - .foregroundStyle(.gray) - .shadow(radius: 1) - .symbolRenderingMode(.hierarchical) - } } else { AsyncImage(url: emoji.remoteImageURL) { phase in if let image = phase.image { @@ -54,13 +47,6 @@ struct EmojiPreview: View { } } -#Preview { - EmojiPreview( - hoarder: EmojiHoarder(localOnly: true), - emoji: Emoji.test - ) -} - struct ImageErrorView: View { var body: some View { Image(systemName: "xmark.app.fill") @@ -70,3 +56,10 @@ struct ImageErrorView: View { .foregroundStyle(.red) } } + +#Preview { + EmojiPreview( + hoarder: EmojiHoarder(localOnly: true), + emoji: Emoji.test + ) +} diff --git a/StickerSlack/SwiftUI/Emoji/EmojiRow.swift b/StickerSlack/SwiftUI/Emoji/EmojiRow.swift index 7080d1e..eece957 100644 --- a/StickerSlack/SwiftUI/Emoji/EmojiRow.swift +++ b/StickerSlack/SwiftUI/Emoji/EmojiRow.swift @@ -9,7 +9,7 @@ import SwiftUI import Haptics struct EmojiRow: View { - @ObservedObject var hoarder: EmojiHoarder + @ObservedObject var hoarder: EmojiHoarder = .shared @State var emoji: Emoji var body: some View { @@ -19,10 +19,7 @@ struct EmojiRow: View { // Text Text(emoji.name) } - EmojiPreview( - hoarder: hoarder, - emoji: emoji - ) + EmojiPreview(emoji: emoji) } .frame(maxWidth: 100, maxHeight: 100) Spacer() diff --git a/StickerSlack/Trie/TrieTestingView.swift b/StickerSlack/Trie/TrieTestingView.swift index 691c5ff..c11f917 100644 --- a/StickerSlack/Trie/TrieTestingView.swift +++ b/StickerSlack/Trie/TrieTestingView.swift @@ -64,11 +64,11 @@ struct TrieTestingView: View { } if uikit { - EmojiCollectionView(hoarder: hoarder, items: filterResult) + EmojiCollectionView(items: filterResult) .id(filterResult) } else { List(filterResult, id: \.self) { item in - EmojiRow(hoarder: hoarder, emoji: hoarder.trie.dict[item]!) + EmojiRow(emoji: hoarder.trie.dict[item]!) } } diff --git a/StickerSlack/UIKit/EmojiCollectionView.swift b/StickerSlack/UIKit/EmojiCollectionView.swift index 103ba56..2735fd5 100644 --- a/StickerSlack/UIKit/EmojiCollectionView.swift +++ b/StickerSlack/UIKit/EmojiCollectionView.swift @@ -11,7 +11,7 @@ import SwiftUI import Haptics struct EmojiCollectionView: UIViewRepresentable { - let hoarder: EmojiHoarder + let hoarder: EmojiHoarder = .shared let items: [String] func makeUIView(context: Context) -> UITableView { @@ -28,15 +28,14 @@ struct EmojiCollectionView: UIViewRepresentable { } func makeCoordinator() -> Coordinator { - Coordinator(hoarder: hoarder, items: items) + Coordinator(items: items) } final class Coordinator: NSObject, UITableViewDataSource { - var hoarder: EmojiHoarder + var hoarder: EmojiHoarder = .shared var items: [String] - init(hoarder: EmojiHoarder, items: [String]) { - self.hoarder = hoarder + init(items: [String]) { self.items = items } diff --git a/StickerSlackTests/StickerSlackTests.swift b/StickerSlackTests/StickerSlackTests.swift index 6293998..64fe81e 100644 --- a/StickerSlackTests/StickerSlackTests.swift +++ b/StickerSlackTests/StickerSlackTests.swift @@ -31,7 +31,7 @@ struct StickerSlackTests { } @Test func deleteAllEmojis() async throws { - let performanceTests = PerformanceTests(hoarder: hoarder) + let performanceTests = PerformanceTests(hoarder: .shared) try! await performanceTests.fakeDownloadAllStickers() await hoarder.deleteAllStickers() }