Browse Source

Merge pull request #1981 from nextcloud/improved/pdfview

Improved PDF Viewer
Marino Faggiana 2 years ago
parent
commit
43b97986c2

+ 17 - 0
Nextcloud.xcodeproj/project.pbxproj

@@ -207,6 +207,7 @@
 		F74E7720277A2EF40013B958 /* XLForm in Frameworks */ = {isa = PBXBuildFile; productRef = F74E771F277A2EF40013B958 /* XLForm */; };
 		F7501C322212E57500FB1415 /* NCMedia.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7501C302212E57400FB1415 /* NCMedia.storyboard */; };
 		F7501C332212E57500FB1415 /* NCMedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7501C312212E57400FB1415 /* NCMedia.swift */; };
+		F753BA93281FD8020015BFB6 /* EasyTipView in Frameworks */ = {isa = PBXBuildFile; productRef = F753BA92281FD8020015BFB6 /* EasyTipView */; };
 		F755BD9B20594AC7008C5FBB /* NCService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F755BD9A20594AC7008C5FBB /* NCService.swift */; };
 		F7581D1A25EFDA61004DC699 /* NCLoginWeb+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7581D1925EFDA60004DC699 /* NCLoginWeb+Menu.swift */; };
 		F7581D2425EFDDDF004DC699 /* NCMedia+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7581D2325EFDDDF004DC699 /* NCMedia+Menu.swift */; };
@@ -1016,6 +1017,7 @@
 				F758A01227A7F03E0069468B /* JGProgressHUD in Frameworks */,
 				F76DA96F277B78AE0082465B /* TLPhotoPicker in Frameworks */,
 				F76DA966277B76F30082465B /* UICKeyChainStore in Frameworks */,
+				F753BA93281FD8020015BFB6 /* EasyTipView in Frameworks */,
 				F76DA95B277B75A90082465B /* TOPasscodeViewController.xcframework in Frameworks */,
 				F76DA963277B760E0082465B /* Queuer in Frameworks */,
 				F75E57BD25BF0EC1002B72C2 /* SVGKit in Frameworks */,
@@ -2085,6 +2087,7 @@
 				F7233B3927835FA400F40A43 /* ChromaColorPicker */,
 				F7BB7E4627A18C56009B9F29 /* Parchment */,
 				F758A01127A7F03E0069468B /* JGProgressHUD */,
+				F753BA92281FD8020015BFB6 /* EasyTipView */,
 			);
 			productName = "Crypto Cloud";
 			productReference = F7CE8AFA1DC1F8D8009CAE48 /* Nextcloud.app */;
@@ -2212,6 +2215,7 @@
 				F7233B3827835FA300F40A43 /* XCRemoteSwiftPackageReference "ChromaColorPicker" */,
 				F7BB7E4527A18C56009B9F29 /* XCRemoteSwiftPackageReference "Parchment" */,
 				F72CD01027A7E92400E59476 /* XCRemoteSwiftPackageReference "JGProgressHUD" */,
+				F753BA91281FD8010015BFB6 /* XCRemoteSwiftPackageReference "EasyTipView" */,
 			);
 			productRefGroup = F7F67B9F1A24D27800EE80DA;
 			projectDirPath = "";
@@ -3209,6 +3213,14 @@
 				minimumVersion = 4.0.0;
 			};
 		};
+		F753BA91281FD8010015BFB6 /* XCRemoteSwiftPackageReference "EasyTipView" */ = {
+			isa = XCRemoteSwiftPackageReference;
+			repositoryURL = "https://github.com/marinofaggiana/EasyTipView";
+			requirement = {
+				kind = upToNextMajorVersion;
+				minimumVersion = 2.0.0;
+			};
+		};
 		F75E57A725BF0D61002B72C2 /* XCRemoteSwiftPackageReference "SVGKit" */ = {
 			isa = XCRemoteSwiftPackageReference;
 			repositoryURL = "https://github.com/SVGKit/SVGKit.git";
@@ -3393,6 +3405,11 @@
 			package = F74E771E277A2EF40013B958 /* XCRemoteSwiftPackageReference "XLForm" */;
 			productName = XLForm;
 		};
