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:
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
+ }
+}