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>
This commit is contained in:
committed by
Daniel D’Aquino
parent
fa7740948b
commit
3986308638
@@ -6,7 +6,8 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import AVFoundation
|
import AVKit
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
enum NostrPostResult {
|
enum NostrPostResult {
|
||||||
case post(NostrPost)
|
case post(NostrPost)
|
||||||
@@ -609,38 +610,79 @@ struct PVImageCarouselView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
HStack {
|
HStack {
|
||||||
ForEach(media.map({$0.representingImage}), id: \.self) { image in
|
ForEach(media.indices, id: \.self) { index in
|
||||||
ZStack(alignment: .topTrailing) {
|
ZStack(alignment: .topLeading) {
|
||||||
Image(uiImage: image)
|
if isSupportedVideo(url: media[index].uploadedURL) {
|
||||||
.resizable()
|
VideoPlayer(player: configurePlayer(with: media[index].localURL))
|
||||||
.aspectRatio(contentMode: .fill)
|
.frame(width: media.count == 1 ? deviceWidth * 0.8 : 250, height: media.count == 1 ? 400 : 250)
|
||||||
.frame(width: media.count == 1 ? deviceWidth*0.8 : 250, height: media.count == 1 ? 400 : 250)
|
.cornerRadius(10)
|
||||||
.cornerRadius(10)
|
.padding()
|
||||||
.padding()
|
.contextMenu { contextMenuContent(for: media[index]) }
|
||||||
.contextMenu {
|
} else if is_animated_image(url: media[index].uploadedURL) {
|
||||||
if let uploadedURL = media.first(where: { $0.representingImage == image })?.uploadedURL {
|
KFAnimatedImage(media[index].uploadedURL)
|
||||||
Button(action: {
|
.imageContext(.note, disable_animation: false)
|
||||||
UIPasteboard.general.string = uploadedURL.absoluteString
|
.configure { view in
|
||||||
}) {
|
view.framePreloadCount = 3
|
||||||
Label(NSLocalizedString("Copy URL", comment: "Label for button in context menu to copy URL of the selected uploaded media asset."), image: "copy")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
.frame(width: media.count == 1 ? deviceWidth * 0.8 : 250, height: media.count == 1 ? 400 : 250)
|
||||||
Image("close-circle")
|
.cornerRadius(10)
|
||||||
.foregroundColor(.white)
|
.padding()
|
||||||
.padding(20)
|
.contextMenu { contextMenuContent(for: media[index]) }
|
||||||
.shadow(radius: 5)
|
} else {
|
||||||
.onTapGesture {
|
Image(uiImage: media[index].representingImage)
|
||||||
if let index = media.map({$0.representingImage}).firstIndex(of: image) {
|
.resizable()
|
||||||
media.remove(at: index)
|
.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()
|
.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 {
|
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)
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user