mirror of
https://github.com/neon443/StickerSlack.git
synced 2026-03-11 05:19:13 +00:00
a lot of updates
grid view for downloaded emojis shared instance of EmojiHoarder nonisolated static localEmojiDB var deleteallstickers is @MainActor
This commit is contained in:
@@ -14,9 +14,9 @@
|
|||||||
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 */; };
|
||||||
A9112EAC2EAFFDB0006739E2 /* Haptics in Frameworks */ = {isa = PBXBuildFile; productRef = A9112EAB2EAFFDB0006739E2 /* Haptics */; };
|
|
||||||
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 */; };
|
||||||
A935437B2EB2A3C800BB80A4 /* FilterCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A935437A2EB2A3C800BB80A4 /* FilterCategory.swift */; };
|
A935437B2EB2A3C800BB80A4 /* FilterCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A935437A2EB2A3C800BB80A4 /* FilterCategory.swift */; };
|
||||||
A949B1F32EA04E8200215164 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A949B1EF2EA04E8200215164 /* Assets.xcassets */; };
|
A949B1F32EA04E8200215164 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A949B1EF2EA04E8200215164 /* Assets.xcassets */; };
|
||||||
A949B1F42EA04E8200215164 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949B1F02EA04E8200215164 /* ContentView.swift */; };
|
A949B1F42EA04E8200215164 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949B1F02EA04E8200215164 /* ContentView.swift */; };
|
||||||
@@ -126,7 +126,6 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
A924C3782EA9225800F20781 /* Haptics in Frameworks */,
|
A924C3782EA9225800F20781 /* Haptics in Frameworks */,
|
||||||
A9112EAC2EAFFDB0006739E2 /* Haptics in Frameworks */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -135,6 +134,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
A986A6AE2EB658DF00B6E0FA /* Messages.framework in Frameworks */,
|
A986A6AE2EB658DF00B6E0FA /* Messages.framework in Frameworks */,
|
||||||
|
A931D4082EBC9646007BC75B /* Haptics in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -337,6 +337,7 @@
|
|||||||
);
|
);
|
||||||
name = StickerSlackiMessageApp;
|
name = StickerSlackiMessageApp;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
|
A931D4072EBC9646007BC75B /* Haptics */,
|
||||||
);
|
);
|
||||||
productName = StickerSlackiMessageApp;
|
productName = StickerSlackiMessageApp;
|
||||||
productReference = A986A6AD2EB658DF00B6E0FA /* StickerSlackiMessageApp.appex */;
|
productReference = A986A6AD2EB658DF00B6E0FA /* StickerSlackiMessageApp.appex */;
|
||||||
@@ -909,6 +910,11 @@
|
|||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = Haptics;
|
productName = Haptics;
|
||||||
};
|
};
|
||||||
|
A931D4072EBC9646007BC75B /* Haptics */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = A9112EAA2EAFFDB0006739E2 /* XCRemoteSwiftPackageReference "Haptics" */;
|
||||||
|
productName = Haptics;
|
||||||
|
};
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = A949B1D72EA04C0B00215164 /* Project object */;
|
rootObject = A949B1D72EA04C0B00215164 /* Project object */;
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ import UniformTypeIdentifiers
|
|||||||
import Haptics
|
import Haptics
|
||||||
|
|
||||||
class EmojiHoarder: ObservableObject {
|
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 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 endpoint: URL = URL(string: "https://cachet.dunkirk.sh/emojis")!
|
||||||
private let encoder = JSONEncoder()
|
private let encoder = JSONEncoder()
|
||||||
private let decoder = JSONDecoder()
|
private let decoder = JSONDecoder()
|
||||||
@@ -46,6 +47,7 @@ class EmojiHoarder: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
func deleteAllStickers() {
|
func deleteAllStickers() {
|
||||||
for i in emojis.indices {
|
for i in emojis.indices {
|
||||||
guard downloadedEmojis.contains(emojis[i].name) else { continue }
|
guard downloadedEmojis.contains(emojis[i].name) else { continue }
|
||||||
|
|||||||
@@ -9,28 +9,63 @@ import SwiftUI
|
|||||||
import Haptics
|
import Haptics
|
||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
@StateObject var hoarder: EmojiHoarder = EmojiHoarder()
|
@StateObject var hoarder: EmojiHoarder = .shared
|
||||||
|
|
||||||
@State var searchTerm: String = ""
|
@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 {
|
var body: some View {
|
||||||
TabView {
|
TabView {
|
||||||
LazyHGrid(rows: Array(repeating: col, count: 4), spacing: 10) {
|
ScrollView {
|
||||||
ForEach(hoarder.downloadedEmojis.sorted(by: <), id: \.self) { name in
|
let columns: Int = max(1, Int((UIScreen.main.bounds.width - 2*spacing) / (minColWidth + spacing)))
|
||||||
if let emoji = hoarder.trie.dict[name] {
|
let layout = Array(repeating: col, count: columns)
|
||||||
EmojiPreview(hoarder: hoarder, emoji: emoji)
|
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)
|
.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 {
|
.tabItem {
|
||||||
Label("Downloaded", systemImage: "arrow.down.circle.fill")
|
Label("Downloaded", systemImage: "arrow.down.circle.fill")
|
||||||
}
|
}
|
||||||
|
|
||||||
List {
|
List {
|
||||||
ForEach(hoarder.emojis, id: \.self) { emoji in
|
ForEach(hoarder.emojis, id: \.self) { emoji in
|
||||||
EmojiRow(hoarder: hoarder, emoji: emoji)
|
EmojiRow(emoji: emoji)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.tabItem {
|
.tabItem {
|
||||||
@@ -42,7 +77,7 @@ struct ContentView: View {
|
|||||||
|
|
||||||
ForEach(hoarder.filteredEmojis, id: \.self) { name in
|
ForEach(hoarder.filteredEmojis, id: \.self) { name in
|
||||||
if let emoji = hoarder.trie.dict[name] {
|
if let emoji = hoarder.trie.dict[name] {
|
||||||
EmojiRow(hoarder: hoarder, emoji: emoji)
|
EmojiRow(emoji: emoji)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,9 +94,7 @@ struct ContentView: View {
|
|||||||
Label("Search", systemImage: "magnifyingglass")
|
Label("Search", systemImage: "magnifyingglass")
|
||||||
}
|
}
|
||||||
|
|
||||||
TrieTestingView(
|
TrieTestingView()
|
||||||
hoarder: hoarder,
|
|
||||||
)
|
|
||||||
.tabItem {
|
.tabItem {
|
||||||
Label("Tree", systemImage: "tree.fill")
|
Label("Tree", systemImage: "tree.fill")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import SwiftUI
|
|||||||
import Haptics
|
import Haptics
|
||||||
|
|
||||||
struct EmojiPreview: View {
|
struct EmojiPreview: View {
|
||||||
@ObservedObject var hoarder: EmojiHoarder
|
@ObservedObject var hoarder: EmojiHoarder = .shared
|
||||||
@State var emoji: Emoji
|
@State var emoji: Emoji
|
||||||
|
|
||||||
@State private var id: UUID = UUID()
|
@State private var id: UUID = UUID()
|
||||||
@@ -20,13 +20,6 @@ struct EmojiPreview: View {
|
|||||||
if let image = emoji.image {
|
if let image = emoji.image {
|
||||||
Image(uiImage: image)
|
Image(uiImage: image)
|
||||||
.resizable().scaledToFit()
|
.resizable().scaledToFit()
|
||||||
.border(.orange)
|
|
||||||
.overlay(alignment: .bottomLeading) {
|
|
||||||
Image(systemName: "arrow.down.circle.fill")
|
|
||||||
.foregroundStyle(.gray)
|
|
||||||
.shadow(radius: 1)
|
|
||||||
.symbolRenderingMode(.hierarchical)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
AsyncImage(url: emoji.remoteImageURL) { phase in
|
AsyncImage(url: emoji.remoteImageURL) { phase in
|
||||||
if let image = phase.image {
|
if let image = phase.image {
|
||||||
@@ -54,13 +47,6 @@ struct EmojiPreview: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
|
||||||
EmojiPreview(
|
|
||||||
hoarder: EmojiHoarder(localOnly: true),
|
|
||||||
emoji: Emoji.test
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ImageErrorView: View {
|
struct ImageErrorView: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Image(systemName: "xmark.app.fill")
|
Image(systemName: "xmark.app.fill")
|
||||||
@@ -70,3 +56,10 @@ struct ImageErrorView: View {
|
|||||||
.foregroundStyle(.red)
|
.foregroundStyle(.red)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
EmojiPreview(
|
||||||
|
hoarder: EmojiHoarder(localOnly: true),
|
||||||
|
emoji: Emoji.test
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import SwiftUI
|
|||||||
import Haptics
|
import Haptics
|
||||||
|
|
||||||
struct EmojiRow: View {
|
struct EmojiRow: View {
|
||||||
@ObservedObject var hoarder: EmojiHoarder
|
@ObservedObject var hoarder: EmojiHoarder = .shared
|
||||||
@State var emoji: Emoji
|
@State var emoji: Emoji
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@@ -19,10 +19,7 @@ struct EmojiRow: View {
|
|||||||
// Text
|
// Text
|
||||||
Text(emoji.name)
|
Text(emoji.name)
|
||||||
}
|
}
|
||||||
EmojiPreview(
|
EmojiPreview(emoji: emoji)
|
||||||
hoarder: hoarder,
|
|
||||||
emoji: emoji
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
.frame(maxWidth: 100, maxHeight: 100)
|
.frame(maxWidth: 100, maxHeight: 100)
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|||||||
@@ -64,11 +64,11 @@ struct TrieTestingView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if uikit {
|
if uikit {
|
||||||
EmojiCollectionView(hoarder: hoarder, items: filterResult)
|
EmojiCollectionView(items: filterResult)
|
||||||
.id(filterResult)
|
.id(filterResult)
|
||||||
} else {
|
} else {
|
||||||
List(filterResult, id: \.self) { item in
|
List(filterResult, id: \.self) { item in
|
||||||
EmojiRow(hoarder: hoarder, emoji: hoarder.trie.dict[item]!)
|
EmojiRow(emoji: hoarder.trie.dict[item]!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import SwiftUI
|
|||||||
import Haptics
|
import Haptics
|
||||||
|
|
||||||
struct EmojiCollectionView: UIViewRepresentable {
|
struct EmojiCollectionView: UIViewRepresentable {
|
||||||
let hoarder: EmojiHoarder
|
let hoarder: EmojiHoarder = .shared
|
||||||
let items: [String]
|
let items: [String]
|
||||||
|
|
||||||
func makeUIView(context: Context) -> UITableView {
|
func makeUIView(context: Context) -> UITableView {
|
||||||
@@ -28,15 +28,14 @@ struct EmojiCollectionView: UIViewRepresentable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func makeCoordinator() -> Coordinator {
|
func makeCoordinator() -> Coordinator {
|
||||||
Coordinator(hoarder: hoarder, items: items)
|
Coordinator(items: items)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Coordinator: NSObject, UITableViewDataSource {
|
final class Coordinator: NSObject, UITableViewDataSource {
|
||||||
var hoarder: EmojiHoarder
|
var hoarder: EmojiHoarder = .shared
|
||||||
var items: [String]
|
var items: [String]
|
||||||
|
|
||||||
init(hoarder: EmojiHoarder, items: [String]) {
|
init(items: [String]) {
|
||||||
self.hoarder = hoarder
|
|
||||||
self.items = items
|
self.items = items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ struct StickerSlackTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test func deleteAllEmojis() async throws {
|
@Test func deleteAllEmojis() async throws {
|
||||||
let performanceTests = PerformanceTests(hoarder: hoarder)
|
let performanceTests = PerformanceTests(hoarder: .shared)
|
||||||
try! await performanceTests.fakeDownloadAllStickers()
|
try! await performanceTests.fakeDownloadAllStickers()
|
||||||
await hoarder.deleteAllStickers()
|
await hoarder.deleteAllStickers()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user