+		F753BA92281FD8020015BFB6 /* EasyTipView */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = F753BA91281FD8010015BFB6 /* XCRemoteSwiftPackageReference "EasyTipView" */;
+			productName = EasyTipView;
+		};
 		F758A01127A7F03E0069468B /* JGProgressHUD */ = {
 			isa = XCSwiftPackageProductDependency;
 			package = F72CD01027A7E92400E59476 /* XCRemoteSwiftPackageReference "JGProgressHUD" */;

+ 5 - 0
iOSClient/Data/NCDatabase.swift

@@ -493,6 +493,11 @@ class tableTag: Object {
     }
 }
 
+class tableTip: Object {
+
+    @Persisted(primaryKey: true) var tipName = ""
+}
+
 class tableTrash: Object {
 
     @objc dynamic var account = ""

+ 32 - 0
iOSClient/Data/NCManageDatabase.swift

@@ -245,6 +245,7 @@ class NCManageDatabase: NSObject {
         self.clearTable(tablePhotoLibrary.self, account: account)
         self.clearTable(tableShare.self, account: account)
         self.clearTable(tableTag.self, account: account)
+        self.clearTable(tableTip.self)
         self.clearTable(tableTrash.self, account: account)
         self.clearTable(tableUserStatus.self, account: account)
         self.clearTable(tableVideo.self, account: account)
@@ -1546,6 +1547,37 @@ class NCManageDatabase: NSObject {
         return tableTag.init(value: result)
     }
 
+    // MARK: -
+    // MARK: Table Tip
+
+    @objc func tipExists(_ tipName: String) -> Bool {
+
+        let realm = try! Realm()
+
+        guard (realm.objects(tableTip.self).where {
+            $0.tipName == tipName
+        }.first) == nil else {
+            return true
+        }
+
+        return false
+    }
+
+    @objc func addTip(_ tipName: String) {
+
+        let realm = try! Realm()
+
+        do {
+            try realm.safeWrite {
+                let addObject = tableTip()
+                addObject.tipName = tipName
+                realm.add(addObject, update: .all)
+            }
+        } catch let error {
+            NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)")
+        }
+    }
+
     // MARK: -
     // MARK: Table Trash
 

+ 0 - 25
iOSClient/Menu/NCViewer+Menu.swift

@@ -226,31 +226,6 @@ extension NCViewer {
                 )
             )
 
