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:
parent
f1f3abfb98
commit
3569da5687
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user