2019-04-15 15:03:05 -05:00
//
2019-09-24 04:29:15 -05:00
// A r t i c l e V i e w C o n t r o l l e r . s w i f t
2019-04-15 15:03:05 -05:00
// N e t N e w s W i r e
//
// C r e a t e d b y M a u r i c e P a r k e r o n 4 / 8 / 1 9 .
// C o p y r i g h t © 2 0 1 9 R a n c h e r o S o f t w a r e . A l l r i g h t s r e s e r v e d .
//
import UIKit
import WebKit
import Account
import Articles
2019-04-21 06:41:59 -05:00
import SafariServices
2019-04-15 15:03:05 -05:00
2022-01-27 15:26:40 -08:00
class ArticleViewController : UIViewController , MainControllerIdentifiable {
2019-10-12 14:45:44 -05:00
2020-01-29 11:30:52 -07:00
typealias State = ( extractedArticle : ExtractedArticle ? ,
2022-02-10 12:13:06 +08:00
isShowingExtractedArticle : Bool ,
articleExtractorButtonState : ArticleExtractorButtonState ,
windowScrollY : Int )
2019-08-31 12:30:01 -07:00
@IBOutlet private weak var nextUnreadBarButtonItem : UIBarButtonItem !
@IBOutlet private weak var prevArticleBarButtonItem : UIBarButtonItem !
@IBOutlet private weak var nextArticleBarButtonItem : UIBarButtonItem !
@IBOutlet private weak var readBarButtonItem : UIBarButtonItem !
@IBOutlet private weak var starBarButtonItem : UIBarButtonItem !
@IBOutlet private weak var actionBarButtonItem : UIBarButtonItem !
2022-02-10 12:13:06 +08:00
@IBOutlet private weak var appearanceBarButtonItem : UIBarButtonItem !
2019-12-31 16:55:39 -07:00
2020-05-11 16:08:01 -04:00
@IBOutlet private var searchBar : ArticleSearchBar !
2020-05-15 17:56:14 -04:00
@IBOutlet private var searchBarBottomConstraint : NSLayoutConstraint !
2020-05-11 16:08:01 -04:00
private var defaultControls : [ UIBarButtonItem ] ?
2019-12-31 16:55:39 -07:00
private var pageViewController : UIPageViewController !
private var currentWebViewController : WebViewController ? {
return pageViewController ? . viewControllers ? . first as ? WebViewController
}
2019-11-17 20:20:50 -06:00
2019-09-27 14:09:28 -05:00
private var articleExtractorButton : ArticleExtractorButton = {
let button = ArticleExtractorButton ( type : . system )
2019-09-27 17:32:13 -05:00
button . frame = CGRect ( x : 0 , y : 0 , width : 44.0 , height : 44.0 )
2019-09-27 14:09:28 -05:00
button . setImage ( AppAssets . articleExtractorOff , for : . normal )
return button
} ( )
2022-01-27 15:26:40 -08:00
var mainControllerIdentifer = MainControllerIdentifier . article
2019-09-01 12:43:07 -05:00
weak var coordinator : SceneCoordinator !
2019-04-15 15:03:05 -05:00
2022-01-26 15:40:47 -08:00
private let poppableDelegate = PoppableGestureRecognizerDelegate ( )
2022-02-10 12:13:06 +08:00
2019-12-31 16:55:39 -07:00
var article : Article ? {
2019-09-23 19:23:23 -05:00
didSet {
2019-12-31 16:55:39 -07:00
if let controller = currentWebViewController , controller . article != article {
2020-03-14 06:31:14 -05:00
controller . setArticle ( article )
2019-12-31 16:55:39 -07:00
DispatchQueue . main . async {
// Y o u h a v e t o s e t t h e v i e w c o n t r o l l e r t o c l e a r o u t t h e U I P a g e V i e w C o n t r o l l e r c h i l d c o n t r o l l e r c a c h e .
// Y o u a l s o h a v e t o d o i t i n a n a s y n c c a l l o r y o u w i l l g e t a s t r a n g e a s s e r t i o n e r r o r .
self . pageViewController . setViewControllers ( [ controller ] , direction : . forward , animated : false , completion : nil )
}
2019-09-23 19:23:23 -05:00
}
2019-12-31 16:55:39 -07:00
updateUI ( )
2019-09-24 16:34:11 -05:00
}
}
2020-01-29 11:30:52 -07:00
2021-09-13 02:22:15 -05:00
var restoreScrollPosition : ( isShowingExtractedArticle : Bool , articleWindowScrollY : Int ) ? {
didSet {
if let rsp = restoreScrollPosition {
currentWebViewController ? . setScrollPosition ( isShowingExtractedArticle : rsp . isShowingExtractedArticle , articleWindowScrollY : rsp . articleWindowScrollY )
}
}
}
2020-01-29 11:30:52 -07:00
var currentState : State ? {
guard let controller = currentWebViewController else { return nil }
return State ( extractedArticle : controller . extractedArticle ,
isShowingExtractedArticle : controller . isShowingExtractedArticle ,
articleExtractorButtonState : controller . articleExtractorButtonState ,
windowScrollY : controller . windowScrollY )
}
var restoreState : State ?
2019-09-24 16:34:11 -05:00
2019-09-05 14:37:07 -05:00
private let keyboardManager = KeyboardManager ( type : . detail )
2019-09-04 21:06:29 -05:00
override var keyCommands : [ UIKeyCommand ] ? {
return keyboardManager . keyCommands
}
2022-10-05 16:43:31 -05:00
private var lastKnownDisplayMode : UISplitViewController . DisplayMode ?
2022-02-13 08:06:10 +08:00
var currentUnreadCount : Int = 0 {
didSet {
updateUnreadCountIndicator ( )
}
}
2019-04-15 15:03:05 -05:00
override func viewDidLoad ( ) {
super . viewDidLoad ( )
2022-02-10 12:13:06 +08:00
2019-04-23 04:35:48 -05:00
NotificationCenter . default . addObserver ( self , selector : #selector ( unreadCountDidChange ( _ : ) ) , name : . UnreadCountDidChange , object : nil )
2019-04-15 15:03:05 -05:00
NotificationCenter . default . addObserver ( self , selector : #selector ( statusesDidChange ( _ : ) ) , name : . StatusesDidChange , object : nil )
2020-02-04 16:00:26 -08:00
NotificationCenter . default . addObserver ( self , selector : #selector ( contentSizeCategoryDidChange ( _ : ) ) , name : UIContentSizeCategory . didChangeNotification , object : nil )
2019-11-18 19:12:24 -06:00
NotificationCenter . default . addObserver ( self , selector : #selector ( willEnterForeground ( _ : ) ) , name : UIApplication . willEnterForegroundNotification , object : nil )
2022-02-10 12:13:06 +08:00
NotificationCenter . default . addObserver ( self , selector : #selector ( reloadDueToThemeChange ( _ : ) ) , name : . CurrentArticleThemeDidChangeNotification , object : nil )
2022-02-10 22:34:04 +08:00
NotificationCenter . default . addObserver ( self , selector : #selector ( configureAppearanceMenu ( _ : ) ) , name : . ArticleThemeNamesDidChangeNotification , object : nil )
2019-12-26 12:21:56 -07:00
2019-09-24 16:34:11 -05:00
articleExtractorButton . addTarget ( self , action : #selector ( toggleArticleExtractor ( _ : ) ) , for : . touchUpInside )
2019-11-06 08:08:08 -06:00
toolbarItems ? . insert ( UIBarButtonItem ( customView : articleExtractorButton ) , at : 6 )
2021-04-10 09:03:08 +08:00
2022-01-26 15:40:47 -08:00
if let parentNavController = navigationController ? . parent as ? UINavigationController {
poppableDelegate . navigationController = parentNavController
parentNavController . interactivePopGestureRecognizer ? . delegate = poppableDelegate
}
2022-02-12 11:35:13 +08:00
navigationItem . leftItemsSupplementBackButton = true
2019-12-31 16:55:39 -07:00
pageViewController = UIPageViewController ( transitionStyle : . scroll , navigationOrientation : . horizontal , options : [ : ] )
pageViewController . delegate = self
pageViewController . dataSource = self
2022-02-10 12:13:06 +08:00
2020-04-12 11:48:14 -05:00
// T h i s c o d e i s t o d i s a l l o w p a g i n g i f w e s c r o l l f r o m t h e l e f t e d g e . I f t h i s c o d e i s r e m o v e d
// P o p p a b l e G e s t u r e R e c o g n i z e r D e l e g a t e w i l l a l l o w u s t o b o t h n a v i g a t e b a c k a n d p a g e b a c k a t t h e
// s a m e t i m e . T h a t i s r e a l l y w e i r d w h e n i t h a p p e n s .
let panGestureRecognizer = UIPanGestureRecognizer ( )
panGestureRecognizer . delegate = self
pageViewController . scrollViewInsidePageControl ? . addGestureRecognizer ( panGestureRecognizer )
2022-02-10 12:13:06 +08:00
2020-01-16 21:29:10 -07:00
pageViewController . view . translatesAutoresizingMaskIntoConstraints = false
2019-12-31 16:55:39 -07:00
view . addSubview ( pageViewController . view )
addChild ( pageViewController ! )
NSLayoutConstraint . activate ( [
view . leadingAnchor . constraint ( equalTo : pageViewController . view . leadingAnchor ) ,
view . trailingAnchor . constraint ( equalTo : pageViewController . view . trailingAnchor ) ,
view . topAnchor . constraint ( equalTo : pageViewController . view . topAnchor ) ,
view . bottomAnchor . constraint ( equalTo : pageViewController . view . bottomAnchor )
] )
2022-02-10 12:13:06 +08:00
2020-03-16 07:58:51 -05:00
let controller : WebViewController
2020-01-29 11:30:52 -07:00
if let state = restoreState {
2020-03-16 07:58:51 -05:00
controller = createWebViewController ( article , updateView : false )
2020-01-29 11:30:52 -07:00
controller . extractedArticle = state . extractedArticle
controller . isShowingExtractedArticle = state . isShowingExtractedArticle
controller . articleExtractorButtonState = state . articleExtractorButtonState
controller . windowScrollY = state . windowScrollY
2020-03-16 07:58:51 -05:00
} else {
controller = createWebViewController ( article , updateView : true )
2020-01-29 11:30:52 -07:00
}
2020-03-16 07:58:51 -05:00
2021-09-13 02:22:15 -05:00
if let rsp = restoreScrollPosition {
controller . setScrollPosition ( isShowingExtractedArticle : rsp . isShowingExtractedArticle , articleWindowScrollY : rsp . articleWindowScrollY )
}
2022-02-10 12:13:06 +08:00
2020-01-15 17:28:37 -07:00
articleExtractorButton . buttonState = controller . articleExtractorButtonState
2020-02-25 18:06:02 -08:00
2020-03-11 18:17:09 -06:00
self . pageViewController . setViewControllers ( [ controller ] , direction : . forward , animated : false , completion : nil )
2020-05-11 16:08:01 -04:00
// S e a r c h b a r
2020-05-15 17:56:14 -04:00
searchBar . translatesAutoresizingMaskIntoConstraints = false
2020-05-11 16:08:01 -04:00
NotificationCenter . default . addObserver ( self , selector : #selector ( beginFind ( _ : ) ) , name : . FindInArticle , object : nil )
NotificationCenter . default . addObserver ( self , selector : #selector ( endFind ( _ : ) ) , name : . EndFindInArticle , object : nil )
2020-05-15 17:56:14 -04:00
NotificationCenter . default . addObserver ( self , selector : #selector ( keyboardWillChangeFrame ( _ : ) ) , name : UIWindow . keyboardWillChangeFrameNotification , object : nil )
searchBar . delegate = self
2020-05-11 16:08:01 -04:00
view . bringSubviewToFront ( searchBar )
2019-12-31 16:55:39 -07:00
updateUI ( )
2019-04-15 15:03:05 -05:00
}
2022-01-26 15:40:47 -08:00
override func viewWillAppear ( _ animated : Bool ) {
navigationController ? . isToolbarHidden = false
2022-01-28 16:15:14 -08:00
if AppDefaults . shared . articleFullscreenEnabled {
currentWebViewController ? . hideBars ( )
}
2022-02-10 12:13:06 +08:00
2022-01-26 15:40:47 -08:00
super . viewWillAppear ( animated )
}
2022-02-10 12:13:06 +08:00
2020-05-11 16:08:01 -04:00
override func viewWillDisappear ( _ animated : Bool ) {
2022-01-26 15:40:47 -08:00
super . viewWillDisappear ( animated )
2020-05-15 17:56:14 -04:00
if searchBar != nil && ! searchBar . isHidden {
endFind ( )
}
2020-05-11 16:08:01 -04:00
}
2019-11-24 03:42:38 -06:00
override func viewSafeAreaInsetsDidChange ( ) {
2019-11-24 13:41:32 -06:00
// T h i s w i l l a n i m a t e i f t h e s h o w / h i d e b a r s a n i m a t i o n i s h a p p e n i n g .
view . layoutIfNeeded ( )
2019-11-24 03:42:38 -06:00
}
2019-04-23 04:35:48 -05:00
func updateUI ( ) {
2019-04-15 15:03:05 -05:00
2019-12-31 16:55:39 -07:00
guard let article = article else {
2019-09-24 16:34:11 -05:00
articleExtractorButton . isEnabled = false
2019-04-21 17:42:26 -05:00
nextUnreadBarButtonItem . isEnabled = false
prevArticleBarButtonItem . isEnabled = false
nextArticleBarButtonItem . isEnabled = false
2019-04-15 15:03:05 -05:00
readBarButtonItem . isEnabled = false
starBarButtonItem . isEnabled = false
actionBarButtonItem . isEnabled = false
2022-02-10 12:13:06 +08:00
appearanceBarButtonItem . isEnabled = false
2019-04-15 15:03:05 -05:00
return
}
2022-02-10 12:13:06 +08:00
2019-07-05 17:45:39 -05:00
nextUnreadBarButtonItem . isEnabled = coordinator . isAnyUnreadAvailable
prevArticleBarButtonItem . isEnabled = coordinator . isPrevArticleAvailable
nextArticleBarButtonItem . isEnabled = coordinator . isNextArticleAvailable
2019-04-15 15:03:05 -05:00
readBarButtonItem . isEnabled = true
starBarButtonItem . isEnabled = true
2022-02-10 12:13:06 +08:00
appearanceBarButtonItem . isEnabled = true
2020-03-13 20:21:18 +01:00
let permalinkPresent = article . preferredLink != nil
2021-04-10 09:03:08 +08:00
var isFeedProvider = false
if let webfeed = article . webFeed {
isFeedProvider = webfeed . isFeedProvider
}
articleExtractorButton . isEnabled = permalinkPresent && ! AppDefaults . shared . isDeveloperBuild && ! isFeedProvider
2020-03-13 20:21:18 +01:00
actionBarButtonItem . isEnabled = permalinkPresent
2020-01-09 14:38:25 -07:00
if article . status . read {
readBarButtonItem . image = AppAssets . circleOpenImage
2020-02-18 13:49:29 -08:00
readBarButtonItem . isEnabled = article . isAvailableToMarkUnread
2020-01-09 14:38:25 -07:00
readBarButtonItem . accLabelText = NSLocalizedString ( " Mark Article Unread " , comment : " Mark Article Unread " )
} else {
readBarButtonItem . image = AppAssets . circleClosedImage
2020-02-18 13:49:29 -08:00
readBarButtonItem . isEnabled = true
2020-01-09 14:38:25 -07:00
readBarButtonItem . accLabelText = NSLocalizedString ( " Selected - Mark Article Unread " , comment : " Selected - Mark Article Unread " )
}
2019-04-15 15:03:05 -05:00
2020-01-09 14:38:25 -07:00
if article . status . starred {
starBarButtonItem . image = AppAssets . starClosedImage
starBarButtonItem . accLabelText = NSLocalizedString ( " Selected - Star Article " , comment : " Selected - Star Article " )
} else {
starBarButtonItem . image = AppAssets . starOpenImage
starBarButtonItem . accLabelText = NSLocalizedString ( " Star Article " , comment : " Star Article " )
}
2019-04-15 15:03:05 -05:00
2022-02-28 07:56:58 +08:00
configureAppearanceMenu ( )
2022-03-03 08:50:55 +08:00
configureArticleExtractorMenu ( )
2022-02-28 07:56:58 +08:00
2019-04-15 15:03:05 -05:00
}
2022-01-29 13:52:57 +08:00
override func contentScrollView ( for edge : NSDirectionalRectEdge ) -> UIScrollView ? {
return currentWebViewController ? . webView ? . scrollView
}
2022-03-03 08:33:53 +08:00
// / T h e a p p e a r a n c e m e n u i s d i f f e r e n t o n i P h o n e a n d i P a d .
// / O n i P a d , i t ' s o n l y t h e t h e m e s e l e c t o r . O n i P h o n e , t h e a p p e a r a n c e m e n u
// / c o n t a i n s t h e t h e t h e m e s e l e c t o r a n d f u l l s c r e e n o p t i o n s .
// / - P a r a m e t e r s e n d e r : ` A n y ? `
2022-02-10 12:13:06 +08:00
@objc
func configureAppearanceMenu ( _ sender : Any ? = nil ) {
var themeActions = [ UIAction ] ( )
for themeName in ArticleThemesManager . shared . themeNames {
let action = UIAction ( title : themeName ,
image : nil ,
identifier : nil ,
discoverabilityTitle : nil ,
attributes : [ ] ,
state : ArticleThemesManager . shared . currentThemeName = = themeName ? . on : . off ,
handler : { action in
ArticleThemesManager . shared . currentThemeName = themeName
} )
themeActions . append ( action )
}
2022-09-17 13:26:59 -05:00
let defaultThemeAction = UIAction ( title : NSLocalizedString ( " Default " , comment : " Default " ) ,
image : nil ,
identifier : nil ,
discoverabilityTitle : nil ,
attributes : [ ] ,
state : ArticleThemesManager . shared . currentThemeName = = AppDefaults . defaultThemeName ? . on : . off ,
handler : { _ in
2022-02-10 12:13:06 +08:00
ArticleThemesManager . shared . currentThemeName = AppDefaults . defaultThemeName
2022-09-17 13:26:59 -05:00
} )
2022-02-13 17:46:50 +08:00
let defaultThemeMenu = UIMenu ( title : " " , image : nil , identifier : nil , options : . displayInline , children : [ defaultThemeAction ] )
let customThemeMenu = UIMenu ( title : " " , image : nil , identifier : nil , options : . displayInline , children : themeActions )
2022-02-10 12:13:06 +08:00
2022-02-13 17:46:50 +08:00
let themeMenu = UIMenu ( title : " Theme " , image : AppAssets . themeImage , identifier : nil , options : . singleSelection , children : [ defaultThemeMenu , customThemeMenu ] )
2022-02-10 12:13:06 +08:00
2022-03-03 08:33:53 +08:00
if UIDevice . current . userInterfaceIdiom = = . pad {
appearanceBarButtonItem . image = AppAssets . themeImage
appearanceBarButtonItem . menu = themeMenu
return
}
2022-02-14 07:43:51 +08:00
var appearanceChildren : [ UIMenuElement ] = [ themeMenu ]
2022-02-10 12:13:06 +08:00
if let currentWebViewController = currentWebViewController {
if currentWebViewController . isFullScreenAvailable {
let fullScreenAction = UIAction ( title : NSLocalizedString ( " Full Screen " , comment : " Full Screen " ) ,
image : UIImage ( systemName : " arrow.up.backward.and.arrow.down.forward " ) ,
identifier : nil ,
discoverabilityTitle : nil ,
attributes : [ ] ,
state : . off ) { [ weak self ] _ in
self ? . currentWebViewController ? . hideBars ( )
2022-02-14 07:43:51 +08:00
if AppDefaults . shared . hasUsedFullScreenPreviously = = false {
let alert = UIAlertController ( title : NSLocalizedString ( " Exit Full Screen " , comment : " Full Screen " ) ,
message : NSLocalizedString ( " To exit Full Screen mode tap the top of the screen. \n \n You'll only see this message once. " , comment : " Full screen explainer. " ) ,
preferredStyle : . alert )
alert . addAction ( UIAlertAction ( title : NSLocalizedString ( " Dismiss " , comment : " Dismiss " ) , style : . default , handler : { _ in
AppDefaults . shared . hasUsedFullScreenPreviously = true
} ) )
self ? . present ( alert , animated : true , completion : nil )
}
2022-02-10 12:13:06 +08:00
}
2022-02-14 07:43:51 +08:00
appearanceChildren . append ( fullScreenAction )
2022-02-10 12:13:06 +08:00
}
}
2022-03-03 08:33:53 +08:00
appearanceBarButtonItem . image = AppAssets . articleAppearanceImage
2022-09-27 18:56:14 -05:00
appearanceBarButtonItem . menu = UIMenu ( title : NSLocalizedString ( " Article Appearance " , comment : " Appearance " ) , children : appearanceChildren )
2022-02-10 12:13:06 +08:00
}
2022-03-03 08:50:55 +08:00
private func configureArticleExtractorMenu ( ) {
if let feed = article ? . webFeed {
let extractorOn = feed . isArticleExtractorAlwaysOn ? ? false
let readerAction = UIAction ( title : NSLocalizedString ( " Always Use Reader View " , comment : " Always Use Reader View " ) ,
image : AppAssets . articleExtractorOffSF ,
identifier : nil ,
discoverabilityTitle : nil ,
attributes : [ ] ,
state : extractorOn ? . on : . off ) { [ weak self ] _ in
if feed . isArticleExtractorAlwaysOn = = nil {
feed . isArticleExtractorAlwaysOn = true
} else {
feed . isArticleExtractorAlwaysOn ? . toggle ( )
}
self ? . configureArticleExtractorMenu ( )
}
let menu = UIMenu ( title : feed . nameForDisplay , image : AppAssets . articleExtractorOffSF , identifier : nil , options : . displayInline , children : [ readerAction ] )
articleExtractorButton . menu = menu
articleExtractorButton . showsMenuAsPrimaryAction = false
}
}
2022-02-14 07:43:51 +08:00
2022-02-10 12:13:06 +08:00
@objc
func reloadDueToThemeChange ( _ notification : Notification ) {
currentWebViewController ? . fullReload ( )
configureAppearanceMenu ( )
}
2019-04-23 04:35:48 -05:00
// MARK: N o t i f i c a t i o n s
@objc dynamic func unreadCountDidChange ( _ notification : Notification ) {
updateUI ( )
}
2019-04-15 15:03:05 -05:00
@objc func statusesDidChange ( _ note : Notification ) {
2019-12-16 22:45:59 -08:00
guard let articleIDs = note . userInfo ? [ Account . UserInfoKey . articleIDs ] as ? Set < String > else {
2019-04-15 15:03:05 -05:00
return
}
2019-12-31 16:55:39 -07:00
guard let article = article else {
2019-12-16 22:45:59 -08:00
return
}
2019-12-31 16:55:39 -07:00
if articleIDs . contains ( article . articleID ) {
2019-04-23 04:35:48 -05:00
updateUI ( )
2019-04-15 15:03:05 -05:00
}
}
2022-02-10 12:13:06 +08:00
2020-02-04 16:00:26 -08:00
@objc func contentSizeCategoryDidChange ( _ note : Notification ) {
2020-07-16 13:56:07 -05:00
currentWebViewController ? . fullReload ( )
2020-02-04 16:00:26 -08:00
}
2019-11-18 19:12:24 -06:00
@objc func willEnterForeground ( _ note : Notification ) {
2019-11-24 14:49:44 -06:00
// T h e t o o l b a r w i l l c o m e b a c k o n y o u i f y o u d o n ' t h i d e i t a g a i n
2020-07-02 10:47:45 +08:00
if AppDefaults . shared . articleFullscreenEnabled {
2019-12-31 16:55:39 -07:00
currentWebViewController ? . hideBars ( )
2019-11-24 14:49:44 -06:00
}
2019-11-18 19:12:24 -06:00
}
2019-04-21 17:42:26 -05:00
// MARK: A c t i o n s
2022-02-10 12:13:06 +08:00
2019-11-18 19:12:24 -06:00
@objc func showBars ( _ sender : Any ) {
2019-12-31 16:55:39 -07:00
currentWebViewController ? . showBars ( )
2019-11-17 20:20:50 -06:00
}
2022-02-10 12:13:06 +08:00
2019-09-24 16:34:11 -05:00
@IBAction func toggleArticleExtractor ( _ sender : Any ) {
2019-12-31 16:55:39 -07:00
currentWebViewController ? . toggleArticleExtractor ( )
2022-03-03 09:01:51 +08:00
configureArticleExtractorMenu ( )
2019-09-24 06:46:53 -05:00
}
2019-04-21 17:42:26 -05:00
@IBAction func nextUnread ( _ sender : Any ) {
2019-07-05 17:45:39 -05:00
coordinator . selectNextUnread ( )
2019-04-21 17:42:26 -05:00
}
@IBAction func prevArticle ( _ sender : Any ) {
2019-07-06 11:32:19 -05:00
coordinator . selectPrevArticle ( )
2019-04-21 17:42:26 -05:00
}
@IBAction func nextArticle ( _ sender : Any ) {
2019-07-06 11:32:19 -05:00
coordinator . selectNextArticle ( )
2019-04-21 17:42:26 -05:00
}
2019-04-15 15:03:05 -05:00
@IBAction func toggleRead ( _ sender : Any ) {
2019-07-06 11:49:53 -05:00
coordinator . toggleReadForCurrentArticle ( )
2019-04-15 15:03:05 -05:00
}
@IBAction func toggleStar ( _ sender : Any ) {
2019-09-05 15:43:01 -05:00
coordinator . toggleStarredForCurrentArticle ( )
2019-04-15 15:03:05 -05:00
}
@IBAction func showActivityDialog ( _ sender : Any ) {
2019-12-31 16:55:39 -07:00
currentWebViewController ? . showActivityDialog ( popOverBarButtonItem : actionBarButtonItem )
2019-04-15 15:03:05 -05:00
}
2022-02-10 12:13:06 +08:00
2020-05-13 17:29:59 +05:30
@objc func toggleReaderView ( _ sender : Any ? ) {
currentWebViewController ? . toggleArticleExtractor ( )
}
2019-08-25 11:38:04 -05:00
2019-09-04 21:06:29 -05:00
// MARK: K e y b o a r d S h o r t c u t s
2022-02-10 12:13:06 +08:00
2019-09-04 21:06:29 -05:00
@objc func navigateToTimeline ( _ sender : Any ? ) {
coordinator . navigateToTimeline ( )
}
2019-08-25 11:38:04 -05:00
// MARK: A P I
2022-02-10 12:13:06 +08:00
2019-09-04 21:06:29 -05:00
func focus ( ) {
2019-12-31 16:55:39 -07:00
currentWebViewController ? . focus ( )
2019-09-04 21:06:29 -05:00
}
2022-02-10 12:13:06 +08:00
2019-09-05 21:14:19 -05:00
func canScrollDown ( ) -> Bool {
2019-12-31 16:55:39 -07:00
return currentWebViewController ? . canScrollDown ( ) ? ? false
2019-09-05 21:14:19 -05:00
}
2022-02-10 12:13:06 +08:00
2020-07-10 13:51:41 -05:00
func canScrollUp ( ) -> Bool {
return currentWebViewController ? . canScrollUp ( ) ? ? false
}
2022-02-10 12:13:06 +08:00
2019-09-05 21:14:19 -05:00
func scrollPageDown ( ) {
2019-12-31 16:55:39 -07:00
currentWebViewController ? . scrollPageDown ( )
2019-10-16 16:40:49 -05:00
}
2022-02-10 12:13:06 +08:00
2020-07-10 13:51:41 -05:00
func scrollPageUp ( ) {
currentWebViewController ? . scrollPageUp ( )
}
2019-10-16 16:40:49 -05:00
2020-01-29 11:30:52 -07:00
func stopArticleExtractorIfProcessing ( ) {
currentWebViewController ? . stopArticleExtractorIfProcessing ( )
}
2022-02-10 12:13:06 +08:00
2020-05-15 12:39:33 +05:30
func openInAppBrowser ( ) {
currentWebViewController ? . openInAppBrowser ( )
2021-09-13 01:11:23 -05:00
}
func setScrollPosition ( isShowingExtractedArticle : Bool , articleWindowScrollY : Int ) {
currentWebViewController ? . setScrollPosition ( isShowingExtractedArticle : isShowingExtractedArticle , articleWindowScrollY : articleWindowScrollY )
}
2022-10-05 16:43:31 -05:00
public func splitViewControllerWillChangeTo ( displayMode : UISplitViewController . DisplayMode ) {
lastKnownDisplayMode = displayMode
updateUnreadCountIndicator ( )
}
2019-11-25 19:43:43 -06:00
}
2020-05-11 16:08:01 -04:00
// MARK: F i n d i n A r t i c l e
public extension Notification . Name {
static let FindInArticle = Notification . Name ( " FindInArticle " )
static let EndFindInArticle = Notification . Name ( " EndFindInArticle " )
}
extension ArticleViewController : SearchBarDelegate {
func searchBar ( _ searchBar : ArticleSearchBar , textDidChange searchText : String ) {
currentWebViewController ? . searchText ( searchText ) {
found in
searchBar . resultsCount = found . count
if let index = found . index {
searchBar . selectedResult = index + 1
}
}
}
func doneWasPressed ( _ searchBar : ArticleSearchBar ) {
NotificationCenter . default . post ( name : . EndFindInArticle , object : nil )
}
func nextWasPressed ( _ searchBar : ArticleSearchBar ) {
if searchBar . selectedResult < searchBar . resultsCount {
currentWebViewController ? . selectNextSearchResult ( )
searchBar . selectedResult += 1
}
}
func previousWasPressed ( _ searchBar : ArticleSearchBar ) {
if searchBar . selectedResult > 1 {
currentWebViewController ? . selectPreviousSearchResult ( )
searchBar . selectedResult -= 1
}
}
}
extension ArticleViewController {
2020-05-13 06:13:31 -04:00
@objc func beginFind ( _ _ : Any ? = nil ) {
2020-05-11 16:08:01 -04:00
searchBar . isHidden = false
navigationController ? . setToolbarHidden ( true , animated : true )
currentWebViewController ? . additionalSafeAreaInsets . bottom = searchBar . frame . height
searchBar . becomeFirstResponder ( )
}
2020-05-15 17:56:14 -04:00
@objc func endFind ( _ _ : Any ? = nil ) {
2020-05-11 16:08:01 -04:00
searchBar . resignFirstResponder ( )
searchBar . isHidden = true
navigationController ? . setToolbarHidden ( false , animated : true )
currentWebViewController ? . additionalSafeAreaInsets . bottom = 0
currentWebViewController ? . endSearch ( )
}
2020-05-15 17:56:14 -04:00
@objc func keyboardWillChangeFrame ( _ notification : Notification ) {
if ! searchBar . isHidden ,
2022-02-10 12:13:06 +08:00
let duration = notification . userInfo ? [ UIWindow . keyboardAnimationDurationUserInfoKey ] as ? Double ,
let curveRaw = notification . userInfo ? [ UIWindow . keyboardAnimationCurveUserInfoKey ] as ? UInt ,
let frame = notification . userInfo ? [ UIWindow . keyboardFrameEndUserInfoKey ] as ? CGRect {
2020-05-15 17:56:14 -04:00
let curve = UIView . AnimationOptions ( rawValue : curveRaw )
let newHeight = view . safeAreaLayoutGuide . layoutFrame . maxY - frame . minY
currentWebViewController ? . additionalSafeAreaInsets . bottom = newHeight + searchBar . frame . height + 10
self . searchBarBottomConstraint . constant = newHeight
UIView . animate ( withDuration : duration , delay : 0 , options : curve , animations : {
self . view . layoutIfNeeded ( )
} )
2020-05-11 16:08:01 -04:00
}
}
2020-05-15 17:56:14 -04:00
2020-05-11 16:08:01 -04:00
}
2020-05-15 17:56:14 -04:00
2019-12-31 16:55:39 -07:00
// MARK: W e b V i e w C o n t r o l l e r D e l e g a t e
2019-04-15 15:03:05 -05:00
2019-12-31 16:55:39 -07:00
extension ArticleViewController : WebViewControllerDelegate {
2020-01-21 11:05:47 -07:00
2019-12-31 16:55:39 -07:00
func webViewController ( _ webViewController : WebViewController , articleExtractorButtonStateDidUpdate buttonState : ArticleExtractorButtonState ) {
if webViewController = = = currentWebViewController {
articleExtractorButton . buttonState = buttonState
2019-04-15 15:03:05 -05:00
}
2019-09-21 12:43:15 -05:00
}
2019-04-15 15:03:05 -05:00
}
2019-04-23 07:26:35 -05:00
2019-12-31 16:55:39 -07:00
// MARK: U I P a g e V i e w C o n t r o l l e r D a t a S o u r c e
2019-10-12 14:45:44 -05:00
2019-12-31 16:55:39 -07:00
extension ArticleViewController : UIPageViewControllerDataSource {
2019-10-13 19:41:34 -05:00
2019-12-31 16:55:39 -07:00
func pageViewController ( _ pageViewController : UIPageViewController , viewControllerBefore viewController : UIViewController ) -> UIViewController ? {
2020-01-26 14:21:04 -07:00
guard let webViewController = viewController as ? WebViewController ,
2022-02-10 12:13:06 +08:00
let currentArticle = webViewController . article ,
let article = coordinator . findPrevArticle ( currentArticle ) else {
return nil
}
2019-12-31 16:55:39 -07:00
return createWebViewController ( article )
2019-10-16 11:31:20 -05:00
}
2019-12-31 16:55:39 -07:00
func pageViewController ( _ pageViewController : UIPageViewController , viewControllerAfter viewController : UIViewController ) -> UIViewController ? {
2020-01-26 14:21:04 -07:00
guard let webViewController = viewController as ? WebViewController ,
2022-02-10 12:13:06 +08:00
let currentArticle = webViewController . article ,
let article = coordinator . findNextArticle ( currentArticle ) else {
return nil
}
2019-12-31 16:55:39 -07:00
return createWebViewController ( article )
2019-10-16 11:31:20 -05:00
}
}
2019-12-31 16:55:39 -07:00
// MARK: U I P a g e V i e w C o n t r o l l e r D e l e g a t e
2019-10-15 18:08:13 -05:00
2019-12-31 16:55:39 -07:00
extension ArticleViewController : UIPageViewControllerDelegate {
2022-02-10 12:13:06 +08:00
2019-12-31 16:55:39 -07:00
func pageViewController ( _ pageViewController : UIPageViewController , didFinishAnimating finished : Bool , previousViewControllers : [ UIViewController ] , transitionCompleted completed : Bool ) {
guard finished , completed else { return }
guard let article = currentWebViewController ? . article else { return }
2020-02-18 11:08:38 -08:00
2020-01-29 16:31:50 -07:00
coordinator . selectArticle ( article , animations : [ . select , . scroll , . navigation ] )
2019-12-31 16:55:39 -07:00
articleExtractorButton . buttonState = currentWebViewController ? . articleExtractorButtonState ? ? . off
2020-02-18 11:08:38 -08:00
previousViewControllers . compactMap ( { $0 as ? WebViewController } ) . forEach ( { $0 . stopWebViewActivity ( ) } )
2019-10-15 18:08:13 -05:00
}
2019-10-12 14:45:44 -05:00
}
2020-04-12 11:48:14 -05:00
// MARK: U I G e s t u r e R e c o g n i z e r D e l e g a t e
extension ArticleViewController : UIGestureRecognizerDelegate {
2022-02-10 12:13:06 +08:00
func gestureRecognizerShouldBegin ( _ gestureRecognizer : UIGestureRecognizer ) -> Bool {
return true
}
func gestureRecognizer ( _ gestureRecognizer : UIGestureRecognizer , shouldRecognizeSimultaneouslyWith otherGestureRecognizer : UIGestureRecognizer ) -> Bool {
2020-04-12 11:48:14 -05:00
let point = gestureRecognizer . location ( in : nil )
if point . x > 40 {
return true
}
return false
2022-02-10 12:13:06 +08:00
}
2020-04-12 11:48:14 -05:00
}
2019-09-20 20:33:28 -05:00
// MARK: P r i v a t e
2019-09-24 04:29:15 -05:00
private extension ArticleViewController {
2019-04-23 07:26:35 -05:00
2020-03-14 06:31:14 -05:00
func createWebViewController ( _ article : Article ? , updateView : Bool = true ) -> WebViewController {
2019-12-31 16:55:39 -07:00
let controller = WebViewController ( )
controller . coordinator = coordinator
controller . delegate = self
2020-03-14 06:31:14 -05:00
controller . setArticle ( article , updateView : updateView )
2019-12-31 16:55:39 -07:00
return controller
2019-11-17 20:20:50 -06:00
}
2022-10-05 16:43:31 -05:00
func updateUnreadCountIndicator ( ) {
if currentUnreadCount > 0 && ( traitCollection . userInterfaceIdiom = = . phone || lastKnownDisplayMode = = . secondaryOnly ) {
let unreadCountView = MasterTimelineUnreadCountView ( frame : . zero )
unreadCountView . unreadCount = currentUnreadCount
unreadCountView . setFrameIfNotEqual ( CGRect ( x : 0 , y : 0 , width : unreadCountView . intrinsicContentSize . width , height : unreadCountView . intrinsicContentSize . height ) )
navigationItem . leftBarButtonItem = UIBarButtonItem ( customView : unreadCountView )
} else {
navigationItem . leftBarButtonItem = nil
}
}
2019-04-23 07:26:35 -05:00
}