-            var title = ""
-            var icon = UIImage()
-
-            if CCUtility.getPDFDisplayDirection() == .horizontal {
-                title = NSLocalizedString("_pdf_vertical_", comment: "")
-                icon = UIImage(named: "pdf-vertical")!.image(color: NCBrandColor.shared.gray, size: 50)
-            } else {
-                title = NSLocalizedString("_pdf_horizontal_", comment: "")
-                icon = UIImage(named: "pdf-horizontal")!.image(color: NCBrandColor.shared.gray, size: 50)
-            }
-
-            actions.append(
-                NCMenuAction(
-                    title: title,
-                    icon: icon,
-                    action: { _ in
-                        if CCUtility.getPDFDisplayDirection() == .horizontal {
-                            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterMenuPDFDisplayDirection, userInfo: ["direction": PDFDisplayDirection.vertical])
-                        } else {
-                            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterMenuPDFDisplayDirection, userInfo: ["direction": PDFDisplayDirection.horizontal])
-                        }
-                    }
-                )
-            )
-
             actions.append(
                 NCMenuAction(
                     title: NSLocalizedString("_go_to_page_", comment: ""),

+ 5 - 2
iOSClient/NCGlobal.swift

@@ -112,7 +112,7 @@ class NCGlobal: NSObject {
     // Database Realm
     //
     let databaseDefault                             = "nextcloud.realm"
-    let databaseSchemaVersion: UInt64               = 217
+    let databaseSchemaVersion: UInt64               = 218
 
     // Intro selector
     //
@@ -344,7 +344,6 @@ class NCGlobal: NSObject {
     let notificationCenterFavoriteFile                          = "favoriteFile"                    // userInfo: ocId
 
     let notificationCenterMenuSearchTextPDF                     = "menuSearchTextPDF"
-    let notificationCenterMenuPDFDisplayDirection               = "menuPDFDisplayDirection"         // userInfo: direction
     let notificationCenterMenuGotToPageInPDF                    = "menuGotToPageInPDF"
     let notificationCenterMenuDetailClose                       = "menuDetailClose"
 
@@ -360,4 +359,8 @@ class NCGlobal: NSObject {
     let notificationCenterReloadMediaPage                       = "reloadMediaPage"
     let notificationCenterPlayMedia                             = "playMedia"
     let notificationCenterPauseMedia                            = "pauseMedia"
+
+    // Tip
+    //
+    let tipNCViewerPDFThumbnail                                 = "tipncviewerpdfthumbnail"
 }

+ 2 - 1
iOSClient/Supporting Files/en.lproj/Localizable.strings

@@ -857,4 +857,5 @@
 "_subtitle_not_found_"      = "Subtitle not found";
 "_disable_"                 = "Disable";
 "_subtitle_not_dowloaded_"  = "There are subtitles not downloaded locally";
-
+// Tip
+"_tip_pdf_thumbnails_"      = "Swipe left from the right edge of the screen to show the thumbnails.";

+ 0 - 3
iOSClient/Utility/CCUtility.h

@@ -180,9 +180,6 @@
 + (NSInteger)getCleanUpDay;
 + (void)setCleanUpDay:(NSInteger)days;
 
-+ (PDFDisplayDirection)getPDFDisplayDirection;
-+ (void)setPDFDisplayDirection:(PDFDisplayDirection)direction;
-
 + (BOOL)getPrivacyScreenEnabled;
 + (void)setPrivacyScreenEnabled:(BOOL)set;
 

+ 0 - 17
iOSClient/Utility/CCUtility.m

@@ -715,23 +715,6 @@
     [UICKeyChainStore setString:daysString forKey:@"cleanUpDay" service:NCGlobal.shared.serviceShareKeyChain];
 }
 
-+ (PDFDisplayDirection)getPDFDisplayDirection
-{
-    NSString *direction = [UICKeyChainStore stringForKey:@"PDFDisplayDirection" service:NCGlobal.shared.serviceShareKeyChain];
-    
-    if (direction == nil) {
-        return kPDFDisplayDirectionVertical;
-    } else {
-        return [direction integerValue];
-    }
-}
-
-+ (void)setPDFDisplayDirection:(PDFDisplayDirection)direction
-{
-    NSString *directionString = [@(direction) stringValue];
-    [UICKeyChainStore setString:directionString forKey:@"PDFDisplayDirection" service:NCGlobal.shared.serviceShareKeyChain];
-}
-
 + (BOOL)getPrivacyScreenEnabled
 {
     NSString *valueString = [UICKeyChainStore stringForKey:@"privacyScreen" service:NCGlobal.shared.serviceShareKeyChain];

+ 244 - 80
iOSClient/Viewer/NCViewerPDF/NCViewerPDF.swift

@@ -23,21 +23,34 @@
 
 import UIKit
 import PDFKit
+import EasyTipView
 
-class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate {
+class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate, UIGestureRecognizerDelegate {
 
-    let appDelegate = UIApplication.shared.delegate as! AppDelegate
     var metadata = tableMetadata()
     var imageIcon: UIImage?
 
+    private var filePath = ""
+
     private var pdfView = PDFView()
-    private var thumbnailViewHeight: CGFloat = 40
+    private var pdfThumbnailScrollView = UIScrollView()
     private var pdfThumbnailView = PDFThumbnailView()
     private var pdfDocument: PDFDocument?
     private let pageView = UIView()
     private let pageViewLabel = UILabel()
+    private var tipView: EasyTipView?
+
+    private let thumbnailViewHeight: CGFloat = 70
+    private let thumbnailViewWidth: CGFloat = 80
+    private let thumbnailPadding: CGFloat = 2
+    private let animateDuration: TimeInterval = 0.3
+
+    private var defaultBackgroundColor: UIColor = .clear
+
+    private var pdfThumbnailScrollViewTopAnchor: NSLayoutConstraint?
+    private var pdfThumbnailScrollViewTrailingAnchor: NSLayoutConstraint?
+    private var pdfThumbnailScrollViewWidthAnchor: NSLayoutConstraint?
     private var pageViewWidthAnchor: NSLayoutConstraint?
-    private var filePath = ""
 
     // MARK: - View Life Cycle
 
@@ -48,80 +61,129 @@ class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate {
     override func viewDidLoad() {
 
         filePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
-
-        pdfView = PDFView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height))
         pdfDocument = PDFDocument(url: URL(fileURLWithPath: filePath))
+        let pageCount = CGFloat(pdfDocument?.pageCount ?? 0)
+        defaultBackgroundColor = pdfView.backgroundColor
+        view.backgroundColor = defaultBackgroundColor
+
+        navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "more")!.image(color: NCBrandColor.shared.label, size: 25), style: .plain, target: self, action: #selector(self.openMenuMore))
+        navigationItem.title = metadata.fileNameView
+
+        // PDF VIEW
 
+        if UIDevice.current.userInterfaceIdiom == .phone {
+            pdfView = PDFView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height))
+        } else {
+            pdfView = PDFView(frame: CGRect(x: 0, y: 0, width: view.frame.width-thumbnailViewWidth, height: view.frame.height))
+        }
+        pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleTopMargin, .flexibleLeftMargin]
         pdfView.document = pdfDocument
-        pdfView.backgroundColor = NCBrandColor.shared.systemBackground
-        pdfView.displayMode = .singlePageContinuous
         pdfView.autoScales = true
-        pdfView.displayDirection = CCUtility.getPDFDisplayDirection()
-        pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleTopMargin, .flexibleBottomMargin]
-        pdfView.usePageViewController(true, withViewOptions: nil)
-
+        pdfView.displayMode = .singlePageContinuous
+        pdfView.displayDirection = .vertical
+        pdfView.maxScaleFactor = 4.0
+        pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
         view.addSubview(pdfView)
 
+        // PDF THUMBNAIL
+
+        pdfThumbnailScrollView.translatesAutoresizingMaskIntoConstraints = false
+        pdfThumbnailScrollView.backgroundColor = defaultBackgroundColor
+        pdfThumbnailScrollView.showsVerticalScrollIndicator = false
+        view.addSubview(pdfThumbnailScrollView)
+
+        pdfThumbnailScrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
+        pdfThumbnailScrollViewTopAnchor = pdfThumbnailScrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor)
+        pdfThumbnailScrollViewTopAnchor?.isActive = true
+        pdfThumbnailScrollViewTrailingAnchor = pdfThumbnailScrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
+        pdfThumbnailScrollViewTrailingAnchor?.isActive = true
+        pdfThumbnailScrollViewWidthAnchor = pdfThumbnailScrollView.widthAnchor.constraint(equalToConstant: thumbnailViewWidth)
+        pdfThumbnailScrollViewWidthAnchor?.isActive = true
+
         pdfThumbnailView.translatesAutoresizingMaskIntoConstraints = false
         pdfThumbnailView.pdfView = pdfView
