-
Notifications
You must be signed in to change notification settings - Fork 289
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gif: Nostr Build GIF Keyboard #2387
base: master
Are you sure you want to change the base?
Conversation
Thanks @ericholguin! I am still reviewing and testing the new GIF keyboard, but just to follow up on that issue with GIFs not always loading on the poster's device, I made an experiment and I believe the issue is around the image cache. Here is the experiment I performed: Device: iPhone 15 simulator
Results:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some general comments
@State var results:[NostrBuildGif] = [] | ||
@State var cursor: Int = 0 | ||
@State var errorAlert: Bool = false | ||
@SceneStorage("NostrBuildGIFGrid.show_nsfw_alert") var show_nsfw_alert : Bool = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to persist show_nsfw_alert
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes this way users are not presented with the alert again.
TopBar | ||
ScrollView { | ||
LazyVGrid(columns: columns, spacing: 5) { | ||
ForEach($results) { gifResult in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personal opinion/optional comment: Instead of having results
as an array that starts out empty and later gets filled up, and a separate errorAlert
state, I would create a single local enum
to track the state, with cases like "loading", "failure", "loaded(results: [NostrBuildGif])".
This helps differentiate the UI when there are no results vs when we are still loading results. It also simplifies state management, as an error on the NostrBuild request is interconnected with the GIF results
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense, I might need some help with that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can help with that if you need anything, just let me know what you would need help with!
@ericholguin did another experiment — this time to try to find where the image caching is going wrong. Here is the second experiment I performed: Device: iPhone 15 simulator
Results: GIF is not animated on the second device This experiment points in the direction that the caching issue is likely getting triggered from the |
@ericholguin I believe I found a way to fix it! diff --git a/damus/Views/GIFs/NostrBuildGIFGrid.swift b/damus/Views/GIFs/NostrBuildGIFGrid.swift
index b8319903..29604724 100644
--- a/damus/Views/GIFs/NostrBuildGIFGrid.swift
+++ b/damus/Views/GIFs/NostrBuildGIFGrid.swift
@@ -82,6 +82,7 @@ struct NostrBuildGIFGrid: View {
if let url = URL(string: gifResult.url.wrappedValue) {
ZStack {
KFAnimatedImage(url)
+ .imageContext(.note, disable_animation: damus_state.settings.disable_animation)
.cancelOnDisappear(true)
.configure { view in
view.framePreloadCount = 3 This seems to do the trick for me. My test: Device: iPhone 15 simulator
|
Furthermore, if we wanted to fix GIF animation in the diff --git a/damus/Views/GIFs/NostrBuildGIFGrid.swift b/damus/Views/GIFs/NostrBuildGIFGrid.swift
index 29604724..cae33271 100644
--- a/damus/Views/GIFs/NostrBuildGIFGrid.swift
+++ b/damus/Views/GIFs/NostrBuildGIFGrid.swift
@@ -87,6 +87,7 @@ struct NostrBuildGIFGrid: View {
.configure { view in
view.framePreloadCount = 3
}
+// .imageModifier(ImageHandler(handler: $image))
.clipShape(RoundedRectangle(cornerRadius: 12.0))
.frame(width: 120, height: 120)
.aspectRatio(contentMode: .fill)
diff --git a/damus/Views/PostView.swift b/damus/Views/PostView.swift
index 3b64b9ea..b8daae9a 100644
--- a/damus/Views/PostView.swift
+++ b/damus/Views/PostView.swift
@@ -7,6 +7,7 @@
import SwiftUI
import AVFoundation
+import Kingfisher
enum NostrPostResult {
case post(NostrPost)
@@ -335,7 +336,7 @@ struct PostView: View {
}
let blurhash = await blurhash
let meta = blurhash.map { bh in calculate_image_metadata(url: url, img: img, blurhash: bh) }
- let uploadedMedia = UploadedMedia(localURL: media.localURL, uploadedURL: url, representingImage: img, metadata: meta)
+ let uploadedMedia = UploadedMedia(localURL: media.localURL, uploadedURL: url, metadata: meta)
uploadedMedias.append(uploadedMedia)
case .failed(let error):
@@ -468,12 +469,13 @@ struct PostView: View {
return
}
self.preUploadedMedia = PreUploadedMedia.processed_image(url)
- if let media = generateMediaUpload(preUploadedMedia) {
- let img = getImage(media: media)
+ let fakeUploadedMedia = PreUploadedMedia.processed_image(URL(string: "https://image.nostr.build/51ccc5f8fc4ab5a70b2094afc8a3c5d0332ae53a90a8337a663062efecc4e000.gif")!)
+ if let media = generateMediaUpload(preUploadedMedia), let fakeMedia = generateMediaUpload(fakeUploadedMedia) {
+ let img = getImage(media: fakeMedia)
Task {
async let blurhash = calculate_blurhash(img: img)
let meta = await blurhash.map { bh in calculate_image_metadata(url: url, img: img, blurhash: bh) }
- let uploadedMedia = UploadedMedia(localURL: media.localURL, uploadedURL: url, representingImage: img, metadata: meta)
+ let uploadedMedia = UploadedMedia(localURL: media.localURL, uploadedURL: url, metadata: meta)
uploadedMedias.append(uploadedMedia)
}
}
@@ -543,21 +545,18 @@ struct PVImageCarouselView: View {
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack {
- ForEach(media.map({$0.representingImage}), id: \.self) { image in
+ ForEach($media, id: \.self) { media_item in
ZStack(alignment: .topTrailing) {
- Image(uiImage: image)
- .resizable()
+ KFAnimatedImage(media_item.uploadedURL.wrappedValue)
.aspectRatio(contentMode: .fill)
.frame(width: media.count == 1 ? deviceWidth*0.8 : 250, height: media.count == 1 ? 400 : 250)
.cornerRadius(10)
.padding()
.contextMenu {
- if let uploadedURL = media.first(where: { $0.representingImage == image })?.uploadedURL {
- Button(action: {
- UIPasteboard.general.string = uploadedURL.absoluteString
- }) {
- Label(NSLocalizedString("Copy URL", comment: "Label for button in context menu to copy URL of the selected uploaded media asset."), image: "copy")
- }
+ Button(action: {
+ UIPasteboard.general.string = media_item.uploadedURL.wrappedValue.absoluteString
+ }) {
+ Label(NSLocalizedString("Copy URL", comment: "Label for button in context menu to copy URL of the selected uploaded media asset."), image: "copy")
}
}
Image("close-circle")
@@ -565,7 +564,7 @@ struct PVImageCarouselView: View {
.padding(20)
.shadow(radius: 5)
.onTapGesture {
- if let index = media.map({$0.representingImage}).firstIndex(of: image) {
+ if let index = media.map({$0.uploadedURL}).firstIndex(of: media_item.uploadedURL.wrappedValue) {
media.remove(at: index)
}
}
@@ -611,11 +610,15 @@ fileprivate func getImage(media: MediaUpload) -> UIImage {
return uiimage
}
-struct UploadedMedia: Equatable {
+struct UploadedMedia: Equatable, Hashable {
let localURL: URL
- let uploadedURL: URL
- let representingImage: UIImage
+ var uploadedURL: URL
let metadata: ImageMetadata?
+
+ func hash(into hasher: inout Hasher) {
+ hasher.combine(localURL)
+ hasher.combine(uploadedURL)
+ }
}
However, I am not sure why we needed |
@ericholguin btw the new GIF keyboard functionality is very cool 🔥🔥! I hope my comments are helpful. Please let me know if you have any questions! |
this is fine but I would like to reiterate the same issue I had with adding nostr.build in the first place. once it goes away half of our app will be broken (image uploads, gif keyboard) of course we can comb over this now but the proper solution is using protocols not platforms:
|
This PR adds a gif keyboard, kind of, to the posting view. Leverages the nostr build API to get latest gifs. Changelog-Added: Nostr Build GIF keyboard Signed-off-by: ericholguin <[email protected]>
2a4f411
to
5caec85
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
LGTM! Handing over to @jb55 if he has any concerns about these changes |
if this is near/ready, would be a fun testflight addition in the near term |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure lets try it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding this review change request as a reminder for myself that we still need to implement search and NIP-94 support before merging this PR (from our standup discussions)
Please feel free to correct me if I am wrong. Thank you!
@fishcakeday yall adding search this quarter 👀 |
On Fri, Oct 04, 2024 at 10:46:18AM -0700, Daniel D’Aquino wrote:
@danieldaquino requested changes on this pull request.
Adding this review change request as a reminder for myself that we still need to implement search and NIP-94 support before merging this PR (from our standup discussions)
Please feel free to correct me if I am wrong. Thank you!
if we're doing a centralized integration I don't see why we don't just
use tenor or something
|
This PR adds a gif keyboard, kind of, to the posting view. Leverages the nostr build API to get latest gifs.
Changelog-Added: Nostr Build GIF keyboard