From 3476751a312786775baf9ec67faa6bcd5651f11e Mon Sep 17 00:00:00 2001 From: neon443 <69979447+neon443@users.noreply.github.com> Date: Fri, 21 Nov 2025 10:09:03 +0000 Subject: [PATCH] add welcome view with how to use steps add listrow for reusability added stats like total, downloaded emojis added letterstats to count how many emojis there are starting with each letter fix lag when opening settigns cos we were initing a new hoarder in trietestingview fix downloaded view using screen width instead of window width now its good on mac and ipad split screen added designed for ipad support --- StickerSlack.xcodeproj/project.pbxproj | 8 +++ StickerSlack/Emoji/EmojiHoarder.swift | 25 +++++++++ StickerSlack/SwiftUI/ContentView.swift | 64 +++++++++++++---------- StickerSlack/SwiftUI/DownloadedView.swift | 58 ++++++++++---------- StickerSlack/SwiftUI/SettingsView.swift | 24 ++++++++- StickerSlack/SwiftUI/WelcomeView.swift | 49 +++++++++++++++++ StickerSlack/Trie/TrieTestingView.swift | 4 +- 7 files changed, 172 insertions(+), 60 deletions(-) create mode 100644 StickerSlack/SwiftUI/WelcomeView.swift diff --git a/StickerSlack.xcodeproj/project.pbxproj b/StickerSlack.xcodeproj/project.pbxproj index bbaa64d..2ddd742 100644 --- a/StickerSlack.xcodeproj/project.pbxproj +++ b/StickerSlack.xcodeproj/project.pbxproj @@ -14,6 +14,8 @@ 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 */; }; + A921C2DF2ED067BB00E57B1A /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A921C2DE2ED067BB00E57B1A /* WelcomeView.swift */; }; + A921C2E02ED067BB00E57B1A /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A921C2DE2ED067BB00E57B1A /* WelcomeView.swift */; }; 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 */; }; @@ -107,6 +109,7 @@ A9104C722EB3AE4700D160EA /* Icon.pxd */ = {isa = PBXFileReference; lastKnownFileType = file; path = Icon.pxd; sourceTree = ""; }; A9104C732EB3AE4700D160EA /* StickerSlack.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = StickerSlack.icon; sourceTree = ""; }; A9104C7D2EB4022500D160EA /* MSSticker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSSticker.swift; sourceTree = ""; }; + A921C2DE2ED067BB00E57B1A /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = ""; }; A924C3712EA9127200F20781 /* Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Emoji.swift; sourceTree = ""; }; A924C3742EA9134C00F20781 /* StickerSlack.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = StickerSlack.entitlements; sourceTree = ""; }; A935437A2EB2A3C800BB80A4 /* FilterCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterCategory.swift; sourceTree = ""; }; @@ -237,6 +240,7 @@ A955B3F02EC22E9700E1732D /* BrowseView.swift */, A955B3F42EC22EE900E1732D /* SearchView.swift */, A957C1732ECCE2CE00EA3EE9 /* SettingsView.swift */, + A921C2DE2ED067BB00E57B1A /* WelcomeView.swift */, ); path = SwiftUI; sourceTree = ""; @@ -495,6 +499,7 @@ A9EB724F2EB94A6B00658CEB /* TrieTestingView.swift in Sources */, A924C3732EA9127200F20781 /* Emoji.swift in Sources */, A9D15B8B2EB1142C00404792 /* EmojiPack.swift in Sources */, + A921C2DF2ED067BB00E57B1A /* WelcomeView.swift in Sources */, A949B1F82EA04F2300215164 /* EmojiHoarder.swift in Sources */, A9BBC5182EB8FA4500FFE82F /* ViewModifiers.swift in Sources */, A9EB72492EB948C400658CEB /* EmojiRow.swift in Sources */, @@ -538,6 +543,7 @@ A955B3F62EC22EE900E1732D /* SearchView.swift in Sources */, A9B9A8322EB2CD29004C9245 /* SlackResponse.swift in Sources */, A957C1782ECD008E00EA3EE9 /* Bundle.swift in Sources */, + A921C2E02ED067BB00E57B1A /* WelcomeView.swift in Sources */, A9B9A82E2EB2CCBE004C9245 /* StickerSlackTests.swift in Sources */, A9B9A8312EB2CD14004C9245 /* FilterCategory.swift in Sources */, A957C1802ECFAA1100EA3EE9 /* GifView.swift in Sources */, @@ -727,6 +733,7 @@ STRING_CATALOG_GENERATE_SYMBOLS = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_EMIT_LOC_STRINGS = YES; @@ -774,6 +781,7 @@ STRING_CATALOG_GENERATE_SYMBOLS = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/StickerSlack/Emoji/EmojiHoarder.swift b/StickerSlack/Emoji/EmojiHoarder.swift index 1e752bc..45640b3 100644 --- a/StickerSlack/Emoji/EmojiHoarder.swift +++ b/StickerSlack/Emoji/EmojiHoarder.swift @@ -26,8 +26,13 @@ class EmojiHoarder: ObservableObject { @Published var downloadedEmojis: Set = [] @Published var downloadedEmojisArr: [String] = [] @Published var searchTerm: String = "" + @Published var letterStats: [EmojiHoarder.LetterStat] = [] + + @Published var showWelcome: Bool = true init(localOnly: Bool = false, skipIndex: Bool = false) { + self.showWelcome = !UserDefaults.standard.bool(forKey: "showWelcome") + let localDB = loadLocalDB() withAnimation { self.emojis = localDB } loadTrie() @@ -196,6 +201,26 @@ class EmojiHoarder: ObservableObject { Haptic.heavy.trigger() } + func setShowWelcome(to newValue: Bool) { + UserDefaults.standard.set(!newValue, forKey: "shownWelcome") + self.showWelcome = newValue + } + + func generateLetterStats() -> [EmojiHoarder.LetterStat] { + var result: [EmojiHoarder.LetterStat] = [] + for child in trie.root.children { + let count = trie.collectWords(startingWith: child.key, from: child.value).count + let stat = LetterStat(char: child.key, count: count) + result.append(stat) + } + return result + } + + struct LetterStat: Hashable { + var char: String + var count: Int + } + // func filterEmojis(byCategory category: FilterCategory, searchTerm: String) { // guard category != .none else { // filterEmojis(by: searchTerm) diff --git a/StickerSlack/SwiftUI/ContentView.swift b/StickerSlack/SwiftUI/ContentView.swift index 6ecea42..4407e28 100644 --- a/StickerSlack/SwiftUI/ContentView.swift +++ b/StickerSlack/SwiftUI/ContentView.swift @@ -12,40 +12,48 @@ struct ContentView: View { @ObservedObject var hoarder: EmojiHoarder = EmojiHoarder() var body: some View { - if #available(iOS 18, *) { - TabView { - Tab("Browse", systemImage: "square.grid.2x2.fill") { - BrowseView(hoarder: hoarder) + Group { + if #available(iOS 18, *) { + TabView { + Tab("Browse", systemImage: "square.grid.2x2.fill") { + BrowseView(hoarder: hoarder) + } + + Tab("Downloaded", systemImage: "arrow.down.circle.fill") { + DownloadedView(hoarder: hoarder) + } + + Tab("Settings", systemImage: "gear") { + SettingsView(hoarder: hoarder) + } + + Tab(role: .search) { + SearchView(hoarder: hoarder) + } } - - Tab("Downloaded", systemImage: "arrow.down.circle.fill") { + } else { + TabView { DownloadedView(hoarder: hoarder) - } - - Tab("Settings", systemImage: "gear") { - SettingsView(hoarder: hoarder) - } - - Tab(role: .search) { + .tabItem { + Label("Downloaded", systemImage: "arrow.down.circle.fill") + } + BrowseView(hoarder: hoarder) + .tabItem { + Label("Browse", systemImage: "square.grid.2x2.fill") + } SearchView(hoarder: hoarder) + .tabItem { + Label("Search", systemImage: "magnifyingglass") + } } } - } else { - TabView { - DownloadedView(hoarder: hoarder) - .tabItem { - Label("Downloaded", systemImage: "arrow.down.circle.fill") - } - BrowseView(hoarder: hoarder) - .tabItem { - Label("Browse", systemImage: "square.grid.2x2.fill") - } - SearchView(hoarder: hoarder) - .tabItem { - Label("Search", systemImage: "magnifyingglass") - } - } } + .sheet(isPresented: $hoarder.showWelcome) { + print("hi") + } content: { + WelcomeView() + } + } } diff --git a/StickerSlack/SwiftUI/DownloadedView.swift b/StickerSlack/SwiftUI/DownloadedView.swift index 52678ce..6f161bb 100644 --- a/StickerSlack/SwiftUI/DownloadedView.swift +++ b/StickerSlack/SwiftUI/DownloadedView.swift @@ -24,40 +24,42 @@ struct DownloadedView: View { } var body: some View { - 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: spacing) { - ForEach(hoarder.downloadedEmojisArr, id: \.self) { name in - if let emoji = hoarder.trie.dict[name] { - ZStack { - Rectangle() - .foregroundStyle(isDark ? .black : .white) - EmojiPreview(hoarder: hoarder, emoji: emoji) - RoundedRectangle(cornerRadius: 15) - .stroke(.gray, lineWidth: 1) - } - .aspectRatio(1, contentMode: .fit) - .clipShape(RoundedRectangle(cornerRadius: 15)) - .contextMenu { - Text(emoji.name) - Button("Copy Name", systemImage: "doc.on.clipboard") { - UIPasteboard.general.string = emoji.name + GeometryReader { geo in + ScrollView { + let columns: Int = max(1, Int((geo.size.width - 2*spacing) / (minColWidth + spacing))) + let layout = Array(repeating: col, count: columns) + LazyVGrid(columns: layout, spacing: spacing) { + ForEach(hoarder.downloadedEmojisArr, id: \.self) { name in + if let emoji = hoarder.trie.dict[name] { + ZStack { + Rectangle() + .foregroundStyle(isDark ? .black : .white) + EmojiPreview(hoarder: hoarder, emoji: emoji) + RoundedRectangle(cornerRadius: 15) + .stroke(.gray, lineWidth: 1) } - Button("Copy Image", systemImage: "photo.fill.on.rectangle.fill") { - UIPasteboard.general.image = emoji.image - } - Divider() - ShareLink("Share", item: emoji.remoteImageURL, subject: nil, message: nil) - Divider() - Button("Delete", systemImage: "trash.fill", role: .destructive) { - hoarder.delete(emoji: emoji) + .aspectRatio(1, contentMode: .fit) + .clipShape(RoundedRectangle(cornerRadius: 15)) + .contextMenu { + Text(emoji.name) + Button("Copy Name", systemImage: "doc.on.clipboard") { + UIPasteboard.general.string = emoji.name + } + Button("Copy Image", systemImage: "photo.fill.on.rectangle.fill") { + UIPasteboard.general.image = emoji.image + } + Divider() + ShareLink("Share", item: emoji.remoteImageURL, subject: nil, message: nil) + Divider() + Button("Delete", systemImage: "trash.fill", role: .destructive) { + hoarder.delete(emoji: emoji) + } } } } } + .padding(.horizontal, spacing) } - .padding(.horizontal, spacing) } } } diff --git a/StickerSlack/SwiftUI/SettingsView.swift b/StickerSlack/SwiftUI/SettingsView.swift index e89171e..a6c7172 100644 --- a/StickerSlack/SwiftUI/SettingsView.swift +++ b/StickerSlack/SwiftUI/SettingsView.swift @@ -25,16 +25,36 @@ struct SettingsView: View { .font(.title) .monospaced() .bold() + HStack(alignment: .center, spacing: 5) { + Text(Bundle.main.appVersion) + .bold() + Text(Bundle.main.appBuild) + .foregroundStyle(.gray) + } } } Section { - Text("") + Text("\(hoarder.emojis.count) total Emoji") + Text("\(hoarder.downloadedEmojis.count) downloaded Emoji") + NavigationLink { + List { + ForEach(hoarder.generateLetterStats(), id: \.self) { stat in + Text("\(stat.count) Emoji starting with \(stat.char)") + } + } + } label: { + Label("Letter Stats", systemImage: "textformat") + } + } + + Button("Show Welcome", systemImage: "arrow.trianglehead.clockwise") { + hoarder.setShowWelcome(to: true) } Section("Debug") { NavigationLink { - TrieTestingView() + TrieTestingView(hoarder: hoarder) } label: { Label("Tree", systemImage: "tree") } diff --git a/StickerSlack/SwiftUI/WelcomeView.swift b/StickerSlack/SwiftUI/WelcomeView.swift new file mode 100644 index 0000000..1dd5442 --- /dev/null +++ b/StickerSlack/SwiftUI/WelcomeView.swift @@ -0,0 +1,49 @@ +// +// WelcomeView.swift +// StickerSlack +// +// Created by neon443 on 21/11/2025. +// + +import SwiftUI + +struct WelcomeView: View { + var body: some View { + VStack { + Text("StickerSlack") + .bold() + .font(.title) + .monospaced() + .padding() + List { + Section("How to use") { + ListRow(number: 1, text: "Browse or search for an emoji") + ListRow(number: 2, text: "Download it") + ListRow(number: 3, text: "Open iMessage") + ListRow(number: 4, text: "Press the +") + ListRow(number: 5, text: "Choose StickerSlack") + ListRow(number: 6, text: "Tap an emoji to use it!") + } + } + .scrollContentBackground(.hidden) + } + } +} + +struct ListRow: View { + @State var number: Int + @State var text: String + + var body: some View { + HStack { + Text("\(number)") + .padding(.trailing, 10) + .foregroundStyle(.gray) + Text(text) + } + } +} + +#Preview { + WelcomeView() +} diff --git a/StickerSlack/Trie/TrieTestingView.swift b/StickerSlack/Trie/TrieTestingView.swift index cd4651a..9b8439f 100644 --- a/StickerSlack/Trie/TrieTestingView.swift +++ b/StickerSlack/Trie/TrieTestingView.swift @@ -8,7 +8,7 @@ import SwiftUI struct TrieTestingView: View { - @ObservedObject var hoarder: EmojiHoarder = EmojiHoarder(localOnly: true) + @ObservedObject var hoarder: EmojiHoarder @State var searchTerm: String = "" @State var searchStatus: Bool? = nil @@ -99,5 +99,5 @@ struct TrieNodeView: View { } #Preview { - TrieTestingView() + TrieTestingView(hoarder: EmojiHoarder(localOnly: true)) }