damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

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:
Mdamus/Components/ImageCarousel.swift | 8++------
Mdamus/Views/Images/ImageContainerView.swift | 6+++---
Mdamus/Views/Images/ImageView.swift | 6+++---
Mdamus/Views/Video/DamusVideoPlayer.swift | 116++++++++++++++++++++++++++++++++++++++++++-------------------------------------
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()) } }