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
This commit is contained in:
neon443
2025-11-21 10:09:03 +00:00
parent c042e0a4bf
commit 3476751a31
7 changed files with 172 additions and 60 deletions

View File

@@ -14,6 +14,8 @@
A9104C7C2EB3AE6300D160EA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A949B1EF2EA04E8200215164 /* Assets.xcassets */; }; A9104C7C2EB3AE6300D160EA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A949B1EF2EA04E8200215164 /* Assets.xcassets */; };
A9104C7F2EB4022500D160EA /* MSSticker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9104C7D2EB4022500D160EA /* MSSticker.swift */; }; A9104C7F2EB4022500D160EA /* MSSticker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9104C7D2EB4022500D160EA /* MSSticker.swift */; };
A9104C802EB4022500D160EA /* 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 */; }; A924C3732EA9127200F20781 /* Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = A924C3712EA9127200F20781 /* Emoji.swift */; };
A924C3782EA9225800F20781 /* Haptics in Frameworks */ = {isa = PBXBuildFile; productRef = A924C3772EA9225800F20781 /* Haptics */; }; A924C3782EA9225800F20781 /* Haptics in Frameworks */ = {isa = PBXBuildFile; productRef = A924C3772EA9225800F20781 /* Haptics */; };
A931D4082EBC9646007BC75B /* Haptics in Frameworks */ = {isa = PBXBuildFile; productRef = A931D4072EBC9646007BC75B /* 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 = "<group>"; }; A9104C722EB3AE4700D160EA /* Icon.pxd */ = {isa = PBXFileReference; lastKnownFileType = file; path = Icon.pxd; sourceTree = "<group>"; };
A9104C732EB3AE4700D160EA /* StickerSlack.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = StickerSlack.icon; sourceTree = "<group>"; }; A9104C732EB3AE4700D160EA /* StickerSlack.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = StickerSlack.icon; sourceTree = "<group>"; };
A9104C7D2EB4022500D160EA /* MSSticker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSSticker.swift; sourceTree = "<group>"; }; A9104C7D2EB4022500D160EA /* MSSticker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSSticker.swift; sourceTree = "<group>"; };
A921C2DE2ED067BB00E57B1A /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
A924C3712EA9127200F20781 /* Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Emoji.swift; sourceTree = "<group>"; }; A924C3712EA9127200F20781 /* Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Emoji.swift; sourceTree = "<group>"; };
A924C3742EA9134C00F20781 /* StickerSlack.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = StickerSlack.entitlements; sourceTree = "<group>"; }; A924C3742EA9134C00F20781 /* StickerSlack.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = StickerSlack.entitlements; sourceTree = "<group>"; };
A935437A2EB2A3C800BB80A4 /* FilterCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterCategory.swift; sourceTree = "<group>"; }; A935437A2EB2A3C800BB80A4 /* FilterCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterCategory.swift; sourceTree = "<group>"; };
@@ -237,6 +240,7 @@
A955B3F02EC22E9700E1732D /* BrowseView.swift */, A955B3F02EC22E9700E1732D /* BrowseView.swift */,
A955B3F42EC22EE900E1732D /* SearchView.swift */, A955B3F42EC22EE900E1732D /* SearchView.swift */,
A957C1732ECCE2CE00EA3EE9 /* SettingsView.swift */, A957C1732ECCE2CE00EA3EE9 /* SettingsView.swift */,
A921C2DE2ED067BB00E57B1A /* WelcomeView.swift */,
); );
path = SwiftUI; path = SwiftUI;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -495,6 +499,7 @@
A9EB724F2EB94A6B00658CEB /* TrieTestingView.swift in Sources */, A9EB724F2EB94A6B00658CEB /* TrieTestingView.swift in Sources */,
A924C3732EA9127200F20781 /* Emoji.swift in Sources */, A924C3732EA9127200F20781 /* Emoji.swift in Sources */,
A9D15B8B2EB1142C00404792 /* EmojiPack.swift in Sources */, A9D15B8B2EB1142C00404792 /* EmojiPack.swift in Sources */,
A921C2DF2ED067BB00E57B1A /* WelcomeView.swift in Sources */,
A949B1F82EA04F2300215164 /* EmojiHoarder.swift in Sources */, A949B1F82EA04F2300215164 /* EmojiHoarder.swift in Sources */,
A9BBC5182EB8FA4500FFE82F /* ViewModifiers.swift in Sources */, A9BBC5182EB8FA4500FFE82F /* ViewModifiers.swift in Sources */,
A9EB72492EB948C400658CEB /* EmojiRow.swift in Sources */, A9EB72492EB948C400658CEB /* EmojiRow.swift in Sources */,
@@ -538,6 +543,7 @@
A955B3F62EC22EE900E1732D /* SearchView.swift in Sources */, A955B3F62EC22EE900E1732D /* SearchView.swift in Sources */,
A9B9A8322EB2CD29004C9245 /* SlackResponse.swift in Sources */, A9B9A8322EB2CD29004C9245 /* SlackResponse.swift in Sources */,
A957C1782ECD008E00EA3EE9 /* Bundle.swift in Sources */, A957C1782ECD008E00EA3EE9 /* Bundle.swift in Sources */,
A921C2E02ED067BB00E57B1A /* WelcomeView.swift in Sources */,
A9B9A82E2EB2CCBE004C9245 /* StickerSlackTests.swift in Sources */, A9B9A82E2EB2CCBE004C9245 /* StickerSlackTests.swift in Sources */,
A9B9A8312EB2CD14004C9245 /* FilterCategory.swift in Sources */, A9B9A8312EB2CD14004C9245 /* FilterCategory.swift in Sources */,
A957C1802ECFAA1100EA3EE9 /* GifView.swift in Sources */, A957C1802ECFAA1100EA3EE9 /* GifView.swift in Sources */,
@@ -727,6 +733,7 @@
STRING_CATALOG_GENERATE_SYMBOLS = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
@@ -774,6 +781,7 @@
STRING_CATALOG_GENERATE_SYMBOLS = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;

