1
0
mirror of git://jb55.com/damus synced 2024-09-18 19:23:49 +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 { func Media(geo: GeometryProxy, url: MediaUrl, index: Int) -> some View {
Group { Group {
switch url { switch url {
@ -125,7 +121,7 @@ struct ImageCarousel: View {
open_sheet = true open_sheet = true
} }
case .video(let url): 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 .onChange(of: video_size) { size in
guard let size else { return } guard let size else { return }
@ -194,7 +190,7 @@ struct ImageCarousel: View {
} }
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.fullScreenCover(isPresented: $open_sheet) { .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) .frame(height: height)
.onChange(of: selectedIndex) { value in .onChange(of: selectedIndex) { value in

View File

@ -10,7 +10,7 @@ import Kingfisher
struct ImageContainerView: View { struct ImageContainerView: View {
let cache: EventCache let video_controller: VideoController
let url: MediaUrl let url: MediaUrl
@State private var image: UIImage? @State private var image: UIImage?
@ -47,7 +47,7 @@ struct ImageContainerView: View {
case .image(let url): case .image(let url):
Img(url: url) Img(url: url)
case .video(let 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 { struct ImageContainerView_Previews: PreviewProvider {
static var previews: some View { 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 import SwiftUI
struct ImageView: View { struct ImageView: View {
let cache: EventCache let video_controller: VideoController
let urls: [MediaUrl] let urls: [MediaUrl]
@Environment(\.presentationMode) var presentationMode @Environment(\.presentationMode) var presentationMode
@ -39,7 +39,7 @@ struct ImageView: View {
TabView(selection: $selectedIndex) { TabView(selection: $selectedIndex) {
ForEach(urls.indices, id: \.self) { index in ForEach(urls.indices, id: \.self) { index in
ZoomableScrollView { 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) .aspectRatio(contentMode: .fit)
.padding(.top, Theme.safeAreaInsets?.top) .padding(.top, Theme.safeAreaInsets?.top)
.padding(.bottom, Theme.safeAreaInsets?.bottom) .padding(.bottom, Theme.safeAreaInsets?.bottom)
@ -80,6 +80,6 @@ struct ImageView: View {
struct ImageView_Previews: PreviewProvider { struct ImageView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
let url: MediaUrl = .image(URL(string: "https://jb55.com/red-me.jpg")!) 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 { struct DamusVideoPlayer: View {
var url: URL let url: URL
@ObservedObject var model: VideoPlayerModel @StateObject var model: DamusVideoPlayerViewModel
@Binding var video_size: CGSize?
@EnvironmentObject private var orientationTracker: OrientationTracker @EnvironmentObject private var orientationTracker: OrientationTracker
var mute_icon: String { init(url: URL, video_size: Binding<CGSize?>, controller: VideoController) {
if model.has_audio == false || model.muted { self.url = url
return "speaker.slash" _model = StateObject(wrappedValue: DamusVideoPlayerViewModel(url: url, video_size: video_size, controller: controller))
} 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)
}
} }
var body: some View { var body: some View {
GeometryReader { geo in GeometryReader { geo in
let localFrame = geo.frame(in: .local) let localFrame = geo.frame(in: .local)
let centerY = globalCoordinate(localX: 0, localY: localFrame.midY, localGeometry: geo).y let centerY = globalCoordinate(localX: 0, localY: localFrame.midY, localGeometry: geo).y
let delta = localFrame.height / 2 ZStack {
ZStack(alignment: .bottomTrailing) { AVPlayerView(player: model.player)
VideoPlayer(url: url, model: model)
if model.has_audio == true { if model.is_loading {
MuteIcon ProgressView()
.zIndex(11.0) .progressViewStyle(.circular)
.onTapGesture { .tint(.white)
self.model.muted = !self.model.muted .scaleEffect(CGSize(width: 1.5, height: 1.5))
}
} }
}
.onChange(of: model.size) { size in if model.has_audio {
guard let size else { mute_button
return
} }
video_size = size
} }
.onChange(of: centerY) { _ in .onChange(of: centerY) { _ in
/// pause video when it is scrolled beyond visible range update_is_visible(centerY: centerY)
let isBelowTop = centerY + delta > 100, /// 100 =~ approx. bottom (y) of ContentView's TabView }
isAboveBottom = centerY - delta < orientationTracker.deviceMajorAxis .onAppear {
if isBelowTop && isAboveBottom { update_is_visible(centerY: centerY)
model.start() }
} else { }
model.stop() .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 { struct DamusVideoPlayer_Previews: PreviewProvider {
@StateObject static var model: VideoPlayerModel = VideoPlayerModel()
static var previews: some View { 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())
} }
} }