IceCubesApp/Packages/Status/Sources/Status/Row/StatusMediaPreviewView.swift

184 lines
5 KiB
Swift
Raw Normal View History

2022-12-17 12:37:46 +00:00
import SwiftUI
import Models
2022-12-19 15:01:23 +00:00
import AVKit
2022-12-22 09:53:36 +00:00
import Env
2022-12-22 18:00:23 +00:00
import Shimmer
2022-12-17 12:37:46 +00:00
2022-12-22 06:00:35 +00:00
private class VideoPlayerViewModel: ObservableObject {
@Published var player: AVPlayer?
private let url: URL
init(url: URL) {
self.url = url
}
func preparePlayer() {
player = .init(url: url)
player?.play()
guard let player else { return }
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime,
object: player.currentItem, queue: .main) { [weak self] _ in
self?.player?.seek(to: CMTime.zero)
self?.player?.play()
}
}
deinit {
NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: self.player)
2022-12-22 06:00:35 +00:00
}
}
private struct VideoPlayerView: View {
@StateObject var viewModel: VideoPlayerViewModel
var body: some View {
VStack {
VideoPlayer(player: viewModel.player)
}.onAppear {
viewModel.preparePlayer()
}
}
}
2022-12-19 16:18:16 +00:00
// Could have just been a state, but SwiftUI .sheet is buggy ATM without @StateObject
2022-12-22 06:00:35 +00:00
private class SelectedMediaSheetManager: ObservableObject {
2022-12-19 16:18:16 +00:00
@Published var selectedAttachement: MediaAttachement?
}
2022-12-17 12:37:46 +00:00
public struct StatusMediaPreviewView: View {
2022-12-22 09:53:36 +00:00
@EnvironmentObject private var quickLook: QuickLook
2022-12-17 12:37:46 +00:00
public let attachements: [MediaAttachement]
2022-12-19 16:18:16 +00:00
@StateObject private var selectedMediaSheetManager = SelectedMediaSheetManager()
2022-12-22 09:53:36 +00:00
@State private var isQuickLookLoading: Bool = false
private var imageMaxHeight: CGFloat {
if attachements.count == 1 {
return 300
}
return attachements.count > 2 ? 100 : 200
}
2022-12-17 12:37:46 +00:00
public var body: some View {
Group {
if attachements.count == 1, let attachement = attachements.first {
makeFeaturedImagePreview(attachement: attachement)
.onTapGesture {
Task {
await quickLook.prepareFor(urls: attachements.map{ $0.url }, selectedURL: attachement.url)
}
}
} else {
VStack {
HStack {
if let firstAttachement = attachements.first {
makePreview(attachement: firstAttachement)
}
if attachements.count > 1, let secondAttachement = attachements[1] {
makePreview(attachement: secondAttachement)
}
}
HStack {
if attachements.count > 2, let secondAttachement = attachements[2] {
makePreview(attachement: secondAttachement)
}
if attachements.count > 3, let secondAttachement = attachements[3] {
makePreview(attachement: secondAttachement)
}
}
2022-12-17 12:37:46 +00:00
}
}
}
2022-12-22 09:53:36 +00:00
.overlay {
if quickLook.isPreparing {
quickLookLoadingView
2022-12-22 09:53:36 +00:00
.transition(.opacity)
}
2022-12-19 16:18:16 +00:00
}
}
@ViewBuilder
private func makeFeaturedImagePreview(attachement: MediaAttachement) -> some View {
switch attachement.supportedType {
case .image:
AsyncImage(
url: attachement.url,
content: { image in
image
.resizable()
.aspectRatio(contentMode: .fill)
.cornerRadius(4)
},
placeholder: {
RoundedRectangle(cornerRadius: 4)
.fill(Color.gray)
.frame(height: imageMaxHeight)
.shimmering()
}
)
case .gifv:
VideoPlayerView(viewModel: .init(url: attachement.url))
.frame(height: imageMaxHeight)
case .none:
EmptyView()
}
2022-12-19 16:18:16 +00:00
}
@ViewBuilder
private func makePreview(attachement: MediaAttachement) -> some View {
if let type = attachement.supportedType {
2022-12-20 07:14:57 +00:00
Group {
GeometryReader { proxy in
switch type {
case .image:
2022-12-19 16:18:16 +00:00
AsyncImage(
url: attachement.url,
content: { image in
image
.resizable()
.aspectRatio(contentMode: attachements.count == 1 ? .fit : .fill)
.frame(height: imageMaxHeight)
2022-12-19 16:18:16 +00:00
.frame(width: proxy.frame(in: .local).width)
.cornerRadius(4)
2022-12-19 16:18:16 +00:00
},
placeholder: {
2022-12-22 18:00:23 +00:00
RoundedRectangle(cornerRadius: 4)
.fill(Color.gray)
.frame(maxHeight: imageMaxHeight)
2022-12-22 18:00:23 +00:00
.frame(width: proxy.frame(in: .local).width)
.shimmering()
2022-12-19 16:18:16 +00:00
}
)
2022-12-20 07:14:57 +00:00
case .gifv:
2022-12-22 06:00:35 +00:00
VideoPlayerView(viewModel: .init(url: attachement.url))
2022-12-20 07:14:57 +00:00
.frame(width: proxy.frame(in: .local).width)
.frame(height: imageMaxHeight)
2022-12-19 16:18:16 +00:00
}
}
.frame(height: imageMaxHeight)
2022-12-20 07:14:57 +00:00
}
.onTapGesture {
2022-12-22 09:53:36 +00:00
Task {
await quickLook.prepareFor(urls: attachements.map{ $0.url }, selectedURL: attachement.url)
}
2022-12-17 12:37:46 +00:00
}
2022-12-19 15:01:23 +00:00
}
2022-12-17 12:37:46 +00:00
}
2022-12-19 18:04:07 +00:00
2022-12-22 09:53:36 +00:00
private var quickLookLoadingView: some View {
ZStack(alignment: .center) {
VStack {
Spacer()
HStack {
Spacer()
ProgressView()
Spacer()
2022-12-19 18:04:07 +00:00
}
2022-12-22 09:53:36 +00:00
Spacer()
2022-12-19 18:04:07 +00:00
}
}
2022-12-22 09:53:36 +00:00
.background(.ultraThinMaterial)
2022-12-19 18:04:07 +00:00
}
2022-12-17 12:37:46 +00:00
}