ui updates

downloaded emojis is a grid
rewrote deleteAllStickers
added downloadEmoji
This commit is contained in:
neon443
2025-11-05 22:04:04 +00:00
parent 08ad69b19a
commit 42739c93da
5 changed files with 76 additions and 73 deletions

View File

@@ -9,6 +9,7 @@ import Foundation
import SwiftUI import SwiftUI
import Combine import Combine
import UniformTypeIdentifiers import UniformTypeIdentifiers
import Haptics
class EmojiHoarder: ObservableObject { class EmojiHoarder: ObservableObject {
static let container = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.neon443.StickerSlack")!.appendingPathComponent("Library", conformingTo: .directory) static let container = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.neon443.StickerSlack")!.appendingPathComponent("Library", conformingTo: .directory)
@@ -45,17 +46,10 @@ class EmojiHoarder: ObservableObject {
} }
} }
func deleteAllStickers() async { func deleteAllStickers() {
await withTaskGroup { group in for i in emojis.indices {
for i in emojis.indices { guard downloadedEmojis.contains(emojis[i].name) else { continue }
group.addTask { delete(emoji: emojis[i])
guard await self.emojis[i].isLocal else { return }
await self.emojis[i].deleteImage()
DispatchQueue.main.sync {
self.emojis[i].refresh()
}
}
}
} }
} }
@@ -150,6 +144,25 @@ class EmojiHoarder: ObservableObject {
withAnimation { filteredEmojis = trie.search(prefix: searchTerm) } withAnimation { filteredEmojis = trie.search(prefix: searchTerm) }
} }
func download(emoji: Emoji) {
Task.detached {
try? await emoji.downloadImage()
await MainActor.run {
self.downloadedEmojis.insert(emoji.name)
self.trie.dict[emoji.name]?.refresh()
Haptic.success.trigger()
}
}
}
@MainActor
func delete(emoji: Emoji) {
emoji.deleteImage()
downloadedEmojis.remove(emoji.name)
self.trie.dict[emoji.name]?.refresh()
Haptic.heavy.trigger()
}
// 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

@@ -13,12 +13,14 @@ struct ContentView: View {
@State var searchTerm: String = "" @State var searchTerm: String = ""
var col: GridItem = GridItem(.fixed(100), spacing: 0, alignment: .center)
var body: some View { var body: some View {
TabView { TabView {
List { LazyHGrid(rows: Array(repeating: col, count: 4), spacing: 10) {
ForEach(hoarder.downloadedEmojis, id: \.self) { name in ForEach(hoarder.downloadedEmojis.sorted(by: <), id: \.self) { name in
if let emoji = hoarder.trie.dict[name] { if let emoji = hoarder.trie.dict[name] {
EmojiRow(hoarder: hoarder, emoji: emoji) EmojiPreview(hoarder: hoarder, emoji: emoji)
.aspectRatio(1, contentMode: .fit)
} }
} }
} }

View File

@@ -15,46 +15,43 @@ struct EmojiPreview: View {
@State private var id: UUID = UUID() @State private var id: UUID = UUID()
@State private var delay: TimeInterval = 0 @State private var delay: TimeInterval = 0
var body: some View { var body: some View {
VStack(alignment: .leading) { Group {
Text(emoji.name) if let image = emoji.image {
Group { Image(uiImage: image)
if let image = emoji.image { .resizable().scaledToFit()
Image(uiImage: image) .border(.orange)
.resizable().scaledToFit() .overlay(alignment: .bottomLeading) {
.border(.orange) Image(systemName: "arrow.down.circle.fill")
.overlay(alignment: .bottomLeading) { .foregroundStyle(.gray)
Image(systemName: "arrow.down.circle.fill") .shadow(radius: 1)
.foregroundStyle(.gray) .symbolRenderingMode(.hierarchical)
.shadow(radius: 1) }
.symbolRenderingMode(.hierarchical) } else {
} AsyncImage(url: emoji.remoteImageURL) { phase in
} else { if let image = phase.image {
AsyncImage(url: emoji.remoteImageURL) { phase in image
if let image = phase.image { .resizable().scaledToFit()
image } else if phase.error != nil {
.resizable().scaledToFit() ImageErrorView()
} else if phase.error != nil { .onTapGesture {
ImageErrorView() id = UUID()
.onTapGesture { }
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now()+delay) {
id = UUID() id = UUID()
delay+=0.1
} }
.onAppear { }
DispatchQueue.main.asyncAfter(deadline: .now()+delay) { } else {
id = UUID() ProgressView()
delay+=0.1 .frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
} else {
ProgressView()
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
} }
} }
} }
.id(id)
} }
} .id(id)
}
} }
#Preview { #Preview {

View File

@@ -14,28 +14,26 @@ struct EmojiRow: View {
var body: some View { var body: some View {
HStack { HStack {
EmojiPreview( VStack {
hoarder: hoarder, HStack(spacing: .zero) {
emoji: emoji // Text
) Text(emoji.name)
}
EmojiPreview(
hoarder: hoarder,
emoji: emoji
)
}
.frame(maxWidth: 100, maxHeight: 100) .frame(maxWidth: 100, maxHeight: 100)
Spacer() Spacer()
if hoarder.downloadedEmojis.contains(emoji.name) { if hoarder.downloadedEmojis.contains(emoji.name) {
Button("", systemImage: "trash") { Button("", systemImage: "trash") {
emoji.deleteImage() hoarder.delete(emoji: emoji)
emoji.refresh()
Haptic.heavy.trigger()
} }
.buttonStyle(.plain) .buttonStyle(.plain)
} else { } else {
Button("", systemImage: "arrow.down.circle") { Button("", systemImage: "arrow.down.circle") {
Task.detached { hoarder.download(emoji: emoji)
try? await emoji.downloadImage()
await MainActor.run {
emoji.refresh()
Haptic.success.trigger()
}
}
} }
.buttonStyle(.plain) .buttonStyle(.plain)
} }

View File

@@ -46,32 +46,25 @@ struct EmojiCollectionView: UIViewRepresentable {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let emojiName = items[indexPath.row] let emojiName = items[indexPath.row]
let emoji = hoarder.trie.dict[emojiName]!
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.contentConfiguration = UIHostingConfiguration { cell.contentConfiguration = UIHostingConfiguration {
HStack { HStack {
EmojiPreview( EmojiPreview(
hoarder: hoarder, hoarder: hoarder,
emoji: hoarder.trie.dict[emojiName]! emoji: emoji
) )
.frame(maxWidth: 100, maxHeight: 100) .frame(maxWidth: 100, maxHeight: 100)
Spacer() Spacer()
if hoarder.downloadedEmojis.contains(emojiName) { if hoarder.downloadedEmojis.contains(emojiName) {
Button("", systemImage: "trash") { Button("", systemImage: "trash") {
self.hoarder.trie.dict[emojiName]!.deleteImage() fatalError()
self.hoarder.trie.dict[emojiName]!.refresh()
Haptic.heavy.trigger()
} }
.buttonStyle(.plain) .buttonStyle(.plain)
} else { } else {
Button("", systemImage: "arrow.down.circle") { Button("", systemImage: "arrow.down.circle") {
Task.detached { self.hoarder.download(emoji: emoji)
try? await self.hoarder.trie.dict[emojiName]!.downloadImage()
await MainActor.run {
self.hoarder.trie.dict[emojiName]!.refresh()
Haptic.success.trigger()
}
}
} }
.buttonStyle(.plain) .buttonStyle(.plain)
} }