@@ -1049,19 +1049,20 @@ struct EnhancedShowEpisodesView: View {
10491049
10501050 var body : some View {
10511051 ZStack {
1052+ heroImageSection
10521053 mainScrollView
10531054 . navigationBarHidden ( true )
10541055 . ignoresSafeArea ( . container, edges: . top)
10551056 navigationOverlay
10561057 }
10571058 . onAppear {
10581059 if let windowScene = UIApplication . shared. connectedScenes. first as? UIWindowScene ,
1059- let window = windowScene. windows. first,
1060- let navigationController = window. rootViewController? . children. first as? UINavigationController {
1060+ let window = windowScene. windows. first,
1061+ let navigationController = window. rootViewController? . children. first as? UINavigationController {
10611062 navigationController. interactivePopGestureRecognizer? . isEnabled = true
10621063 navigationController. interactivePopGestureRecognizer? . delegate = nil
10631064 }
1064-
1065+
10651066 NotificationCenter . default. post ( name: . hideTabBar, object: nil )
10661067 }
10671068 . onDisappear {
@@ -1097,10 +1098,7 @@ struct EnhancedShowEpisodesView: View {
10971098 @ViewBuilder
10981099 private var mainScrollView : some View {
10991100 ScrollView ( showsIndicators: false ) {
1100- ZStack ( alignment: . top) {
1101- heroImageSection
1102- contentContainer
1103- }
1101+ contentContainer
11041102 }
11051103 . onAppear {
11061104 UIScrollView . appearance ( ) . bounces = false
@@ -1109,22 +1107,24 @@ struct EnhancedShowEpisodesView: View {
11091107
11101108 @ViewBuilder
11111109 private var heroImageSection : some View {
1112- Group {
1113- if let posterURL = group. posterURL {
1114- LazyImage ( url: posterURL) { @MainActor state in
1115- if let uiImage = state. imageContainer? . image {
1116- Image ( uiImage: uiImage)
1117- . resizable ( )
1118- . aspectRatio ( contentMode: . fill)
1119- . frame ( width: UIScreen . main. bounds. width, height: 700 )
1120- . clipped ( )
1121- } else {
1122- placeholderGradient
1123- }
1110+ if let posterURL = group. posterURL {
1111+ LazyImage ( url: posterURL) { state in
1112+ if let uiImage = state. imageContainer? . image {
1113+ Image ( uiImage: uiImage)
1114+ . resizable ( )
1115+ . aspectRatio ( contentMode: . fill)
1116+ } else {
1117+ placeholderGradient
11241118 }
1125- } else {
1126- placeholderGradient
11271119 }
1120+ . ignoresSafeArea ( . all)
1121+ . frame ( maxWidth: . infinity, maxHeight: 400 )
1122+ . clipped ( )
1123+ } else {
1124+ placeholderGradient
1125+ . ignoresSafeArea ( . all)
1126+ . frame ( maxWidth: . infinity, maxHeight: 400 )
1127+ . clipped ( )
11281128 }
11291129 }
11301130
@@ -1141,8 +1141,6 @@ struct EnhancedShowEpisodesView: View {
11411141 endPoint: . bottomTrailing
11421142 )
11431143 )
1144- . frame ( width: UIScreen . main. bounds. width, height: 700 )
1145- . clipped ( )
11461144 }
11471145
11481146 @ViewBuilder
@@ -1324,55 +1322,15 @@ struct EnhancedEpisodeRow: View {
13241322 let showDivider : Bool
13251323 let onPlay : ( DownloadedAsset ) -> Void
13261324 let onDelete : ( DownloadedAsset ) -> Void
1327- @State private var swipeOffset : CGFloat = 0
1328- @State private var isShowingActions : Bool = false
1329- @State private var dragState = DragState . inactive
1330-
1331- struct DragState {
1332- var translation : CGSize
1333- var isActive : Bool
1334-
1335- static var inactive : DragState {
1336- DragState ( translation: . zero, isActive: false )
1337- }
1338- }
1339-
1325+
13401326 @Environment ( \. colorScheme) private var colorScheme
13411327 private var fillerBadgeOpacity : Double { colorScheme == . dark ? 0.18 : 0.12 }
13421328 var body : some View {
1343- ZStack {
1344- actionButtonsBackground
1345- episodeCellContent
1346- }
1347- }
1348-
1349- private var actionButtonsBackground : some View {
1350- HStack {
1351- Spacer ( )
1352- Button ( action: {
1353- onDelete ( asset)
1354- } ) {
1355- VStack ( spacing: 4 ) {
1356- Image ( systemName: " trash.fill " )
1357- . font ( . title2)
1358- . foregroundColor ( . red)
1359- Text ( " Delete " )
1360- . font ( . caption)
1361- . foregroundColor ( . red)
1362- }
1363- . frame ( width: 60 )
1364- }
1365- . frame ( height: 76 )
1366- }
1367- . zIndex ( 0 )
1368- }
1369-
1370- private var episodeCellContent : some View {
13711329 HStack {
13721330 // Thumbnail
13731331 Group {
13741332 if let backdropURL = asset. metadata? . backdropURL ?? asset. metadata? . posterURL {
1375- LazyImage ( url: backdropURL) { @ MainActor state in
1333+ LazyImage ( url: backdropURL) { state in
13761334 if let uiImage = state. imageContainer? . image {
13771335 Image ( uiImage: uiImage)
13781336 . resizable ( )
@@ -1397,7 +1355,7 @@ struct EnhancedEpisodeRow: View {
13971355 }
13981356 . frame ( width: 100 , height: 56 )
13991357 . clipShape ( RoundedRectangle ( cornerRadius: 8 ) )
1400-
1358+
14011359 VStack ( alignment: . leading) {
14021360 HStack ( spacing: 8 ) {
14031361 Text ( " Episode \( asset. metadata? . episode ?? 0 ) " )
@@ -1422,9 +1380,9 @@ struct EnhancedEpisodeRow: View {
14221380 . lineLimit ( 1 )
14231381 }
14241382 }
1425-
1383+
14261384 Spacer ( )
1427-
1385+
14281386 CircularProgressBar ( progress: 0.0 )
14291387 . frame ( width: 40 , height: 40 )
14301388 . padding ( . trailing, 4 )
@@ -1435,23 +1393,18 @@ struct EnhancedEpisodeRow: View {
14351393 . frame ( maxWidth: . infinity)
14361394 . background ( cellBackground)
14371395 . clipShape ( RoundedRectangle ( cornerRadius: 15 ) )
1438- . offset ( x: swipeOffset + dragState. translation. width)
1439- . zIndex ( 1 )
1440- . scaleEffect ( dragState. isActive ? 0.98 : 1.0 )
1441- . animation ( . spring( response: 0.4 , dampingFraction: 0.8 ) , value: swipeOffset)
1442- . animation ( . spring( response: 0.3 , dampingFraction: 0.6 ) , value: dragState. isActive)
1443- . simultaneousGesture (
1444- DragGesture ( coordinateSpace: . local)
1445- . onChanged { value in
1446- handleDragChanged ( value)
1447- }
1448- . onEnded { value in
1449- handleDragEnded ( value)
1450- }
1451- )
1452- . onTapGesture { handleTap ( ) }
1396+ . swipeActions ( edge: . trailing) {
1397+ Button ( role: . destructive, action: {
1398+ onDelete ( asset)
1399+ } ) {
1400+ Label ( " Delete " , systemImage: " trash.fill " )
1401+ }
1402+ }
1403+ . onTapGesture {
1404+ onPlay ( asset)
1405+ }
14531406 }
1454-
1407+
14551408 private var cellBackground : some View {
14561409 RoundedRectangle ( cornerRadius: 15 )
14571410 . fill ( Color ( UIColor . systemBackground) )
@@ -1474,78 +1427,6 @@ struct EnhancedEpisodeRow: View {
14741427 )
14751428 )
14761429 }
1477-
1478- private func handleDragChanged( _ value: DragGesture . Value ) {
1479- let translation = value. translation
1480- let velocity = value. velocity
1481-
1482- let isHorizontalGesture = abs ( translation. width) > abs ( translation. height)
1483- let hasSignificantHorizontalMovement = abs ( translation. width) > 10
1484-
1485- if isHorizontalGesture && hasSignificantHorizontalMovement {
1486- dragState = . inactive
1487-
1488- let proposedOffset = swipeOffset + translation. width
1489- let maxSwipe : CGFloat = 60 // Only one button
1490-
1491- if translation. width < 0 {
1492- let newOffset = max ( proposedOffset, - maxSwipe)
1493- if proposedOffset < - maxSwipe {
1494- let resistance = abs ( proposedOffset + maxSwipe) * 0.15
1495- swipeOffset = - maxSwipe - resistance
1496- } else {
1497- swipeOffset = newOffset
1498- }
1499- } else if isShowingActions {
1500- swipeOffset = min ( max ( proposedOffset, - maxSwipe) , maxSwipe * 0.2 )
1501- }
1502- } else if !hasSignificantHorizontalMovement {
1503- dragState = . inactive
1504- }
1505- }
1506-
1507- private func handleDragEnded( _ value: DragGesture . Value ) {
1508- let translation = value. translation
1509- let velocity = value. velocity
1510-
1511- dragState = . inactive
1512-
1513- let isHorizontalGesture = abs ( translation. width) > abs ( translation. height)
1514- let hasSignificantHorizontalMovement = abs ( translation. width) > 10
1515-
1516- if isHorizontalGesture && hasSignificantHorizontalMovement {
1517- let maxSwipe : CGFloat = 60
1518- let threshold = maxSwipe * 0.3
1519- let velocityThreshold : CGFloat = 500
1520-
1521- withAnimation ( . spring( response: 0.4 , dampingFraction: 0.8 ) ) {
1522- if translation. width < - threshold || velocity. width < - velocityThreshold {
1523- swipeOffset = - maxSwipe
1524- isShowingActions = true
1525- } else if translation. width > threshold || velocity. width > velocityThreshold {
1526- swipeOffset = 0
1527- isShowingActions = false
1528- } else {
1529- swipeOffset = isShowingActions ? - maxSwipe : 0
1530- }
1531- }
1532- } else {
1533- withAnimation ( . spring( response: 0.4 , dampingFraction: 0.8 ) ) {
1534- swipeOffset = isShowingActions ? - 60 : 0
1535- }
1536- }
1537- }
1538-
1539- private func handleTap( ) {
1540- if isShowingActions {
1541- withAnimation ( . spring( response: 0.3 , dampingFraction: 0.8 ) ) {
1542- swipeOffset = 0
1543- isShowingActions = false
1544- }
1545- } else {
1546- onPlay ( asset)
1547- }
1548- }
15491430}
15501431
15511432struct SearchableStyleModifier : ViewModifier {
0 commit comments