From 2b8c63694534afc058653ae2fcf57ebc6fb89af8 Mon Sep 17 00:00:00 2001 From: Nihaal Sharma <69979447+neon443@users.noreply.github.com> Date: Fri, 6 Mar 2026 23:46:09 +0000 Subject: [PATCH] adding gifs added sticker.swift to add a protocol for stickers and an extension that has shared default implementations of functions and computed properties added stickertype enum --- StickerSlack.xcodeproj/project.pbxproj | 48 +++++++++++++ StickerSlack/Emoji/Emoji.swift | 61 +--------------- StickerSlack/Gifs/Gif.swift | 25 +++++++ StickerSlack/Gifs/GifHoarder.swift | 8 +++ StickerSlack/StickerType.swift | 24 +++++++ StickerSlack/Stickers/Sticker.swift | 71 +++++++++++++++++++ StickerSlack/Stickers/StickerProtocol.swift | 30 ++++++++ StickerSlack/SwiftUI/BrowseView.swift | 6 ++ StickerSlack/SwiftUI/ContentView.swift | 1 + .../StickerBrowserDataSource.swift | 2 +- 10 files changed, 215 insertions(+), 61 deletions(-) create mode 100644 StickerSlack/Gifs/Gif.swift create mode 100644 StickerSlack/Gifs/GifHoarder.swift create mode 100644 StickerSlack/StickerType.swift create mode 100644 StickerSlack/Stickers/Sticker.swift create mode 100644 StickerSlack/Stickers/StickerProtocol.swift diff --git a/StickerSlack.xcodeproj/project.pbxproj b/StickerSlack.xcodeproj/project.pbxproj index 66d1a11..68031c0 100644 --- a/StickerSlack.xcodeproj/project.pbxproj +++ b/StickerSlack.xcodeproj/project.pbxproj @@ -63,6 +63,17 @@ A9BBC5182EB8FA4500FFE82F /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BBC5172EB8FA4500FFE82F /* ViewModifiers.swift */; }; A9BBC5192EB8FA4500FFE82F /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BBC5172EB8FA4500FFE82F /* ViewModifiers.swift */; }; A9BBC51A2EB8FA4500FFE82F /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BBC5172EB8FA4500FFE82F /* ViewModifiers.swift */; }; + A9BE30EC2F5AFE1E00A57668 /* StickerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BE30EB2F5AFE1E00A57668 /* StickerType.swift */; }; + A9BE30EF2F5B004800A57668 /* GifHoarder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BE30EE2F5B004800A57668 /* GifHoarder.swift */; }; + A9BE30F02F5B004800A57668 /* GifHoarder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BE30EE2F5B004800A57668 /* GifHoarder.swift */; }; + A9BE30F22F5B004E00A57668 /* Gif.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BE30F12F5B004E00A57668 /* Gif.swift */; }; + A9BE30F32F5B004E00A57668 /* Gif.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BE30F12F5B004E00A57668 /* Gif.swift */; }; + A9BE30F62F5B011A00A57668 /* StickerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BE30F52F5B011A00A57668 /* StickerProtocol.swift */; }; + A9BE30F72F5B011A00A57668 /* StickerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BE30F52F5B011A00A57668 /* StickerProtocol.swift */; }; + A9BE30F82F5B02BF00A57668 /* StickerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BE30F52F5B011A00A57668 /* StickerProtocol.swift */; }; + A9BE30FA2F5B03F400A57668 /* Sticker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BE30F92F5B03F400A57668 /* Sticker.swift */; }; + A9BE30FB2F5B03F400A57668 /* Sticker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BE30F92F5B03F400A57668 /* Sticker.swift */; }; + A9BE30FC2F5B03F400A57668 /* Sticker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BE30F92F5B03F400A57668 /* Sticker.swift */; }; A9C172DC2EB8C9AC008A7885 /* Trie.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C172DB2EB8C9AC008A7885 /* Trie.swift */; }; A9C172DD2EB8C9AC008A7885 /* Trie.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C172DB2EB8C9AC008A7885 /* Trie.swift */; }; A9CD6C352EDDE22800B7F421 /* EmojiPackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9CD6C342EDDE22800B7F421 /* EmojiPackView.swift */; }; @@ -148,6 +159,11 @@ A9B9A8232EB2CCB5004C9245 /* StickerSlackTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StickerSlackTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; A9B9A82C2EB2CCBE004C9245 /* StickerSlackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerSlackTests.swift; sourceTree = ""; }; A9BBC5172EB8FA4500FFE82F /* ViewModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifiers.swift; sourceTree = ""; }; + A9BE30EB2F5AFE1E00A57668 /* StickerType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerType.swift; sourceTree = ""; }; + A9BE30EE2F5B004800A57668 /* GifHoarder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifHoarder.swift; sourceTree = ""; }; + A9BE30F12F5B004E00A57668 /* Gif.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Gif.swift; sourceTree = ""; }; + A9BE30F52F5B011A00A57668 /* StickerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerProtocol.swift; sourceTree = ""; }; + A9BE30F92F5B03F400A57668 /* Sticker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sticker.swift; sourceTree = ""; }; A9C172DB2EB8C9AC008A7885 /* Trie.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Trie.swift; sourceTree = ""; }; A9CD6C342EDDE22800B7F421 /* EmojiPackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPackView.swift; sourceTree = ""; }; A9CD6C372EDDE37500B7F421 /* EmojiPackManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPackManager.swift; sourceTree = ""; }; @@ -295,6 +311,9 @@ children = ( A924C3742EA9134C00F20781 /* StickerSlack.entitlements */, A949B1F12EA04E8200215164 /* StickerSlackApp.swift */, + A9BE30EB2F5AFE1E00A57668 /* StickerType.swift */, + A9BE30F42F5B011400A57668 /* Stickers */, + A9BE30ED2F5B003A00A57668 /* Gifs */, A949B1F92EA0517800215164 /* Emoji */, A91C09892EBBD75A00210C34 /* Trie */, A9104C812EB4022E00D160EA /* Extensions */, @@ -347,6 +366,24 @@ path = StickerSlackTests; sourceTree = ""; }; + A9BE30ED2F5B003A00A57668 /* Gifs */ = { + isa = PBXGroup; + children = ( + A9BE30F12F5B004E00A57668 /* Gif.swift */, + A9BE30EE2F5B004800A57668 /* GifHoarder.swift */, + ); + path = Gifs; + sourceTree = ""; + }; + A9BE30F42F5B011400A57668 /* Stickers */ = { + isa = PBXGroup; + children = ( + A9BE30F52F5B011A00A57668 /* StickerProtocol.swift */, + A9BE30F92F5B03F400A57668 /* Sticker.swift */, + ); + path = Stickers; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -515,12 +552,17 @@ A9EB724F2EB94A6B00658CEB /* TrieTestingView.swift in Sources */, A924C3732EA9127200F20781 /* Emoji.swift in Sources */, A9D15B8B2EB1142C00404792 /* EmojiPack.swift in Sources */, + A9BE30FB2F5B03F400A57668 /* Sticker.swift in Sources */, + A9BE30F62F5B011A00A57668 /* StickerProtocol.swift in Sources */, A921C2DF2ED067BB00E57B1A /* WelcomeView.swift in Sources */, A949B1F82EA04F2300215164 /* EmojiHoarder.swift in Sources */, A9BBC5182EB8FA4500FFE82F /* ViewModifiers.swift in Sources */, A9EB72492EB948C400658CEB /* EmojiRow.swift in Sources */, A921C2E22ED071C900E57B1A /* ListRow.swift in Sources */, A9773C2F2EA54AF000F3B753 /* EmojiPreview.swift in Sources */, + A9BE30F02F5B004800A57668 /* GifHoarder.swift in Sources */, + A9BE30F22F5B004E00A57668 /* Gif.swift in Sources */, + A9BE30EC2F5AFE1E00A57668 /* StickerType.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -529,9 +571,11 @@ buildActionMask = 2147483647; files = ( A957C17B2ECE542D00EA3EE9 /* GifManager.swift in Sources */, + A9BE30F82F5B02BF00A57668 /* StickerProtocol.swift in Sources */, A986A6C32EB6598100B6E0FA /* FilterCategory.swift in Sources */, A9CD6C3A2EDDE6B500B7F421 /* EmojiPack.swift in Sources */, A9EB724B2EB94A5700658CEB /* Trie.swift in Sources */, + A9BE30FA2F5B03F400A57668 /* Sticker.swift in Sources */, A986A6CD2EB659E000B6E0FA /* MessagesViewController.swift in Sources */, A986A6CE2EB659E000B6E0FA /* StickerBrowserDataSource.swift in Sources */, A957C17E2ECFAA1100EA3EE9 /* GifView.swift in Sources */, @@ -550,9 +594,12 @@ A9104C7F2EB4022500D160EA /* MSSticker.swift in Sources */, A9B9A8302EB2CD0B004C9245 /* Emoji.swift in Sources */, A955B3F12EC22E9700E1732D /* BrowseView.swift in Sources */, + A9BE30F72F5B011A00A57668 /* StickerProtocol.swift in Sources */, + A9BE30FC2F5B03F400A57668 /* Sticker.swift in Sources */, A9B9A82F2EB2CCED004C9245 /* EmojiHoarder.swift in Sources */, A9CD6C3B2EDDE6B500B7F421 /* EmojiPack.swift in Sources */, A921C2E32ED071C900E57B1A /* ListRow.swift in Sources */, + A9BE30EF2F5B004800A57668 /* GifHoarder.swift in Sources */, A9EB724A2EB948E000658CEB /* EmojiCollectionView.swift in Sources */, A9EB724D2EB94A6B00658CEB /* TrieTestingView.swift in Sources */, A9BBC51A2EB8FA4500FFE82F /* ViewModifiers.swift in Sources */, @@ -567,6 +614,7 @@ A957C1782ECD008E00EA3EE9 /* Bundle.swift in Sources */, A921C2E02ED067BB00E57B1A /* WelcomeView.swift in Sources */, A9B9A82E2EB2CCBE004C9245 /* StickerSlackTests.swift in Sources */, + A9BE30F32F5B004E00A57668 /* Gif.swift in Sources */, A9B9A8312EB2CD14004C9245 /* FilterCategory.swift in Sources */, A957C1802ECFAA1100EA3EE9 /* GifView.swift in Sources */, A9EB72502EB94FAD00658CEB /* EmojiPreview.swift in Sources */, diff --git a/StickerSlack/Emoji/Emoji.swift b/StickerSlack/Emoji/Emoji.swift index ab8669f..0dadb9c 100644 --- a/StickerSlack/Emoji/Emoji.swift +++ b/StickerSlack/Emoji/Emoji.swift @@ -11,7 +11,7 @@ import SwiftUI import Messages import UniformTypeIdentifiers -struct Emoji: Codable, Identifiable, Hashable { +struct Emoji: StickerProtocol { var id: UUID var uiID: UUID var name: String @@ -21,31 +21,8 @@ struct Emoji: Codable, Identifiable, Hashable { let fileExtension = ".\(split.last ?? "png")" return EmojiHoarder.container.absoluteString+id.uuidString+fileExtension } - var localImageURL: URL { - return URL(string: localImageURLString)! - } var remoteImageURL: URL - var isLocal: Bool { - return (try? Data(contentsOf: localImageURL)) != nil - } - - var sticker: MSSticker? { - guard isLocal else { - return nil - } - return try? MSSticker(contentsOfFileURL: localImageURL, localizedDescription: name) - } - - var image: UIImage? { - if let data = try? Data(contentsOf: localImageURL), - let img = UIImage(data: data) { - return img - } else { - return nil - } - } - enum CodingKeys: String, CodingKey { case id = "id" case name = "name" @@ -90,47 +67,11 @@ struct Emoji: Codable, Identifiable, Hashable { return } - func deleteImage() { - try? FileManager.default.removeItem(at: localImageURL) - return - } - @MainActor mutating func refresh() { withAnimation { self.uiID = UUID() } } - func resize(image: UIImage, to targetSize: CGSize) -> UIImage { - let oldSize = image.size - let ratio: (x: CGFloat, y: CGFloat) - ratio.x = targetSize.width / oldSize.width - ratio.y = targetSize.height / oldSize.height - - var newSize: CGSize - if ratio.x > ratio.y { - newSize = CGSize(width: oldSize.width * ratio.y, height: oldSize.height * ratio.y) - } else { - newSize = CGSize(width: oldSize.width * ratio.x, height: oldSize.height * ratio.x) - } - - let rect = CGRect(origin: .zero, size: newSize) - - if let frames = image.images { - var result: [UIImage] = [] - for frame in frames { - UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) - frame.draw(in: rect) - result.append(UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()) - UIGraphicsEndImageContext() - } - return UIImage.animatedImage(with: result, duration: image.duration) ?? UIImage() - } - - UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) - image.draw(in: rect) - return UIGraphicsGetImageFromCurrentImageContext() ?? UIImage() - } - static var test: Emoji = Emoji( name: "s?", url: URL(string: "https://neon443.github.io/images/fav.ico")! diff --git a/StickerSlack/Gifs/Gif.swift b/StickerSlack/Gifs/Gif.swift new file mode 100644 index 0000000..54ec81b --- /dev/null +++ b/StickerSlack/Gifs/Gif.swift @@ -0,0 +1,25 @@ +// +// Gif.swift +// StickerSlack +// +// Created by neon443 on 06/03/2026. +// + +import Foundation + +//struct Gif: StickerProtocol { +// var id: UUID +// var uiID: UUID +// var name: String +// var localImageURLString: String { +// +// } +// var localImageURL: URL { +// return URL(string: localImageURLString)! +// } +// var remoteImageURL: URL +// +// var isLocal: Bool { +// +// } +//} diff --git a/StickerSlack/Gifs/GifHoarder.swift b/StickerSlack/Gifs/GifHoarder.swift new file mode 100644 index 0000000..4434518 --- /dev/null +++ b/StickerSlack/Gifs/GifHoarder.swift @@ -0,0 +1,8 @@ +// +// GifHoarder.swift +// StickerSlack +// +// Created by neon443 on 06/03/2026. +// + +import Foundation diff --git a/StickerSlack/StickerType.swift b/StickerSlack/StickerType.swift new file mode 100644 index 0000000..1a722b0 --- /dev/null +++ b/StickerSlack/StickerType.swift @@ -0,0 +1,24 @@ +// +// StickerType.swift +// StickerSlack +// +// Created by neon443 on 06/03/2026. +// + +import Foundation + +enum StickerType: CustomStringConvertible, CaseIterable, Identifiable { + case slackEmoji + case giphyGif + + var id: String { self.description } + + var description: String { + switch self { + case .slackEmoji: + return "Slack Emoji" + case .giphyGif: + return "Giphy GIF" + } + } +} diff --git a/StickerSlack/Stickers/Sticker.swift b/StickerSlack/Stickers/Sticker.swift new file mode 100644 index 0000000..bead561 --- /dev/null +++ b/StickerSlack/Stickers/Sticker.swift @@ -0,0 +1,71 @@ +// +// Sticker.swift +// StickerSlack +// +// Created by neon443 on 06/03/2026. +// + +import Foundation +import Messages + +extension StickerProtocol { + var localImageURL: URL { + return URL(string: localImageURLString)! + } + + var isLocal: Bool { + return (try? Data(contentsOf: localImageURL)) != nil + } + + var msSticker: MSSticker? { + guard isLocal else { + return nil + } + return try? MSSticker(contentsOfFileURL: localImageURL, localizedDescription: name) + } + + var image: UIImage? { + if let data = try? Data(contentsOf: localImageURL), + let img = UIImage(data: data) { + return img + } else { + return nil + } + } + + func deleteImage() { + try? FileManager.default.removeItem(at: localImageURL) + return + } + + func resize(image: UIImage, to targetSize: CGSize) -> UIImage { + let oldSize = image.size + let ratio: (x: CGFloat, y: CGFloat) + ratio.x = targetSize.width / oldSize.width + ratio.y = targetSize.height / oldSize.height + + var newSize: CGSize + if ratio.x > ratio.y { + newSize = CGSize(width: oldSize.width * ratio.y, height: oldSize.height * ratio.y) + } else { + newSize = CGSize(width: oldSize.width * ratio.x, height: oldSize.height * ratio.x) + } + + let rect = CGRect(origin: .zero, size: newSize) + + if let frames = image.images { + var result: [UIImage] = [] + for frame in frames { + UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) + frame.draw(in: rect) + result.append(UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()) + UIGraphicsEndImageContext() + } + return UIImage.animatedImage(with: result, duration: image.duration) ?? UIImage() + } + + UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) + image.draw(in: rect) + return UIGraphicsGetImageFromCurrentImageContext() ?? UIImage() + } +} diff --git a/StickerSlack/Stickers/StickerProtocol.swift b/StickerSlack/Stickers/StickerProtocol.swift new file mode 100644 index 0000000..e18c9c9 --- /dev/null +++ b/StickerSlack/Stickers/StickerProtocol.swift @@ -0,0 +1,30 @@ +// +// StickerProtocol.swift +// StickerSlack +// +// Created by neon443 on 06/03/2026. +// + +import Foundation +import Messages + +protocol StickerProtocol: Codable, Identifiable, Hashable { + var id: UUID { get set } + var uiID: UUID { get set } + var name: String { get set } + + var localImageURL: URL { get } + var localImageURLString: String { get } + var remoteImageURL: URL { get set } + + var isLocal: Bool { get } + + var msSticker: MSSticker? { get } + var image: UIImage? { get } + + func downloadImage() async throws + func deleteImage() + mutating func refresh() + func resize(image: UIImage, to targetSize: CGSize) -> UIImage + static var test: Self { get } +} diff --git a/StickerSlack/SwiftUI/BrowseView.swift b/StickerSlack/SwiftUI/BrowseView.swift index 4f33f44..f822c0f 100644 --- a/StickerSlack/SwiftUI/BrowseView.swift +++ b/StickerSlack/SwiftUI/BrowseView.swift @@ -12,6 +12,12 @@ struct BrowseView: View { var body: some View { List { + Picker("", selection: .constant(StickerType.giphyGif)) { + ForEach(StickerType.allCases, id: \.self) { type in + Text(type.description) + } + } + .pickerStyle(.segmented) ForEach(hoarder.emojis, id: \.self) { emoji in EmojiRow(hoarder: hoarder, emoji: emoji) } diff --git a/StickerSlack/SwiftUI/ContentView.swift b/StickerSlack/SwiftUI/ContentView.swift index b09aee8..e5d0830 100644 --- a/StickerSlack/SwiftUI/ContentView.swift +++ b/StickerSlack/SwiftUI/ContentView.swift @@ -10,6 +10,7 @@ import Haptics struct ContentView: View { @ObservedObject var hoarder: EmojiHoarder = EmojiHoarder() +// @ObservedObject var hoarder: = EmojiHoarder() var body: some View { Group { diff --git a/StickerSlackiMessageApp/StickerBrowserDataSource.swift b/StickerSlackiMessageApp/StickerBrowserDataSource.swift index 2b6b048..bf77261 100644 --- a/StickerSlackiMessageApp/StickerBrowserDataSource.swift +++ b/StickerSlackiMessageApp/StickerBrowserDataSource.swift @@ -16,7 +16,7 @@ class StickerBrowserDataSource: NSObject, MSStickerBrowserViewDataSource { func numberOfStickers(in stickerBrowserView: MSStickerBrowserView) -> Int { guard emojis.isEmpty else { return emojis.count } for emoji in hoarder.emojis { - guard let sticker = emoji.sticker else { continue } + guard let sticker = emoji.msSticker else { continue } emojis.append(sticker) } return emojis.count