View File

@@ -26,8 +26,13 @@ class EmojiHoarder: ObservableObject {
@Published var downloadedEmojis: Set<String> = [] @Published var downloadedEmojis: Set<String> = []
@Published var downloadedEmojisArr: [String] = [] @Published var downloadedEmojisArr: [String] = []
@Published var searchTerm: String = "" @Published var searchTerm: String = ""
@Published var letterStats: [EmojiHoarder.LetterStat] = []
@Published var showWelcome: Bool = true
init(localOnly: Bool = false, skipIndex: Bool = false) { init(localOnly: Bool = false, skipIndex: Bool = false) {
self.showWelcome = !UserDefaults.standard.bool(forKey: "showWelcome")
let localDB = loadLocalDB() let localDB = loadLocalDB()
withAnimation { self.emojis = localDB } withAnimation { self.emojis = localDB }
loadTrie() loadTrie()
@@ -196,6 +201,26 @@ class EmojiHoarder: ObservableObject {
Haptic.heavy.trigger() 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) { // func filterEmojis(byCategory category: FilterCategory, searchTerm: String) {
// guard category != .none else { // guard category != .none else {
// filterEmojis(by: searchTerm) // filterEmojis(by: searchTerm)

View File

@@ -12,6 +12,7 @@ struct ContentView: View {
@ObservedObject var hoarder: EmojiHoarder = EmojiHoarder() @ObservedObject var hoarder: EmojiHoarder = EmojiHoarder()
var body: some View { var body: some View {
Group {
if #available(iOS 18, *) { if #available(iOS 18, *) {
TabView { TabView {
Tab("Browse", systemImage: "square.grid.2x2.fill") { Tab("Browse", systemImage: "square.grid.2x2.fill") {
@@ -47,6 +48,13 @@ struct ContentView: View {
} }
} }
} }
.sheet(isPresented: $hoarder.showWelcome) {
print("hi")
} content: {
WelcomeView()
}
}
} }
#Preview { #Preview {

View File

@@ -24,8 +24,9 @@ struct DownloadedView: View {
} }
var body: some View { var body: some View {
GeometryReader { geo in
ScrollView { ScrollView {
let columns: Int = max(1, Int((UIScreen.main.bounds.width - 2*spacing) / (minColWidth + spacing))) let columns: Int = max(1, Int((geo.size.width - 2*spacing) / (minColWidth + spacing)))
let layout = Array(repeating: col, count: columns) let layout = Array(repeating: col, count: columns)
LazyVGrid(columns: layout, spacing: spacing) { LazyVGrid(columns: layout, spacing: spacing) {
ForEach(hoarder.downloadedEmojisArr, id: \.self) { name in ForEach(hoarder.downloadedEmojisArr, id: \.self) { name in
@@ -61,6 +62,7 @@ struct DownloadedView: View {
} }
} }
} }
}
#Preview { #Preview {
DownloadedView(hoarder: EmojiHoarder(localOnly: true)) DownloadedView(hoarder: EmojiHoarder(localOnly: true))

View File

@@ -25,16 +25,36 @@ struct SettingsView: View {
.font(.title) .font(.title)
.monospaced() .monospaced()
.bold() .bold()
HStack(alignment: .center, spacing: 5) {
Text(Bundle.main.appVersion)
.bold()
Text(Bundle.main.appBuild)
.foregroundStyle(.gray)
}
} }
} }
Section { 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") { Section("Debug") {
NavigationLink { NavigationLink {
TrieTestingView() TrieTestingView(hoarder: hoarder)
} label: { } label: {
Label("Tree", systemImage: "tree") Label("Tree", systemImage: "tree")
} }

View File

@@ -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()
}

View File

@@ -8,7 +8,7 @@
import SwiftUI import SwiftUI
struct TrieTestingView: View { struct TrieTestingView: View {
@ObservedObject var hoarder: EmojiHoarder = EmojiHoarder(localOnly: true) @ObservedObject var hoarder: EmojiHoarder
@State var searchTerm: String = "" @State var searchTerm: String = ""
@State var searchStatus: Bool? = nil @State var searchStatus: Bool? = nil
@@ -99,5 +99,5 @@ struct TrieNodeView: View {
} }
#Preview { #Preview {
TrieTestingView() TrieTestingView(hoarder: EmojiHoarder(localOnly: true))
} }