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:
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 {
|
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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user