-        pdfThumbnailView.layoutMode = .horizontal
-        pdfThumbnailView.thumbnailSize = CGSize(width: 40, height: thumbnailViewHeight)
+        pdfThumbnailView.layoutMode = .vertical
+        pdfThumbnailView.thumbnailSize = CGSize(width: thumbnailViewHeight, height: thumbnailViewHeight)
         pdfThumbnailView.backgroundColor = .clear
-        // pdfThumbnailView.layer.shadowOffset.height = -5
-        // pdfThumbnailView.layer.shadowOpacity = 0.25
-
-        view.addSubview(pdfThumbnailView)
-
-        pdfThumbnailView.heightAnchor.constraint(equalToConstant: thumbnailViewHeight).isActive = true
-        pdfThumbnailView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
-        pdfThumbnailView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
-        pdfThumbnailView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
+        if UIDevice.current.userInterfaceIdiom == .phone {
+            self.pdfThumbnailScrollView.isHidden = true
+        } else {
+            self.pdfThumbnailScrollView.isHidden = false
+        }
+        pdfThumbnailScrollView.addSubview(pdfThumbnailView)
+
+        NSLayoutConstraint.activate([
+            pdfThumbnailView.topAnchor.constraint(equalTo: pdfThumbnailScrollView.topAnchor),
+            pdfThumbnailView.bottomAnchor.constraint(equalTo: pdfThumbnailScrollView.bottomAnchor),
+            pdfThumbnailView.leadingAnchor.constraint(equalTo: pdfThumbnailScrollView.leadingAnchor),
+            pdfThumbnailView.leadingAnchor.constraint(equalTo: pdfThumbnailScrollView.trailingAnchor, constant: (UIApplication.shared.keyWindow?.safeAreaInsets.left ?? 0)),
+            pdfThumbnailView.widthAnchor.constraint(equalToConstant: thumbnailViewWidth)
+        ])
+        let contentViewCenterY = pdfThumbnailView.centerYAnchor.constraint(equalTo: pdfThumbnailScrollView.centerYAnchor)
+        contentViewCenterY.priority = .defaultLow
+        let contentViewHeight = pdfThumbnailView.heightAnchor.constraint(equalToConstant: CGFloat(pageCount * thumbnailViewHeight) + CGFloat(pageCount * thumbnailPadding) + 30)
+        contentViewHeight.priority = .defaultLow
+        NSLayoutConstraint.activate([
+            contentViewCenterY,
+            contentViewHeight
+        ])
+
+        // COUNTER PDF PAGE VIEW
 
         pageView.translatesAutoresizingMaskIntoConstraints = false
         pageView.layer.cornerRadius = 10
         pageView.backgroundColor = NCBrandColor.shared.systemBackground.withAlphaComponent(
             UIAccessibility.isReduceTransparencyEnabled ? 1 : 0.5
         )
