damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit 398630863891871e0fda9a71627d2893b02b6b97
parent fa7740948b6b6498bfa2994d3bbe0baf00b55328
Author: Swift Coder <scoder1747@gmail.com>
Date:   Wed, 11 Dec 2024 13:38:59 -0500

Render Gif and video files while composing posts
Changelog-Added: Render Gif and video files while composing posts
Signed-off-by: Swift Coder <scoder1747@gmail.com>

Diffstat:
Mdamus/Views/PostView.swift | 103++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 78 insertions(+), 25 deletions(-)

diff --git a/damus/Views/PostView.swift b/damus/Views/PostView.swift @@ -6,7 +6,8 @@ // import SwiftUI -import AVFoundation +import AVKit +import Kingfisher enum NostrPostResult { case post(NostrPost) @@ -609,38 +610,79 @@ struct PVImageCarouselView: View { var body: some View { ScrollView(.horizontal, showsIndicators: false) { HStack { - ForEach(media.map({$0.representingImage}), id: \.self) { image in - ZStack(alignment: .topTrailing) { - Image(uiImage: image) - .resizable() - .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") - } + ForEach(media.indices, id: \.self) { index in + ZStack(alignment: .topLeading) { + if isSupportedVideo(url: media[index].uploadedURL) { + VideoPlayer(player: configurePlayer(with: media[index].localURL)) + .frame(width: media.count == 1 ? deviceWidth * 0.8 : 250, height: media.count == 1 ? 400 : 250) + .cornerRadius(10) + .padding() + .contextMenu { contextMenuContent(for: media[index]) } + } else if is_animated_image(url: media[index].uploadedURL) { + KFAnimatedImage(media[index].uploadedURL) + .imageContext(.note, disable_animation: false) + .configure { view in + view.framePreloadCount = 3 } - } - Image("close-circle") - .foregroundColor(.white) - .padding(20) - .shadow(radius: 5) - .onTapGesture { - if let index = media.map({$0.representingImage}).firstIndex(of: image) { - media.remove(at: index) + .frame(width: media.count == 1 ? deviceWidth * 0.8 : 250, height: media.count == 1 ? 400 : 250) + .cornerRadius(10) + .padding() + .contextMenu { contextMenuContent(for: media[index]) } + } else { + Image(uiImage: media[index].representingImage) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: media.count == 1 ? deviceWidth * 0.8 : 250, height: media.count == 1 ? 400 : 250) + .cornerRadius(10) + .padding() + .contextMenu { contextMenuContent(for: media[index]) } + } + + VStack { // Set spacing to 0 to remove the gap between items + Image("close-circle") + .foregroundColor(.white) + .padding(20) + .shadow(radius: 5) + .onTapGesture { + media.remove(at: index) // Direct removal using index } - } + + if isSupportedVideo(url: media[index].uploadedURL) { + Spacer() + Image(systemName: "video") + .foregroundColor(.white) + .padding(10) + .shadow(radius: 5) + .opacity(0.6) + } + } + .padding(.bottom, 35) } } } .padding() } } + + // Helper Function for Context Menu + @ViewBuilder + private func contextMenuContent(for mediaItem: UploadedMedia) -> some View { + Button(action: { + UIPasteboard.general.string = mediaItem.uploadedURL.absoluteString + }) { + Label( + NSLocalizedString("Copy URL", comment: "Copy URL of the selected uploaded media asset."), + systemImage: "doc.on.doc" + ) + } + } + + private func configurePlayer(with url: URL) -> AVPlayer { + let player = AVPlayer(url: url) + player.allowsExternalPlayback = false + player.usesExternalPlaybackWhileExternalScreenIsActive = false + return player + } } fileprivate func getImage(media: MediaUpload) -> UIImage { @@ -813,3 +855,14 @@ func build_post(state: DamusState, post: NSMutableAttributedString, action: Post return NostrPost(content: content, kind: .text, tags: tags) } +func isSupportedVideo(url: URL?) -> Bool { + guard let url = url else { return false } + let fileExtension = url.pathExtension.lowercased() + let supportedUTIs = AVURLAsset.audiovisualTypes().map { $0.rawValue } + return supportedUTIs.contains { utiString in + if let utType = UTType(utiString), let fileUTType = UTType(filenameExtension: fileExtension) { + return fileUTType.conforms(to: utType) + } + return false + } +}