@@ -1336,7 +1336,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
13361336 private let playbackRatePromise = ValuePromise < Double > ( )
13371337 private let videoQualityPromise = ValuePromise < UniversalVideoContentVideoQuality > ( )
13381338
1339+ private var playerStatusValue : MediaPlayerStatus ?
13391340 private let statusDisposable = MetaDisposable ( )
1341+
13401342 private let moreButtonStateDisposable = MetaDisposable ( )
13411343 private let settingsButtonStateDisposable = MetaDisposable ( )
13421344 private let mediaPlaybackStateDisposable = MetaDisposable ( )
@@ -1927,6 +1929,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
19271929 self . statusDisposable. set ( ( combineLatest ( queue: . mainQueue( ) , videoNode. status, mediaFileStatus)
19281930 |> deliverOnMainQueue) . start ( next: { [ weak self] value, fetchStatus in
19291931 if let strongSelf = self {
1932+ strongSelf. playerStatusValue = value
1933+
19301934 var initialBuffering = false
19311935 var isPlaying = false
19321936 var isPaused = true
@@ -3598,84 +3602,109 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
35983602 return
35993603 }
36003604
3601- var items : [ ContextMenuItem ] = [ ]
3602-
3603- items. append ( . action( ContextMenuActionItem ( text: self . presentationData. strings. Common_Back, icon: { theme in
3604- return generateTintedImage ( image: UIImage ( bundleImageName: " Chat/Context Menu/Back " ) , color: theme. actionSheet. primaryTextColor)
3605- } , iconPosition: . left, action: { c, _ in
3606- c? . popItems ( )
3607- } ) ) )
3605+ var allFiles : [ FileMediaReference ] = [ ]
3606+ allFiles. append ( content. fileReference)
3607+ allFiles. append ( contentsOf: qualitySet. qualityFiles. values)
36083608
3609- let addItem : ( Int ? , FileMediaReference ) -> Void = { quality, qualityFile in
3610- guard let qualityFileSize = qualityFile. media. size else {
3611- return
3609+ let qualitySignals = allFiles. map { file -> Signal < ( fileId: MediaId , isCached: Bool ) , NoError > in
3610+ return self . context. account. postbox. mediaBox. resourceStatus ( file. media. resource)
3611+ |> take ( 1 )
3612+ |> map { status -> ( fileId: MediaId , isCached: Bool ) in
3613+ return ( file. media. fileId, status == . Local)
36123614 }
3613- let fileSizeString = dataSizeString ( qualityFileSize, formatting: DataSizeStringFormatting ( presentationData: self . presentationData) )
3614- let title : String
3615- if let quality {
3616- title = self . presentationData. strings. Gallery_SaveToGallery_Quality ( " \( quality) " ) . string
3617- } else {
3618- title = self . presentationData. strings. Gallery_SaveToGallery_Original
3615+ }
3616+ let _ = ( combineLatest ( queue: . mainQueue( ) , qualitySignals)
3617+ |> deliverOnMainQueue) . startStandalone ( next: { [ weak self, weak c] fileStatuses in
3618+ guard let self else {
3619+ return
36193620 }
3620- items. append ( . action( ContextMenuActionItem ( text: title, textLayout: . secondLineWithValue( fileSizeString) , icon: { _ in
3621- return nil
3622- } , action: { [ weak self] c, _ in
3623- c? . dismiss ( result: . default, completion: nil )
3624-
3625- guard let self else {
3621+
3622+ var items : [ ContextMenuItem ] = [ ]
3623+
3624+ items. append ( . action( ContextMenuActionItem ( text: self . presentationData. strings. Common_Back, icon: { theme in
3625+ return generateTintedImage ( image: UIImage ( bundleImageName: " Chat/Context Menu/Back " ) , color: theme. actionSheet. primaryTextColor)
3626+ } , iconPosition: . left, action: { c, _ in
3627+ c? . popItems ( )
3628+ } ) ) )
3629+
3630+ let addItem : ( Int ? , FileMediaReference ) -> Void = { quality, qualityFile in
3631+ guard let qualityFileSize = qualityFile. media. size else {
36263632 return
36273633 }
3628- guard let controller = self . galleryController ( ) else {
3629- return
3634+ var fileSizeString = dataSizeString ( qualityFileSize, formatting: DataSizeStringFormatting ( presentationData: self . presentationData) )
3635+ let title : String
3636+ if let quality {
3637+ title = self . presentationData. strings. Gallery_SaveToGallery_Quality ( " \( quality) " ) . string
3638+ } else {
3639+ title = self . presentationData. strings. Gallery_SaveToGallery_Original
36303640 }
36313641
3632- let saveScreen = SaveProgressScreen ( context: self . context, content: . progress( self . presentationData. strings. Story_TooltipSaving, 0.0 ) )
3633- controller. present ( saveScreen, in: . current)
3634-
3635- let stringSaving = self . presentationData. strings. Story_TooltipSaving
3636- let stringSaved = self . presentationData. strings. Story_TooltipSaved
3637-
3638- let saveFileReference : AnyMediaReference = qualityFile. abstract
3639- let saveSignal = SaveToCameraRoll . saveToCameraRoll ( context: self . context, postbox: self . context. account. postbox, userLocation: . peer( message. id. peerId) , mediaReference: saveFileReference)
3642+ if let statusValue = fileStatuses. first ( where: { $0. fileId == qualityFile. media. fileId } ) , statusValue. isCached {
3643+ fileSizeString. append ( " • cached " )
3644+ } else {
3645+ fileSizeString. insert ( contentsOf: " ↓ " , at: fileSizeString. startIndex)
3646+ }
36403647
3641- let disposable = ( saveSignal
3642- |> deliverOnMainQueue) . start ( next: { [ weak saveScreen] progress in
3643- guard let saveScreen else {
3648+ items. append ( . action( ContextMenuActionItem ( text: title, textLayout: . secondLineWithValue( fileSizeString) , icon: { _ in
3649+ return nil
3650+ } , action: { [ weak self] c, _ in
3651+ c? . dismiss ( result: . default, completion: nil )
3652+
3653+ guard let self else {
36443654 return
36453655 }
3646- saveScreen. content = . progress( stringSaving, progress)
3647- } , completed: { [ weak saveScreen] in
3648- guard let saveScreen else {
3656+ guard let controller = self . galleryController ( ) else {
36493657 return
36503658 }
3651- saveScreen. content = . completion( stringSaved)
3652- Queue . mainQueue ( ) . after ( 3.0 , { [ weak saveScreen] in
3653- saveScreen? . dismiss ( )
3659+
3660+ let saveScreen = SaveProgressScreen ( context: self . context, content: . progress( self . presentationData. strings. Story_TooltipSaving, 0.0 ) )
3661+ controller. present ( saveScreen, in: . current)
3662+
3663+ let stringSaving = self . presentationData. strings. Story_TooltipSaving
3664+ let stringSaved = self . presentationData. strings. Story_TooltipSaved
3665+
3666+ let saveFileReference : AnyMediaReference = qualityFile. abstract
3667+ let saveSignal = SaveToCameraRoll . saveToCameraRoll ( context: self . context, postbox: self . context. account. postbox, userLocation: . peer( message. id. peerId) , mediaReference: saveFileReference)
3668+
3669+ let disposable = ( saveSignal
3670+ |> deliverOnMainQueue) . start ( next: { [ weak saveScreen] progress in
3671+ guard let saveScreen else {
3672+ return
3673+ }
3674+ saveScreen. content = . progress( stringSaving, progress)
3675+ } , completed: { [ weak saveScreen] in
3676+ guard let saveScreen else {
3677+ return
3678+ }
3679+ saveScreen. content = . completion( stringSaved)
3680+ Queue . mainQueue ( ) . after ( 3.0 , { [ weak saveScreen] in
3681+ saveScreen? . dismiss ( )
3682+ } )
36543683 } )
3655- } )
3656-
3657- saveScreen. cancelled = {
3658- disposable. dispose ( )
3684+
3685+ saveScreen. cancelled = {
3686+ disposable. dispose ( )
3687+ }
3688+ } ) ) )
3689+ }
3690+
3691+ if self . context. isPremium {
3692+ addItem ( nil , content. fileReference)
3693+ } else {
3694+ #if DEBUG
3695+ addItem ( nil , content. fileReference)
3696+ #endif
3697+ }
3698+
3699+ for quality in qualityState. available {
3700+ guard let qualityFile = qualitySet. qualityFiles [ quality] else {
3701+ continue
36593702 }
3660- } ) ) )
3661- }
3662-
3663- if self . context. isPremium {
3664- addItem ( nil , content. fileReference)
3665- } else {
3666- #if DEBUG
3667- addItem ( nil , content. fileReference)
3668- #endif
3669- }
3670-
3671- for quality in qualityState. available {
3672- guard let qualityFile = qualitySet. qualityFiles [ quality] else {
3673- continue
3703+ addItem ( quality, qualityFile)
36743704 }
3675- addItem ( quality, qualityFile)
3676- }
3677-
3678- c? . pushItems ( items: . single( ContextController . Items ( content: . list( items) ) ) )
3705+
3706+ c? . pushItems ( items: . single( ContextController . Items ( content: . list( items) ) ) )
3707+ } )
36793708 } else {
36803709 c? . dismiss ( result: . default, completion: nil )
36813710
@@ -3984,7 +4013,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
39844013
39854014 override func hasActiveEdgeAction( edge: ActiveEdge ) -> Bool {
39864015 if case . right = edge {
3987- return true
4016+ if let playerStatusValue = self . playerStatusValue, case . playing = playerStatusValue. status {
4017+ return true
4018+ } else {
4019+ return false
4020+ }
39884021 } else {
39894022 return false
39904023 }
@@ -3997,13 +4030,13 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
39974030 if let edge, case . right = edge {
39984031 let effectiveRate : Double
39994032 if let current = self . activeEdgeRateState {
4000- effectiveRate = min ( 2.5 , current. initialRate + 0.5 )
4033+ effectiveRate = min ( 4.0 , current. initialRate + 1.0 )
40014034 self . activeEdgeRateState = ( current. initialRate, effectiveRate)
40024035 } else {
40034036 guard let playbackRate = self . playbackRate else {
40044037 return
40054038 }
4006- effectiveRate = min ( 2.5 , playbackRate + 0.5 )
4039+ effectiveRate = min ( 4.0 , playbackRate + 1.0 )
40074040 self . activeEdgeRateState = ( playbackRate, effectiveRate)
40084041 }
40094042 videoNode. setBaseRate ( effectiveRate)
@@ -4023,9 +4056,16 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
40234056 }
40244057 if let current = self . activeEdgeRateState {
40254058 var rateFraction = Double ( distance) / 100.0
4026- rateFraction = max ( 0.0 , min ( 1.0 , rateFraction) )
4027- let rateDistance = ( current. initialRate + 0.5 ) * ( 1.0 - rateFraction) + 2.5 * rateFraction
4028- let effectiveRate = max ( 1.0 , min ( 2.5 , rateDistance) )
4059+ rateFraction = max ( - 1.0 , min ( 1.0 , rateFraction) )
4060+
4061+ let effectiveRate : Double
4062+ if rateFraction < 0.0 {
4063+ let rateDistance = ( current. initialRate + 1.0 ) * ( 1.0 - ( - rateFraction) ) + 1.0 * ( - rateFraction)
4064+ effectiveRate = max ( 1.0 , min ( 4.0 , rateDistance) )
4065+ } else {
4066+ let rateDistance = ( current. initialRate + 1.0 ) * ( 1.0 - rateFraction) + 3.0 * rateFraction
4067+ effectiveRate = max ( 1.0 , min ( 4.0 , rateDistance) )
4068+ }
40294069 self . activeEdgeRateState = ( current. initialRate, effectiveRate)
40304070 videoNode. setBaseRate ( effectiveRate)
40314071
0 commit comments