-
         view.addSubview(pageView)
 
-        pageView.heightAnchor.constraint(equalToConstant: 30).isActive = true
+        NSLayoutConstraint.activate([
+            pageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10),
+            pageView.heightAnchor.constraint(equalToConstant: 30),
+            pageView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 10)
+        ])
         pageViewWidthAnchor = pageView.widthAnchor.constraint(equalToConstant: 10)
         pageViewWidthAnchor?.isActive = true
-        pageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 4).isActive = true
-        pageView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 7).isActive = true
 
         pageViewLabel.translatesAutoresizingMaskIntoConstraints = false
         pageViewLabel.textAlignment = .center
         pageViewLabel.textColor = NCBrandColor.shared.label
-
         pageView.addSubview(pageViewLabel)
 
-        pageViewLabel.leftAnchor.constraint(equalTo: pageView.leftAnchor).isActive = true
-        pageViewLabel.rightAnchor.constraint(equalTo: pageView.rightAnchor).isActive = true
-        pageViewLabel.topAnchor.constraint(equalTo: pageView.topAnchor).isActive = true
-        pageViewLabel.bottomAnchor.constraint(equalTo: pageView.bottomAnchor).isActive = true
+        NSLayoutConstraint.activate([
+            pageViewLabel.topAnchor.constraint(equalTo: pageView.topAnchor),
+            pageViewLabel.leftAnchor.constraint(equalTo: pageView.leftAnchor),
+            pageViewLabel.rightAnchor.constraint(equalTo: pageView.rightAnchor),
+            pageViewLabel.bottomAnchor.constraint(equalTo: pageView.bottomAnchor)
+        ])
 
-        pdfView.backgroundColor = NCBrandColor.shared.systemBackground
-        pdfView.layoutIfNeeded()
-        handlePageChange()
+        // GESTURE
 
-        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTap(_:)))
-        tapGesture.numberOfTapsRequired = 1
-        pdfView.addGestureRecognizer(tapGesture)
+        let tapPdfView = UITapGestureRecognizer(target: self, action: #selector(tapPdfView))
+        tapPdfView.numberOfTapsRequired = 1
+        pdfView.addGestureRecognizer(tapPdfView)
 
         // recognize single / double tap
         for gesture in pdfView.gestureRecognizers! {
-            tapGesture.require(toFail: gesture)
+            tapPdfView.require(toFail: gesture)
         }
-    }
 
