diff --git a/StickerSlack.xcodeproj/project.pbxproj b/StickerSlack.xcodeproj/project.pbxproj index 2702097..24b3445 100644 --- a/StickerSlack.xcodeproj/project.pbxproj +++ b/StickerSlack.xcodeproj/project.pbxproj @@ -46,6 +46,7 @@ A9C172DD2EB8C9AC008A7885 /* Trie.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C172DB2EB8C9AC008A7885 /* Trie.swift */; }; A9C172DE2EB8C9AC008A7885 /* Trie.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C172DB2EB8C9AC008A7885 /* Trie.swift */; }; A9D15B8B2EB1142C00404792 /* EmojiPack.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D15B892EB1142C00404792 /* EmojiPack.swift */; }; + A9EB72392EB93FDB00658CEB /* EmojiCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EB72382EB93FDB00658CEB /* EmojiCollectionView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -108,6 +109,7 @@ A9C172DB2EB8C9AC008A7885 /* Trie.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Trie.swift; sourceTree = ""; }; A9D15B892EB1142C00404792 /* EmojiPack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPack.swift; sourceTree = ""; }; A9E2ECD72EB74CE00038B2D6 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; + A9EB72382EB93FDB00658CEB /* EmojiCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCollectionView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -210,6 +212,7 @@ A9D15B892EB1142C00404792 /* EmojiPack.swift */, A935437A2EB2A3C800BB80A4 /* FilterCategory.swift */, A9C172DB2EB8C9AC008A7885 /* Trie.swift */, + A9EB72382EB93FDB00658CEB /* EmojiCollectionView.swift */, ); path = Emoji; sourceTree = ""; @@ -395,6 +398,7 @@ A949B1F42EA04E8200215164 /* ContentView.swift in Sources */, A949B1F52EA04E8200215164 /* StickerSlackApp.swift in Sources */, A9104C802EB4022500D160EA /* MSSticker.swift in Sources */, + A9EB72392EB93FDB00658CEB /* EmojiCollectionView.swift in Sources */, A949B1FB2EA0518800215164 /* SlackResponse.swift in Sources */, A935437B2EB2A3C800BB80A4 /* FilterCategory.swift in Sources */, A9C172DD2EB8C9AC008A7885 /* Trie.swift in Sources */, @@ -606,17 +610,18 @@ "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15; + IPHONEOS_DEPLOYMENT_TARGET = 16; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 12; + MACOSX_DEPLOYMENT_TARGET = 13; MARKETING_VERSION = "$(VERSION)"; PRODUCT_BUNDLE_IDENTIFIER = com.neon443.StickerSlack; PRODUCT_NAME = "$(TARGET_NAME)"; REGISTER_APP_GROUPS = YES; SDKROOT = auto; STRING_CATALOG_GENERATE_SYMBOLS = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_EMIT_LOC_STRINGS = YES; @@ -652,17 +657,18 @@ "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15; + IPHONEOS_DEPLOYMENT_TARGET = 16; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 12; + MACOSX_DEPLOYMENT_TARGET = 13; MARKETING_VERSION = "$(VERSION)"; PRODUCT_BUNDLE_IDENTIFIER = com.neon443.StickerSlack; PRODUCT_NAME = "$(TARGET_NAME)"; REGISTER_APP_GROUPS = YES; SDKROOT = auto; STRING_CATALOG_GENERATE_SYMBOLS = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_EMIT_LOC_STRINGS = YES; @@ -685,7 +691,7 @@ INFOPLIST_FILE = StickerSlackiMessageApp/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = StickerSlack; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15; + IPHONEOS_DEPLOYMENT_TARGET = 16; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -717,7 +723,7 @@ INFOPLIST_FILE = StickerSlackiMessageApp/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = StickerSlack; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 15; + IPHONEOS_DEPLOYMENT_TARGET = 16; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/StickerSlack/Emoji/EmojiCollectionView.swift b/StickerSlack/Emoji/EmojiCollectionView.swift new file mode 100644 index 0000000..a11671a --- /dev/null +++ b/StickerSlack/Emoji/EmojiCollectionView.swift @@ -0,0 +1,83 @@ +// +// EmojiCollectionView.swift +// StickerSlack +// +// Created by neon443 on 03/11/2025. +// + +import Foundation +import UIKit +import SwiftUI +import Haptics + +struct EmojiCollectionView: UIViewRepresentable { + let hoarder: EmojiHoarder + + func makeUIView(context: Context) -> UITableView { + let tableView = UITableView() + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + tableView.dataSource = context.coordinator + return tableView + } + + func updateUIView(_ uiView: UITableView, context: Context) { + uiView.reloadData() + } + + func makeCoordinator() -> Coordinator { + Coordinator(hoarder: hoarder) + } + + final class Coordinator: NSObject, UITableViewDataSource { + var hoarder: EmojiHoarder + + init(hoarder: EmojiHoarder) { + self.hoarder = hoarder + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return hoarder.searchTerm.isEmpty ? hoarder.emojis.count : hoarder.filteredEmojis.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + var emojiName: String + if hoarder.searchTerm.isEmpty { + emojiName = hoarder.emojis[indexPath.row].name + } else { + emojiName = hoarder.filteredEmojis[indexPath.row] + } + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + + cell.contentConfiguration = UIHostingConfiguration { + HStack { + EmojiPreview( + hoarder: hoarder, + emoji: hoarder.trie.dict[emojiName]! + ) + .frame(maxWidth: 100, maxHeight: 100) + Spacer() + if hoarder.trie.dict[emojiName]!.isLocal { + Button("", systemImage: "trash") { + self.hoarder.trie.dict[emojiName]!.deleteImage() + self.hoarder.trie.dict[emojiName]!.refresh() + Haptic.heavy.trigger() + } + .buttonStyle(.plain) + } else { + Button("", systemImage: "arrow.down.circle") { + Task.detached { + try? await self.hoarder.trie.dict[emojiName]!.downloadImage() + await MainActor.run { + self.hoarder.trie.dict[emojiName]!.refresh() + Haptic.success.trigger() + } + } + } + .buttonStyle(.plain) + } + } + } + return cell + } + } +} diff --git a/StickerSlack/EmojiHoarder.swift b/StickerSlack/EmojiHoarder.swift index 64e387f..2e92859 100644 --- a/StickerSlack/EmojiHoarder.swift +++ b/StickerSlack/EmojiHoarder.swift @@ -21,6 +21,7 @@ class EmojiHoarder: ObservableObject { @Published var trie: Trie = Trie() @Published var filteredEmojis: [String] = [] + @Published var searchTerm: String = "" init(localOnly: Bool = false) { let localDB = loadLocalDB() diff --git a/StickerSlack/Views/ContentView.swift b/StickerSlack/Views/ContentView.swift index 2df1cfb..bcd8710 100644 --- a/StickerSlack/Views/ContentView.swift +++ b/StickerSlack/Views/ContentView.swift @@ -11,77 +11,33 @@ import Haptics struct ContentView: View { @StateObject var hoarder: EmojiHoarder = EmojiHoarder() - @State var searchTerm: String = "" - var body: some View { - NavigationView { - List { +// NavigationView { +// List { + TextField("", text: $hoarder.searchTerm) NavigationLink("trieTester") { TrieTestingView( hoarder: hoarder, ) } - Text("\(searchTerm.isEmpty ? hoarder.emojis.count : hoarder.filteredEmojis.count) Emoji") + Text("\(hoarder.searchTerm.isEmpty ? hoarder.emojis.count : hoarder.filteredEmojis.count) Emoji") - if searchTerm.isEmpty { - ForEach($hoarder.emojis, id: \.self) { $emoji in - HStack { - EmojiPreview( - hoarder: hoarder, - emoji: emoji - ) - .frame(maxWidth: 100, maxHeight: 100) - Spacer() - if emoji.isLocal { - Button("", systemImage: "trash") { - emoji.deleteImage() - emoji.refresh() - Haptic.heavy.trigger() - } - .buttonStyle(.plain) - } else { - Button("", systemImage: "arrow.down.circle") { - Task.detached { - try? await emoji.downloadImage() - await MainActor.run { - emoji.refresh() - Haptic.success.trigger() - } - } - } - .buttonStyle(.plain) - } - } - } - } else { - ForEach(hoarder.filteredEmojis, id: \.self) { name in - if let emoji = hoarder.trie.dict[name] { - EmojiPreview(hoarder: hoarder, emoji: emoji) - .onTapGesture { - Task.detached { - try? await hoarder.trie.dict[name]!.downloadImage() - await MainActor.run { - hoarder.trie.dict[name]!.refresh() - } - } - } - } - } - } - } + EmojiCollectionView(hoarder: hoarder) + + .navigationTitle("StickerSlack") - .onChange(of: searchTerm) { _ in - hoarder.filterEmojis(by: searchTerm) + .onChange(of: hoarder.searchTerm) { _ in + hoarder.filterEmojis(by: hoarder.searchTerm) } .refreshable { Task.detached { await hoarder.refreshDB() } - searchTerm = "" + hoarder.searchTerm = "" } - } - .searchable(text: $searchTerm, placement: .automatic) +// } + .searchable(text: $hoarder.searchTerm, placement: .automatic) } }