diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index de4189af..919fb273 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -377,7 +377,7 @@ 4CFD502F2A2DA45800A229DB /* MediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFD502E2A2DA45800A229DB /* MediaView.swift */; }; 4CFF8F5929C9FD1E008DB934 /* DamusPurpleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F5829C9FD1E008DB934 /* DamusPurpleView.swift */; }; 4CFF8F6329CC9AD7008DB934 /* ImageContextMenuModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */; }; - 4CFF8F6729CC9E3A008DB934 /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6629CC9E3A008DB934 /* ImageView.swift */; }; + 4CFF8F6729CC9E3A008DB934 /* FullScreenCarouselView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6629CC9E3A008DB934 /* FullScreenCarouselView.swift */; }; 4CFF8F6929CC9ED1008DB934 /* ImageContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */; }; 4CFF8F6B29CD0079008DB934 /* RepostedEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6A29CD0079008DB934 /* RepostedEvent.swift */; }; 4CFF8F6D29CD022E008DB934 /* WideEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6C29CD022E008DB934 /* WideEventView.swift */; }; @@ -388,7 +388,7 @@ 504323A72A34915F006AE6DC /* RelayModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504323A62A34915F006AE6DC /* RelayModel.swift */; }; 504323A92A3495B6006AE6DC /* RelayModelCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504323A82A3495B6006AE6DC /* RelayModelCache.swift */; }; 5053ACA72A56DF3B00851AE3 /* DeveloperSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */; }; - 50A16FFB2AA6C06600DFEC1F /* AVPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFA2AA6C06600DFEC1F /* AVPlayerView.swift */; }; + 50A16FFB2AA6C06600DFEC1F /* DamusAVPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */; }; 50A16FFD2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */; }; 50A16FFF2AA76A0900DFEC1F /* VideoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFE2AA76A0900DFEC1F /* VideoController.swift */; }; 50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; }; @@ -433,8 +433,6 @@ B59CAD4D2B688D1000677E8B /* MutelistManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B533694D2B66D791008A805E /* MutelistManager.swift */; }; B5A75C2A2B546D94007AFBC0 /* MuteItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A75C292B546D94007AFBC0 /* MuteItemTests.swift */; }; B5B4D1432B37D47600844320 /* NdbExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B4D1422B37D47600844320 /* NdbExtensions.swift */; }; - BA0F0A6F2B36207E001641B2 /* CameraMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA0F0A6E2B36207E001641B2 /* CameraMediaView.swift */; }; - BA10192F2B449556009C57DA /* CameraPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA10192E2B449556009C57DA /* CameraPreview.swift */; }; B5C60C202B530D5100C5ECA7 /* MuteItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C60C1F2B530D5100C5ECA7 /* MuteItem.swift */; }; B5C60C212B530D5600C5ECA7 /* MuteItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C60C1F2B530D5100C5ECA7 /* MuteItem.swift */; }; B5C60C232B532A8700C5ECA7 /* DamusDuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C60C222B532A8700C5ECA7 /* DamusDuration.swift */; }; @@ -1300,7 +1298,7 @@ 4CFD502E2A2DA45800A229DB /* MediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaView.swift; sourceTree = ""; }; 4CFF8F5829C9FD1E008DB934 /* DamusPurpleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleView.swift; sourceTree = ""; }; 4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContextMenuModifier.swift; sourceTree = ""; }; - 4CFF8F6629CC9E3A008DB934 /* ImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageView.swift; sourceTree = ""; }; + 4CFF8F6629CC9E3A008DB934 /* FullScreenCarouselView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenCarouselView.swift; sourceTree = ""; }; 4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContainerView.swift; sourceTree = ""; }; 4CFF8F6A29CD0079008DB934 /* RepostedEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepostedEvent.swift; sourceTree = ""; }; 4CFF8F6C29CD022E008DB934 /* WideEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WideEventView.swift; sourceTree = ""; }; @@ -1311,7 +1309,7 @@ 504323A62A34915F006AE6DC /* RelayModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayModel.swift; sourceTree = ""; }; 504323A82A3495B6006AE6DC /* RelayModelCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayModelCache.swift; sourceTree = ""; }; 5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettingsView.swift; sourceTree = ""; }; - 50A16FFA2AA6C06600DFEC1F /* AVPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayerView.swift; sourceTree = ""; }; + 50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusAVPlayerView.swift; sourceTree = ""; }; 50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusVideoPlayerViewModel.swift; sourceTree = ""; }; 50A16FFE2AA76A0900DFEC1F /* VideoController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoController.swift; sourceTree = ""; }; 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = ""; }; @@ -1355,8 +1353,6 @@ B57B4C652B312C3700A232C0 /* NostrAuth.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NostrAuth.swift; sourceTree = ""; }; B5A75C292B546D94007AFBC0 /* MuteItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuteItemTests.swift; sourceTree = ""; usesTabs = 0; }; B5B4D1422B37D47600844320 /* NdbExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NdbExtensions.swift; sourceTree = ""; usesTabs = 0; }; - BA0F0A6E2B36207E001641B2 /* CameraMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraMediaView.swift; sourceTree = ""; }; - BA10192E2B449556009C57DA /* CameraPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraPreview.swift; sourceTree = ""; }; B5C60C1F2B530D5100C5ECA7 /* MuteItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuteItem.swift; sourceTree = ""; usesTabs = 0; }; B5C60C222B532A8700C5ECA7 /* DamusDuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusDuration.swift; sourceTree = ""; usesTabs = 0; }; BA3759892ABCCDE30018D73B /* ImageResizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageResizer.swift; sourceTree = ""; }; @@ -1733,7 +1729,7 @@ 4C1A9A2929DDF54400516EAC /* DamusVideoPlayer.swift */, 50A16FFC2AA7525700DFEC1F /* DamusVideoPlayerViewModel.swift */, 50A16FFE2AA76A0900DFEC1F /* VideoController.swift */, - 50A16FFA2AA6C06600DFEC1F /* AVPlayerView.swift */, + 50A16FFA2AA6C06600DFEC1F /* DamusAVPlayerView.swift */, ); path = Video; sourceTree = ""; @@ -2659,7 +2655,7 @@ isa = PBXGroup; children = ( 4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */, - 4CFF8F6629CC9E3A008DB934 /* ImageView.swift */, + 4CFF8F6629CC9E3A008DB934 /* FullScreenCarouselView.swift */, 6439E013296790CF0020672B /* ProfilePicImageView.swift */, 4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */, 4CFD502E2A2DA45800A229DB /* MediaView.swift */, @@ -2744,14 +2740,6 @@ path = DamusNotificationService; sourceTree = ""; }; - E06336A72B7582D600A88E6B /* Assets */ = { - isa = PBXGroup; - children = ( - E06336A82B7582E000A88E6B /* img_with_location.jpeg */, - ); - path = Assets; - sourceTree = ""; - }; D7CBD1D22B8D21C100BFD889 /* Extensions */ = { isa = PBXGroup; children = ( @@ -2760,6 +2748,14 @@ path = Extensions; sourceTree = ""; }; + E06336A72B7582D600A88E6B /* Assets */ = { + isa = PBXGroup; + children = ( + E06336A82B7582E000A88E6B /* img_with_location.jpeg */, + ); + path = Assets; + sourceTree = ""; + }; F71694E82A66221E001F4053 /* Onboarding */ = { isa = PBXGroup; children = ( @@ -3178,7 +3174,7 @@ 4C363A9A28283854006E126D /* Reply.swift in Sources */, BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */, D7ADD3E02B538D4200F104C4 /* DamusPurpleURLSheetView.swift in Sources */, - 4CFF8F6729CC9E3A008DB934 /* ImageView.swift in Sources */, + 4CFF8F6729CC9E3A008DB934 /* FullScreenCarouselView.swift in Sources */, 4CA927632A290EB10098A105 /* EventTop.swift in Sources */, 4C90BD18283A9EE5008EE7EF /* LoginView.swift in Sources */, 4CB8838B296F6E1E00DC99E7 /* NIP05Badge.swift in Sources */, @@ -3416,7 +3412,7 @@ 4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */, 4C9BB83129C0ED4F00FC4E37 /* DisplayName.swift in Sources */, 7CFF6317299FEFE5005D382A /* SelectableText.swift in Sources */, - 50A16FFB2AA6C06600DFEC1F /* AVPlayerView.swift in Sources */, + 50A16FFB2AA6C06600DFEC1F /* DamusAVPlayerView.swift in Sources */, 4CA352A82A76B37E003BB08B /* NewMutesNotify.swift in Sources */, 4CFF8F6929CC9ED1008DB934 /* ImageContainerView.swift in Sources */, 7527271E2A93FF0100214108 /* Block.swift in Sources */, diff --git a/damus/Components/ImageCarousel.swift b/damus/Components/ImageCarousel.swift index 4d23ef7a..20be57cb 100644 --- a/damus/Components/ImageCarousel.swift +++ b/damus/Components/ImageCarousel.swift @@ -132,7 +132,7 @@ struct ImageCarousel: View { model.open_sheet = true } case .video(let url): - DamusVideoPlayer(url: url, video_size: $model.video_size, controller: state.video) + DamusVideoPlayer(url: url, video_size: $model.video_size, controller: state.video, style: .preview(on_tap: { model.open_sheet = true })) .onChange(of: model.video_size) { size in guard let size else { return } @@ -201,7 +201,7 @@ struct ImageCarousel: View { } .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) .fullScreenCover(isPresented: $model.open_sheet) { - ImageView(video_controller: state.video, urls: urls, settings: state.settings, selectedIndex: $model.selectedIndex) + FullScreenCarouselView(video_controller: state.video, urls: urls, settings: state.settings, selectedIndex: $model.selectedIndex) } .frame(height: height) .onChange(of: model.selectedIndex) { value in @@ -296,7 +296,9 @@ public struct ImageFill { struct ImageCarousel_Previews: PreviewProvider { static var previews: some View { let url: MediaUrl = .image(URL(string: "https://jb55.com/red-me.jpg")!) - ImageCarousel(state: test_damus_state, evid: test_note.id, urls: [url, url]) + let test_video_url: MediaUrl = .video(URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!) + ImageCarousel(state: test_damus_state, evid: test_note.id, urls: [test_video_url, url]) + .environmentObject(OrientationTracker()) } } diff --git a/damus/Views/Images/FullScreenCarouselView.swift b/damus/Views/Images/FullScreenCarouselView.swift new file mode 100644 index 00000000..38290bf5 --- /dev/null +++ b/damus/Views/Images/FullScreenCarouselView.swift @@ -0,0 +1,127 @@ +// +// FullScreenCarouselView.swift +// damus +// +// Created by William Casarin on 2023-03-23. +// + +import SwiftUI + +struct FullScreenCarouselView: View { + let video_controller: VideoController + let urls: [MediaUrl] + + @Environment(\.presentationMode) var presentationMode + + @State var showMenu = true + + let settings: UserSettingsStore + @Binding var selectedIndex: Int + + var tabViewIndicator: some View { + HStack(spacing: 10) { + ForEach(urls.indices, id: \.self) { index in + Capsule() + .fill(index == selectedIndex ? Color.white : Color.damusMediumGrey) + .frame(width: 7, height: 7) + .onTapGesture { + selectedIndex = index + } + } + } + .padding() + .clipShape(Capsule()) + } + + var background: some ShapeStyle { + if case .video = urls[safe: selectedIndex] { + return AnyShapeStyle(Color.black) + } + else { + return AnyShapeStyle(.regularMaterial) + } + } + + var background_color: UIColor { + return .black + } + + var body: some View { + ZStack { + Color(self.background_color) + .ignoresSafeArea() + + TabView(selection: $selectedIndex) { + ForEach(urls.indices, id: \.self) { index in + VStack { + if case .video = urls[safe: index] { + ImageContainerView(video_controller: video_controller, url: urls[index], settings: settings) + .clipped() // SwiftUI hack from https://stackoverflow.com/a/74401288 to make playback controls show up within the TabView + .aspectRatio(contentMode: .fit) + .padding(.top, Theme.safeAreaInsets?.top) + .padding(.bottom, Theme.safeAreaInsets?.bottom) + .modifier(SwipeToDismissModifier(minDistance: 50, onDismiss: { + presentationMode.wrappedValue.dismiss() + })) + .ignoresSafeArea() + } + else { + ZoomableScrollView { + ImageContainerView(video_controller: video_controller, url: urls[index], settings: settings) + .aspectRatio(contentMode: .fit) + .padding(.top, Theme.safeAreaInsets?.top) + .padding(.bottom, Theme.safeAreaInsets?.bottom) + } + .modifier(SwipeToDismissModifier(minDistance: 50, onDismiss: { + presentationMode.wrappedValue.dismiss() + })) + .ignoresSafeArea() + } + }.tag(index) + } + } + .ignoresSafeArea() + .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) + .gesture(TapGesture(count: 2).onEnded { + // Prevents menu from hiding on double tap + }) + .gesture(TapGesture(count: 1).onEnded { + showMenu.toggle() + }) + .overlay( + GeometryReader { geo in + VStack { + if showMenu { + NavDismissBarView(showBackgroundCircle: false) + .foregroundColor(.white) + Spacer() + + if (urls.count > 1) { + tabViewIndicator + } + } + } + .animation(.easeInOut, value: showMenu) + .padding(.bottom, geo.safeAreaInsets.bottom == 0 ? 12 : 0) + } + ) + } + } +} + +fileprivate struct ImageViewPreview: View { + @State var selectedIndex: Int = 0 + let url: MediaUrl = .image(URL(string: "https://jb55.com/red-me.jpg")!) + let test_video_url: MediaUrl = .video(URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!) + + var body: some View { + FullScreenCarouselView(video_controller: test_damus_state.video, urls: [test_video_url, url], settings: test_damus_state.settings, selectedIndex: $selectedIndex) + .environmentObject(OrientationTracker()) + } +} + +struct ImageView_Previews: PreviewProvider { + static var previews: some View { + ImageViewPreview() + } +} diff --git a/damus/Views/Images/ImageContainerView.swift b/damus/Views/Images/ImageContainerView.swift index 094d8753..5c697e4d 100644 --- a/damus/Views/Images/ImageContainerView.swift +++ b/damus/Views/Images/ImageContainerView.swift @@ -43,19 +43,26 @@ struct ImageContainerView: View { var body: some View { Group { switch url { - case .image(let url): - Img(url: url) - case .video(let url): - DamusVideoPlayer(url: url, video_size: .constant(nil), controller: video_controller) + case .image(let url): + Img(url: url) + case .video(let url): + DamusVideoPlayer(url: url, video_size: .constant(nil), controller: video_controller, style: .full) } } } } let test_image_url = URL(string: "https://jb55.com/red-me.jpg")! +fileprivate let test_video_url = URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")! struct ImageContainerView_Previews: PreviewProvider { static var previews: some View { - ImageContainerView(video_controller: test_damus_state.video, url: .image(test_image_url), settings: test_damus_state.settings) + Group { + ImageContainerView(video_controller: test_damus_state.video, url: .image(test_image_url), settings: test_damus_state.settings) + .previewDisplayName("Image") + ImageContainerView(video_controller: test_damus_state.video, url: .video(test_video_url), settings: test_damus_state.settings) + .previewDisplayName("Video") + } + .environmentObject(OrientationTracker()) } } diff --git a/damus/Views/Images/ImageView.swift b/damus/Views/Images/ImageView.swift deleted file mode 100644 index 2a9ce50d..00000000 --- a/damus/Views/Images/ImageView.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// ImageView.swift -// damus -// -// Created by William Casarin on 2023-03-23. -// - -import SwiftUI - -struct ImageView: View { - let video_controller: VideoController - let urls: [MediaUrl] - - @Environment(\.presentationMode) var presentationMode - - @State var showMenu = true - - let settings: UserSettingsStore - @Binding var selectedIndex: Int - - var tabViewIndicator: some View { - HStack(spacing: 10) { - ForEach(urls.indices, id: \.self) { index in - Capsule() - .fill(index == selectedIndex ? Color(UIColor.label) : Color.secondary) - .frame(width: 7, height: 7) - .onTapGesture { - selectedIndex = index - } - } - } - .padding() - .background(.regularMaterial) - .clipShape(Capsule()) - } - - var body: some View { - ZStack { - Color(.systemBackground) - .ignoresSafeArea() - - TabView(selection: $selectedIndex) { - ForEach(urls.indices, id: \.self) { index in - ZoomableScrollView { - ImageContainerView(video_controller: video_controller, url: urls[index], settings: settings) - .aspectRatio(contentMode: .fit) - .padding(.top, Theme.safeAreaInsets?.top) - .padding(.bottom, Theme.safeAreaInsets?.bottom) - } - .modifier(SwipeToDismissModifier(minDistance: 50, onDismiss: { - presentationMode.wrappedValue.dismiss() - })) - .ignoresSafeArea() - .tag(index) - } - } - .ignoresSafeArea() - .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) - .gesture(TapGesture(count: 2).onEnded { - // Prevents menu from hiding on double tap - }) - .gesture(TapGesture(count: 1).onEnded { - showMenu.toggle() - }) - .overlay( - GeometryReader { geo in - VStack { - if showMenu { - NavDismissBarView() - Spacer() - - if (urls.count > 1) { - tabViewIndicator - } - } - } - .animation(.easeInOut, value: showMenu) - .padding(.bottom, geo.safeAreaInsets.bottom == 0 ? 12 : 0) - } - ) - } - } -} - -struct ImageView_Previews: PreviewProvider { - static var previews: some View { - let url: MediaUrl = .image(URL(string: "https://jb55.com/red-me.jpg")!) - ImageView(video_controller: test_damus_state.video, urls: [url], settings: test_damus_state.settings, selectedIndex: Binding.constant(0)) - } -} diff --git a/damus/Views/Images/ProfilePicImageView.swift b/damus/Views/Images/ProfilePicImageView.swift index b900813d..d7b3f56e 100644 --- a/damus/Views/Images/ProfilePicImageView.swift +++ b/damus/Views/Images/ProfilePicImageView.swift @@ -42,16 +42,27 @@ struct ProfileImageContainerView: View { struct NavDismissBarView: View { @Environment(\.presentationMode) var presentationMode + let showBackgroundCircle: Bool + + init(showBackgroundCircle: Bool = true) { + self.showBackgroundCircle = showBackgroundCircle + } var body: some View { HStack { Button(action: { presentationMode.wrappedValue.dismiss() }, label: { - Image("close") - .frame(width: 33, height: 33) - .background(.regularMaterial) - .clipShape(Circle()) + if showBackgroundCircle { + Image("close") + .frame(width: 33, height: 33) + .background(.regularMaterial) + .clipShape(Circle()) + } + else { + Image("close") + .frame(width: 33, height: 33) + } }) Spacer() diff --git a/damus/Views/Video/AVPlayerView.swift b/damus/Views/Video/DamusAVPlayerView.swift similarity index 73% rename from damus/Views/Video/AVPlayerView.swift rename to damus/Views/Video/DamusAVPlayerView.swift index 7a69aef7..a4036172 100644 --- a/damus/Views/Video/AVPlayerView.swift +++ b/damus/Views/Video/DamusAVPlayerView.swift @@ -9,12 +9,15 @@ import Foundation import AVKit import SwiftUI -struct AVPlayerView: UIViewControllerRepresentable { +struct DamusAVPlayerView: UIViewControllerRepresentable { let player: AVPlayer + var controller: AVPlayerViewController + let show_playback_controls: Bool func makeUIViewController(context: Context) -> AVPlayerViewController { - AVPlayerViewController() + self.controller.showsPlaybackControls = show_playback_controls + return self.controller } func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) { diff --git a/damus/Views/Video/DamusVideoPlayer.swift b/damus/Views/Video/DamusVideoPlayer.swift index f27253de..502987e3 100644 --- a/damus/Views/Video/DamusVideoPlayer.swift +++ b/damus/Views/Video/DamusVideoPlayer.swift @@ -20,10 +20,19 @@ struct DamusVideoPlayer: View { let url: URL @StateObject var model: DamusVideoPlayerViewModel @EnvironmentObject private var orientationTracker: OrientationTracker + let style: Style - init(url: URL, video_size: Binding, controller: VideoController) { + init(url: URL, video_size: Binding, controller: VideoController, style: Style) { self.url = url - _model = StateObject(wrappedValue: DamusVideoPlayerViewModel(url: url, video_size: video_size, controller: controller)) + let mute: Bool? + if case .full = style { + mute = false + } + else { + mute = nil + } + _model = StateObject(wrappedValue: DamusVideoPlayerViewModel(url: url, video_size: video_size, controller: controller, mute: mute)) + self.style = style } var body: some View { @@ -31,7 +40,15 @@ struct DamusVideoPlayer: View { let localFrame = geo.frame(in: .local) let centerY = globalCoordinate(localX: 0, localY: localFrame.midY, localGeometry: geo).y ZStack { - AVPlayerView(player: model.player) + if case .full = self.style { + DamusAVPlayerView(player: model.player, controller: model.player_view_controller, show_playback_controls: true) + } + if case .preview(let on_tap) = self.style { + DamusAVPlayerView(player: model.player, controller: model.player_view_controller, show_playback_controls: false) + .simultaneousGesture(TapGesture().onEnded({ + on_tap?() + })) + } if model.is_loading { ProgressView() @@ -40,8 +57,10 @@ struct DamusVideoPlayer: View { .scaleEffect(CGSize(width: 1.5, height: 1.5)) } - if model.has_audio { - mute_button + if case .preview = self.style { + if model.has_audio { + mute_button + } } if model.is_live { live_indicator @@ -115,9 +134,24 @@ struct DamusVideoPlayer: View { Spacer() } } + + enum Style { + /// A full video player with playback controls + case full + /// A style suitable for muted, auto-playing videos on a feed + case preview(on_tap: (() -> Void)?) + } } struct DamusVideoPlayer_Previews: PreviewProvider { static var previews: some View { - DamusVideoPlayer(url: URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!, video_size: .constant(nil), controller: VideoController()) + Group { + DamusVideoPlayer(url: URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!, video_size: .constant(nil), controller: VideoController(), style: .full) + .environmentObject(OrientationTracker()) + .previewDisplayName("Full video player") + + DamusVideoPlayer(url: URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!, video_size: .constant(nil), controller: VideoController(), style: .preview(on_tap: nil)) + .environmentObject(OrientationTracker()) + .previewDisplayName("Preview video player") + } } } diff --git a/damus/Views/Video/DamusVideoPlayerViewModel.swift b/damus/Views/Video/DamusVideoPlayerViewModel.swift index 628ef6ba..524c0b72 100644 --- a/damus/Views/Video/DamusVideoPlayerViewModel.swift +++ b/damus/Views/Video/DamusVideoPlayerViewModel.swift @@ -6,6 +6,7 @@ // import AVFoundation +import AVKit import Combine import Foundation import SwiftUI @@ -27,7 +28,8 @@ final class DamusVideoPlayerViewModel: ObservableObject { private let url: URL private let player_item: AVPlayerItem let player: AVPlayer - private let controller: VideoController + fileprivate let controller: VideoController + let player_view_controller = AVPlayerViewController() let id = UUID() @Published var has_audio = false @@ -55,7 +57,7 @@ final class DamusVideoPlayerViewModel: ObservableObject { } } - init(url: URL, video_size: Binding, controller: VideoController) { + init(url: URL, video_size: Binding, controller: VideoController, mute: Bool? = nil) { self.url = url player_item = AVPlayerItem(url: url) player = AVPlayer(playerItem: player_item) @@ -66,7 +68,7 @@ final class DamusVideoPlayerViewModel: ObservableObject { await load() } - is_muted = controller.should_mute_video(url: url) + is_muted = mute ?? controller.should_mute_video(url: url) player.isMuted = is_muted NotificationCenter.default.addObserver(