-    override func viewWillAppear(_ animated: Bool) {
-        super.viewWillAppear(animated)
+        let swipePdfView = UISwipeGestureRecognizer(target: self, action: #selector(gestureClosePdfThumbnail))
+        swipePdfView.direction = .right
+        pdfView.addGestureRecognizer(swipePdfView)
 
-        appDelegate.activeViewController = self
+        let swipePdfThumbnailScrollView = UISwipeGestureRecognizer(target: self, action: #selector(gestureClosePdfThumbnail))
+        swipePdfThumbnailScrollView.direction = .right
+        pdfThumbnailScrollView.addGestureRecognizer(swipePdfThumbnailScrollView)
+
+        let edgePdfView = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(gestureOpenPdfThumbnail))
+        edgePdfView.edges = .right
+        pdfView.addGestureRecognizer(edgePdfView)
+
+        let edgeView = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(gestureOpenPdfThumbnail))
+        edgeView.edges = .right
+        view.addGestureRecognizer(edgeView)
 
-        //
         NotificationCenter.default.addObserver(self, selector: #selector(favoriteFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterFavoriteFile), object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(deleteFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(renameFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterRenameFile), object: nil)
@@ -130,19 +192,62 @@ class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate {
 
         NotificationCenter.default.addObserver(self, selector: #selector(viewUnload), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuDetailClose), object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(searchText), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuSearchTextPDF), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(direction(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuPDFDisplayDirection), object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(goToPage), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuGotToPageInPDF), object: nil)
+
         NotificationCenter.default.addObserver(self, selector: #selector(handlePageChange), name: Notification.Name.PDFViewPageChanged, object: nil)
 
-        //
-        navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "more")!.image(color: NCBrandColor.shared.label, size: 25), style: .plain, target: self, action: #selector(self.openMenuMore))
+        // Tip
+        if UIDevice.current.userInterfaceIdiom == .phone && !NCManageDatabase.shared.tipExists(NCGlobal.shared.tipNCViewerPDFThumbnail){
 
-        navigationController?.navigationBar.prefersLargeTitles = true
-        navigationItem.title = metadata.fileNameView
+            var preferences = EasyTipView.Preferences()
+            preferences.drawing.foregroundColor = NCBrandColor.shared.brandText
+            preferences.drawing.backgroundColor = NCBrandColor.shared.customer
+            preferences.drawing.textAlignment = .left
+            preferences.drawing.arrowPosition = .right
+            preferences.drawing.cornerRadius = 10
+
+            preferences.positioning.bubbleInsets.right = UIApplication.shared.keyWindow?.safeAreaInsets.right ?? 0
+
+            preferences.animating.dismissTransform = CGAffineTransform(translationX: 0, y: 100)
+            preferences.animating.showInitialTransform = CGAffineTransform(translationX: 0, y: -100)
+            preferences.animating.showInitialAlpha = 0
+            preferences.animating.showDuration = 1.5
+            preferences.animating.dismissDuration = 1.5
+
+            tipView = EasyTipView(text: NSLocalizedString("_tip_pdf_thumbnails_", comment: ""), preferences: preferences, delegate: self)
+        }
+
+        setConstraints()
+        handlePageChange()
+    }
+
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+
+        self.tipView?.show(forView: self.pdfThumbnailScrollView, withinSuperview: self.view)
     }
 
-    override func viewWillDisappear(_ animated: Bool) {
-        super.viewWillDisappear(animated)
+    @objc func viewUnload() {
+
+        navigationController?.popViewController(animated: true)
+    }
+
+    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
+        super.viewWillTransition(to: size, with: coordinator)
+
+        coordinator.animate(alongsideTransition: { context in
+            if UIDevice.current.userInterfaceIdiom == .phone {
+                // Close
+                self.tipView?.dismiss()
+                self.pdfThumbnailScrollViewTrailingAnchor?.constant = self.thumbnailViewWidth + (UIApplication.shared.keyWindow?.safeAreaInsets.right ?? 0)
+                self.pdfThumbnailScrollView.isHidden = true
+            }
+        }, completion: { context in
+            self.setConstraints()
+        })
+    }
+
+    deinit {
 
         NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterFavoriteFile), object: nil)
         NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil)
