commit 3569da568776dd2e015ca4130c23813c8889d71d
parent f1f3abfb98da7bd1f88defab3dc49199ab68e763
Author: Bryan Montz <bryanmontz@me.com>
Date: Wed, 6 Sep 2023 11:29:45 -0500
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>
Diffstat:
4 files changed, 69 insertions(+), 67 deletions(-)
diff --git a/damus/Components/ImageCarousel.swift b/damus/Components/ImageCarousel.swift
@@ -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
diff --git a/damus/Views/Images/ImageContainerView.swift b/damus/Views/Images/ImageContainerView.swift
@@ -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)
}
}
diff --git a/damus/Views/Images/ImageView.swift b/damus/Views/Images/ImageView.swift
@@ -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)
}
}
diff --git a/damus/Views/Video/DamusVideoPlayer.swift b/damus/Views/Video/DamusVideoPlayer.swift
@@ -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())
}
}