damus

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

FullScreenCarouselView.swift (6187B)


      1 //
      2 //  FullScreenCarouselView.swift
      3 //  damus
      4 //
      5 //  Created by William Casarin on 2023-03-23.
      6 //
      7 
      8 import SwiftUI
      9 
     10 struct FullScreenCarouselView<Content: View>: View {
     11     let video_controller: VideoController
     12     let urls: [MediaUrl]
     13     
     14     @Environment(\.presentationMode) var presentationMode
     15     
     16     @State var showMenu = true
     17     
     18     let settings: UserSettingsStore
     19     @Binding var selectedIndex: Int
     20     let content: (() -> Content)?
     21     
     22     init(video_controller: VideoController, urls: [MediaUrl], showMenu: Bool = true, settings: UserSettingsStore, selectedIndex: Binding<Int>, @ViewBuilder content: @escaping () -> Content) {
     23         self.video_controller = video_controller
     24         self.urls = urls
     25         self._showMenu = State(initialValue: showMenu)
     26         self.settings = settings
     27         _selectedIndex = selectedIndex
     28         self.content = content
     29     }
     30     
     31     init(video_controller: VideoController, urls: [MediaUrl], showMenu: Bool = true, settings: UserSettingsStore, selectedIndex: Binding<Int>) {
     32         self.video_controller = video_controller
     33         self.urls = urls
     34         self._showMenu = State(initialValue: showMenu)
     35         self.settings = settings
     36         _selectedIndex = selectedIndex
     37         self.content = nil
     38     }
     39     
     40     var background: some ShapeStyle {
     41         if case .video = urls[safe: selectedIndex] {
     42             return AnyShapeStyle(Color.black)
     43         }
     44         else {
     45             return AnyShapeStyle(.regularMaterial)
     46         }
     47     }
     48     
     49     var background_color: UIColor {
     50         return .black
     51     }
     52     
     53     var body: some View {
     54         ZStack {
     55             Color(self.background_color)
     56                 .ignoresSafeArea()
     57             
     58             TabView(selection: $selectedIndex) {
     59                 ForEach(urls.indices, id: \.self) { index in
     60                     VStack {
     61                         if case .video = urls[safe: index] {
     62                             ImageContainerView(video_controller: video_controller, url: urls[index], settings: settings)
     63                                 .clipped()  // SwiftUI hack from https://stackoverflow.com/a/74401288 to make playback controls show up within the TabView
     64                                 .aspectRatio(contentMode: .fit)
     65                                 .padding(.top, Theme.safeAreaInsets?.top)
     66                                 .padding(.bottom, Theme.safeAreaInsets?.bottom)
     67                                 .modifier(SwipeToDismissModifier(minDistance: 50, onDismiss: {
     68                                     presentationMode.wrappedValue.dismiss()
     69                                 }))
     70                                 .ignoresSafeArea()
     71                         }
     72                         else {
     73                             ZoomableScrollView {
     74                                 ImageContainerView(video_controller: video_controller, url: urls[index], settings: settings)
     75                                     .aspectRatio(contentMode: .fit)
     76                                     .padding(.top, Theme.safeAreaInsets?.top)
     77                                     .padding(.bottom, Theme.safeAreaInsets?.bottom)
     78                             }
     79                             .modifier(SwipeToDismissModifier(minDistance: 50, onDismiss: {
     80                                 presentationMode.wrappedValue.dismiss()
     81                             }))
     82                             .ignoresSafeArea()
     83                         }
     84                     }.tag(index)
     85                 }
     86             }
     87             .ignoresSafeArea()
     88             .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
     89             .gesture(TapGesture(count: 2).onEnded {
     90                 // Prevents menu from hiding on double tap
     91             })
     92             .gesture(TapGesture(count: 1).onEnded {
     93                 showMenu.toggle()
     94             })
     95             .overlay(
     96                 GeometryReader { geo in
     97                     VStack {
     98                         if showMenu {
     99                             NavDismissBarView(showBackgroundCircle: false)
    100                                 .foregroundColor(.white)
    101                             Spacer()
    102                             
    103                             if urls.count > 1 {
    104                                 PageControlView(currentPage: $selectedIndex, numberOfPages: urls.count)
    105                                     .frame(maxWidth: 0, maxHeight: 0)
    106                                     .padding(.top, 5)
    107                             }
    108                             
    109                             self.content?()
    110                         }
    111                     }
    112                     .animation(.easeInOut, value: showMenu)
    113                     .padding(.bottom, geo.safeAreaInsets.bottom == 0 ? 12 : 0)
    114                 }
    115             )
    116         }
    117     }
    118 }
    119 
    120 fileprivate struct FullScreenCarouselPreviewView<Content: View>: View {
    121     @State var selectedIndex: Int = 0
    122     let url: MediaUrl = .image(URL(string: "https://jb55.com/red-me.jpg")!)
    123     let test_video_url: MediaUrl = .video(URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!)
    124     let custom_content: (() -> Content)?
    125     
    126     init(content: (() -> Content)? = nil) {
    127         self.custom_content = content
    128     }
    129     
    130     var body: some View {
    131         FullScreenCarouselView(video_controller: test_damus_state.video, urls: [test_video_url, url], settings: test_damus_state.settings, selectedIndex: $selectedIndex) {
    132             self.custom_content?()
    133         }
    134             .environmentObject(OrientationTracker())
    135     }
    136 }
    137 
    138 struct FullScreenCarouselView_Previews: PreviewProvider {
    139     static var previews: some View {
    140         Group {
    141             FullScreenCarouselPreviewView<AnyView>()
    142                 .previewDisplayName("No custom content on overlay")
    143             
    144             FullScreenCarouselPreviewView(content: {
    145                 HStack {
    146                     Spacer()
    147                     
    148                     Text(verbatim: "Some content")
    149                         .padding()
    150                         .foregroundColor(.white)
    151                         
    152                     Spacer()
    153                 }.background(.ultraThinMaterial)
    154             })
    155                 .previewDisplayName("Custom content on overlay")
    156         }
    157     }
    158 }