@@ -152,17 +257,11 @@ class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate {
 
         NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuDetailClose), object: nil)
         NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuSearchTextPDF), object: nil)
-        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuPDFDisplayDirection), object: nil)
         NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuGotToPageInPDF), object: nil)
 
         NotificationCenter.default.removeObserver(self, name: Notification.Name.PDFViewPageChanged, object: nil)
     }
 
-    @objc func viewUnload() {
-
-        navigationController?.popViewController(animated: true)
-    }
-
     // MARK: - NotificationCenter
 
     @objc func uploadedFile(_ notification: NSNotification) {
@@ -236,17 +335,6 @@ class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate {
         self.present(navigaionController, animated: true)
     }
 
-    @objc func direction(_ notification: NSNotification) {
-
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let direction = userInfo["direction"] as? PDFDisplayDirection {
-                pdfView.displayDirection = direction
-                CCUtility.setPDFDisplayDirection(direction)
-                handlePageChange()
-            }
-        }
-    }
-
     @objc func goToPage() {
 
         guard let pdfDocument = pdfView.document else { return }
@@ -272,45 +360,112 @@ class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate {
     // MARK: - Action
 
     @objc func openMenuMore() {
+
         if imageIcon == nil { imageIcon = UIImage(named: "file_pdf") }
         NCViewer.shared.toggleMenu(viewController: self, metadata: metadata, webView: false, imageIcon: imageIcon)
     }
 
     // MARK: - Gesture Recognizer
 
-    @objc func didTap(_ recognizer: UITapGestureRecognizer) {
+    @objc func tapPdfView(_ recognizer: UITapGestureRecognizer) {
 
-        if navigationController?.isNavigationBarHidden ?? false {
+        pdfThumbnailScrollViewTopAnchor?.isActive = false
 
-            navigationController?.setNavigationBarHidden(false, animated: false)
-            pdfThumbnailView.isHidden = false
-            pdfView.backgroundColor = NCBrandColor.shared.systemBackground
+        if navigationController?.isNavigationBarHidden ?? false {
+            navigationController?.setNavigationBarHidden(false, animated: true)
+            pdfThumbnailScrollViewTopAnchor = pdfThumbnailScrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor)
 
         } else {
+            navigationController?.setNavigationBarHidden(true, animated: true)
+            pdfThumbnailScrollViewTopAnchor = pdfThumbnailScrollView.topAnchor.constraint(equalTo: view.topAnchor)
+        }
+
+        pdfThumbnailScrollViewTopAnchor?.isActive = true
+
+        handlePageChange()
+    }
 
-            let point = recognizer.location(in: pdfView)
-            if point.y > pdfView.frame.height - thumbnailViewHeight { return }
+    @objc func gestureOpenPdfThumbnail(_ recognizer: UIScreenEdgePanGestureRecognizer) {
 
-            navigationController?.setNavigationBarHidden(true, animated: false)
-            pdfThumbnailView.isHidden = true
-            pdfView.backgroundColor = .black
+        if UIDevice.current.userInterfaceIdiom == .phone && self.pdfThumbnailScrollView.isHidden {
+            if let tipView = self.tipView {
+                tipView.dismiss()
+                NCManageDatabase.shared.addTip(NCGlobal.shared.tipNCViewerPDFThumbnail)
+                self.tipView = nil
+            }
+            self.pdfThumbnailScrollView.isHidden = false
+            self.pdfThumbnailScrollViewWidthAnchor?.constant = thumbnailViewWidth + (UIApplication.shared.keyWindow?.safeAreaInsets.right ?? 0)
+            UIView.animate(withDuration: animateDuration, animations: {
+                self.pdfThumbnailScrollViewTrailingAnchor?.constant = 0
+                self.view.layoutIfNeeded()
+            })
         }
+    }
 
-        handlePageChange()
+    @objc func gestureClosePdfThumbnail(_ recognizer: UIScreenEdgePanGestureRecognizer) {
+
+        if recognizer.state == .recognized && UIDevice.current.userInterfaceIdiom == .phone && !self.pdfThumbnailScrollView.isHidden {
+            UIView.animate(withDuration: animateDuration) {
+                self.pdfThumbnailScrollViewTrailingAnchor?.constant = self.thumbnailViewWidth + (UIApplication.shared.keyWindow?.safeAreaInsets.right ?? 0)
+                self.view.layoutIfNeeded()
+            } completion: { _ in
+                self.pdfThumbnailScrollView.isHidden = true
+            }
+        }
     }
 
     // MARK: -
 
+    func setConstraints() {
+
+        let widthThumbnail = thumbnailViewWidth + (UIApplication.shared.keyWindow?.safeAreaInsets.right ?? 0)
+
+        UIView.animate(withDuration: animateDuration, animations: {
+            if UIDevice.current.userInterfaceIdiom == .phone {
+                // Close
+                self.pdfThumbnailScrollView.isHidden = true
+                self.pdfThumbnailScrollViewTrailingAnchor?.constant = widthThumbnail
+                self.pdfThumbnailScrollViewWidthAnchor?.constant = widthThumbnail
+            } else {
+                // Open
+                self.pdfThumbnailScrollViewTrailingAnchor?.constant = 0
+                self.pdfThumbnailScrollViewWidthAnchor?.constant = widthThumbnail
+            }
+            self.view.layoutIfNeeded()
+            self.pdfView.autoScales = true
+        })
+    }
+
     @objc func handlePageChange() {
 
         guard let curPage = pdfView.currentPage?.pageRef?.pageNumber else { pageView.alpha = 0; return }
         guard let totalPages = pdfView.document?.pageCount else { return }
 
+        let visibleRect = CGRect(x: pdfThumbnailScrollView.contentOffset.x, y: pdfThumbnailScrollView.contentOffset.y, width: pdfThumbnailScrollView.bounds.size.width, height: pdfThumbnailScrollView.bounds.size.height)
+        let centerPoint = CGPoint(x: visibleRect.size.width/2, y: visibleRect.size.height/2)
+        let currentPageY = CGFloat(curPage) * thumbnailViewHeight + CGFloat(curPage) * thumbnailPadding
+        var gotoY = currentPageY - centerPoint.y
+
+        let startY = visibleRect.origin.y < 0 ? 0 : (visibleRect.origin.y + thumbnailViewHeight)
+        let endY = visibleRect.origin.y + visibleRect.height
+
+        if currentPageY < startY {
+            if gotoY < 0 { gotoY = 0 }
+            pdfThumbnailScrollView.setContentOffset(CGPoint(x: 0, y: gotoY), animated: true)
+        } else if currentPageY > endY {
+            if gotoY > pdfThumbnailView.frame.height - visibleRect.height {
+                gotoY = pdfThumbnailView.frame.height - visibleRect.height
+            }
+            pdfThumbnailScrollView.setContentOffset(CGPoint(x: 0, y: gotoY), animated: true)
+        } else {
+            print("visible")
+        }
+
         pageView.alpha = 1
         pageViewLabel.text = String(curPage) + " " + NSLocalizedString("_of_", comment: "") + " " + String(totalPages)
         pageViewWidthAnchor?.constant = pageViewLabel.intrinsicContentSize.width + 10
 
-        UIView.animate(withDuration: 1.0, delay: 3.0, animations: {
+        UIView.animate(withDuration: 1.0, delay: 2.5, animations: {
             self.pageView.alpha = 0
         })
     }
@@ -341,3 +496,12 @@ class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate {
          }
      }
 }
+
+extension NCViewerPDF: EasyTipViewDelegate {
+
+    func easyTipViewDidTap(_ tipView: EasyTipView) {
+        NCManageDatabase.shared.addTip(NCGlobal.shared.tipNCViewerPDFThumbnail)
+    }
+
+    func easyTipViewDidDismiss(_ tipView: EasyTipView) { }
+}