1
0
mirror of git://jb55.com/damus synced 2024-09-16 02:03:45 +00:00

video: switch player to use new view model

pass VideoController through containing views

Closes: https://github.com/damus-io/damus/pull/1539
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
Bryan Montz 2023-09-06 11:29:45 -05:00 committed by William Casarin
parent f1f3abfb98
commit 3569da5687
4 changed files with 69 additions and 67 deletions

View File

@ -112,10 +112,6 @@ struct ImageCarousel: View {
}
}
func video_model(_ url: URL) -> VideoPlayerModel {
return state.events.get_video_player_model(url: url)
}
func Media(geo: GeometryProxy, url: MediaUrl, index: Int) -> some View {
Group {
switch url {
@ -125,7 +121,7 @@ struct ImageCarousel: View {
open_sheet = true
}
case .video(let url):
DamusVideoPlayer(url: url, model: video_model(url), video_size: $video_size)
DamusVideoPlayer(url: url, video_size: $video_size, controller: state.video)
.onChange(of: video_size) { size in
guard let size else { return }
@ -194,7 +190,7 @@ struct ImageCarousel: View {
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.fullScreenCover(isPresented: $open_sheet) {
ImageView(cache: state.events, urls: urls, disable_animation: state.settings.disable_animation)
ImageView(video_controller: state.video, urls: urls, disable_animation: state.settings.disable_animation)
}
.frame(height: height)
.onChange(of: selectedIndex) { value in

View File

@ -10,7 +10,7 @@ import Kingfisher
struct ImageContainerView: View {
let cache: EventCache
let video_controller: VideoController
let url: MediaUrl
@State private var image: UIImage?
@ -47,7 +47,7 @@ struct ImageContainerView: View {
case .image(let url):
Img(url: url)
case .video(let url):
DamusVideoPlayer(url: url, model: cache.get_video_player_model(url: url), video_size: .constant(nil))
DamusVideoPlayer(url: url, video_size: .constant(nil), controller: video_controller)
}
}
}
@ -57,6 +57,6 @@ let test_image_url = URL(string: "https://jb55.com/red-me.jpg")!
struct ImageContainerView_Previews: PreviewProvider {
static var previews: some View {
ImageContainerView(cache: test_damus_state().events, url: .image(test_image_url), disable_animation: false)
ImageContainerView(video_controller: test_damus_state().video, url: .image(test_image_url), disable_animation: false)
}
}

View File

@ -8,7 +8,7 @@
import SwiftUI
struct ImageView: View {
let cache: EventCache
let video_controller: VideoController
let urls: [MediaUrl]
@Environment(\.presentationMode) var presentationMode
@ -39,7 +39,7 @@ struct ImageView: View {
TabView(selection: $selectedIndex) {
ForEach(urls.indices, id: \.self) { index in
ZoomableScrollView {
ImageContainerView(cache: cache, url: urls[index], disable_animation: disable_animation)
ImageContainerView(video_controller: video_controller, url: urls[index], disable_animation: disable_animation)
.aspectRatio(contentMode: .fit)
.padding(.top, Theme.safeAreaInsets?.top)
.padding(.bottom, Theme.safeAreaInsets?.bottom)
@ -80,6 +80,6 @@ struct ImageView: View {
struct ImageView_Previews: PreviewProvider {
static var previews: some View {
let url: MediaUrl = .image(URL(string: "https://jb55.com/red-me.jpg")!)
ImageView(cache: test_damus_state().events, urls: [url], disable_animation: false)
ImageView(video_controller: test_damus_state().video, urls: [url], disable_animation: false)
}
}

View File

@ -17,79 +17,85 @@ func globalCoordinate(localX x: CGFloat, localY y: CGFloat,
}
struct DamusVideoPlayer: View {
var url: URL
@ObservedObject var model: VideoPlayerModel
@Binding var video_size: CGSize?
let url: URL
@StateObject var model: DamusVideoPlayerViewModel
@EnvironmentObject private var orientationTracker: OrientationTracker
var mute_icon: String {
if model.has_audio == false || model.muted {
return "speaker.slash"
} else {
return "speaker"
}
}
var mute_icon_color: Color {
switch self.model.has_audio {
case .none:
return .white
case .some(let has_audio):
return has_audio ? .white : .red
}
}
var MuteIcon: some View {
ZStack {
Circle()
.opacity(0.2)
.frame(width: 32, height: 32)
.foregroundColor(.black)
Image(systemName: mute_icon)
.padding()
.foregroundColor(mute_icon_color)
}
init(url: URL, video_size: Binding<CGSize?>, controller: VideoController) {
self.url = url
_model = StateObject(wrappedValue: DamusVideoPlayerViewModel(url: url, video_size: video_size, controller: controller))
}
var body: some View {
GeometryReader { geo in
let localFrame = geo.frame(in: .local)
let centerY = globalCoordinate(localX: 0, localY: localFrame.midY, localGeometry: geo).y
let delta = localFrame.height / 2
ZStack(alignment: .bottomTrailing) {
VideoPlayer(url: url, model: model)
if model.has_audio == true {
MuteIcon
.zIndex(11.0)
.onTapGesture {
self.model.muted = !self.model.muted
}
ZStack {
AVPlayerView(player: model.player)
if model.is_loading {
ProgressView()
.progressViewStyle(.circular)
.tint(.white)
.scaleEffect(CGSize(width: 1.5, height: 1.5))
}
}
.onChange(of: model.size) { size in
guard let size else {
return
if model.has_audio {
mute_button
}
video_size = size
}
.onChange(of: centerY) { _ in
/// pause video when it is scrolled beyond visible range
let isBelowTop = centerY + delta > 100, /// 100 =~ approx. bottom (y) of ContentView's TabView
isAboveBottom = centerY - delta < orientationTracker.deviceMajorAxis
if isBelowTop && isAboveBottom {
model.start()
} else {
model.stop()
update_is_visible(centerY: centerY)
}
.onAppear {
update_is_visible(centerY: centerY)
}
}
.onDisappear {
model.view_did_disappear()
}
}
private func update_is_visible(centerY: CGFloat) {
let isBelowTop = centerY > 100, /// 100 =~ approx. bottom (y) of ContentView's TabView
isAboveBottom = centerY < orientationTracker.deviceMajorAxis
model.set_view_is_visible(isBelowTop && isAboveBottom)
}
private var mute_icon: String {
!model.has_audio || model.is_muted ? "speaker.slash" : "speaker"
}
private var mute_icon_color: Color {
model.has_audio ? .white : .red
}
private var mute_button: some View {
HStack {
Spacer()
VStack {
Spacer()
Button {
model.did_tap_mute_button()
} label: {
ZStack {
Circle()
.opacity(0.2)
.frame(width: 32, height: 32)
.foregroundColor(.black)
Image(systemName: mute_icon)
.padding()
.foregroundColor(mute_icon_color)
}
}
}
}
}
}
struct DamusVideoPlayer_Previews: PreviewProvider {
@StateObject static var model: VideoPlayerModel = VideoPlayerModel()
static var previews: some View {
DamusVideoPlayer(url: URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!, model: model, video_size: .constant(nil))
DamusVideoPlayer(url: URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!, video_size: .constant(nil), controller: VideoController())
}
}