2 // BookDetailsViewController.swift
5 // Created by Pawel Dabrowski on 19/06/2018.
6 // Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
10 import MZDownloadManager
14 class BookDetailsViewController: WLViewController {
16 var bookDetailsModel: BookDetailsModel?
17 private var bookSlug: String!
18 private var isFavourite = false
19 private var isBookPremium: Bool = false
20 @IBOutlet weak var headerView: UIView!
21 @IBOutlet weak var refreshButton: ActivityIndicatorButton!
22 @IBOutlet weak var backButton: UIButton!
23 @IBOutlet weak var tableView: UITableView!
25 @IBOutlet weak var favouriteButton: UIButton!
26 @IBOutlet weak var shareButton: UIButton!
27 @IBOutlet weak var buttonsContainer: UIView!
28 @IBOutlet weak var headerHeightConstraint: NSLayoutConstraint!
29 @IBOutlet weak var buttonsContainerWidthConstraint: NSLayoutConstraint!
31 @IBOutlet weak var becomeFriendView: UIView!
32 @IBOutlet weak var becomeFriendLabel: UILabel!
33 @IBOutlet weak var becomeFriendButton: UIButton!
34 @IBOutlet weak var becomeFriendStarImageView: UIImageView!
35 @IBOutlet weak var headerLabel: UILabel!
37 var topColor = UIColor(red:0.91, green:0.31, blue:0.20, alpha:1.00)
38 var headerProgress: CGFloat = 100
40 var cellsArray = [WLTableViewCell]()
41 var readCell: BookDetailsButtonTableViewCell?
42 var audiobookCell: BookDetailsButtonTableViewCell?
43 var readingState: ReadingStateModel.ReadingState = .unknown
45 @IBAction func refreshButtonAction(_ sender: Any) {
49 @IBAction func becomeFriendButtonAction(_ sender: Any) {
50 onBecomeFriendButtonTapped()
53 @IBAction func backButtonAction(_ sender: Any) {
54 navigationController?.popViewController(animated: true)
57 @IBAction func favouriteButtonAction(_ sender: Any) {
58 setFavourite(favourite: !isFavourite)
61 @IBAction func sharebuttonAction(_ sender: UIButton) {
63 if let url = bookDetailsModel?.url {
64 self.share(string: url, button: sender)
68 static func instance(bookSlug: String, isBookPremium: Bool = false) -> BookDetailsViewController{
69 let controller = BookDetailsViewController.instance()
70 controller.bookSlug = bookSlug
71 controller.isBookPremium = isBookPremium
75 var bigHeaderHeight: CGFloat = 200
76 var smallHeaderHeight: CGFloat = 200
78 open override var preferredStatusBarStyle : UIStatusBarStyle {
82 override func viewDidLoad() {
86 tableView.isHidden = true
87 buttonsContainer.isHidden = true
88 becomeFriendStarImageView.tintColor = UIColor.white
89 becomeFriendButton.layer.cornerRadius = 18
90 refreshButton.tintColor = UIColor.white
92 bigHeaderHeight = 190 + UIApplication.shared.statusBarFrame.size.height
93 smallHeaderHeight = 44 + UIApplication.shared.statusBarFrame.size.height
94 headerHeightConstraint.constant = bigHeaderHeight
96 headerView.backgroundColor = Constants.Colors.navbarBgColor()
97 tableView.rowHeight = UITableViewAutomaticDimension
98 tableView.estimatedRowHeight = 200
99 tableView.separatorStyle = .none
100 tableView.registerNib(name: "BookDetailsHeaderTableViewCell")
101 tableView.registerNib(name: "BookDetailsInfoTableViewCell")
104 if syncManager.isLoggedIn() == false{
105 favouriteButton.isHidden = true
106 buttonsContainerWidthConstraint.constant = 42
109 let tintColor = UIColor(red:0.91, green:0.31, blue:0.20, alpha:1.00)
110 favouriteButton.tintColor = tintColor
111 shareButton.tintColor = tintColor
112 favouriteButton.layer.cornerRadius = 21
113 shareButton.layer.cornerRadius = 21
114 let buttonBorderColor = UIColor(red:0.96, green:0.96, blue:0.96, alpha:1.00)
115 favouriteButton.layer.borderColor = buttonBorderColor.cgColor
116 shareButton.layer.borderColor = buttonBorderColor.cgColor
117 favouriteButton.layer.borderWidth = 1.0
118 shareButton.layer.borderWidth = 1.0
120 edgesForExtendedLayout = []
121 extendedLayoutIncludesOpaqueBars = false
122 if #available(iOS 11.0, *) {
123 tableView.contentInsetAdjustmentBehavior = .never
125 automaticallyAdjustsScrollViewInsets = false
128 if isBookPremium && DatabaseManager.shared.isUserPremium() == false {
129 becomeFriendView.isHidden = false
130 becomeFriendLabel.text = "become_friend_desc".localized
131 becomeFriendButton.text = "become_friend_button".localized.uppercased()
134 becomeFriendView.isHidden = true
138 refreshReadingState()
141 func createCells(bookDetails: BookDetailsModel) {
142 cellsArray = [WLTableViewCell]()
143 self.bookDetailsModel = bookDetails
144 let titleCell = BookDetailsHeaderTableViewCell.instance(height: bigHeaderHeight)
145 titleCell.setup(bookModel: bookDetails, topColor: topColor)
146 cellsArray.append(titleCell)
147 let infoCell = BookDetailsInfoTableViewCell.instance()
148 infoCell.setup(bookModel: bookDetails)
149 cellsArray.append(infoCell)
150 cellsArray.append(BookDetailsSeparatorTableViewCell.instance())
152 if bookDetails.fragmentHtml.count > 0{
153 let fragmentCell = BookDetailsFragmentTableViewCell.instance()
154 fragmentCell.setup(fragmentTitle: bookDetails.fragmentTitle, fragmentHtml: bookDetails.fragmentHtml)
155 cellsArray.append( fragmentCell)
156 cellsArray.append(BookDetailsSeparatorTableViewCell.instance())
160 if isBookPremium && DatabaseManager.shared.isUserPremium() == false{ // dont show buttons when user is not premium
165 if bookDetails.epub.count > 0{
166 var buttonType = BookDetailsButtonType.download_ebook
167 switch DownloadManager.sharedInstance.checkEbookStatus(bookSlug: bookDetails.slug){
169 buttonType = .download_ebook_read
171 DownloadManager.sharedInstance.delegate = self
172 buttonType = .download_ebook_loading
176 readCell = BookDetailsButtonTableViewCell.instance(delegate: self, bookDetailsButtonType: buttonType, bookDetailsModel: bookDetails)
177 cellsArray.append(readCell!)
180 if bookDetails.getAudiobookFilesUrls().count > 0 {
181 var buttonType = BookDetailsButtonType.download_audiobook
183 switch DownloadManager.sharedInstance.checkAudiobookStatus(bookDetailsModel: bookDetails){
185 buttonType = .download_audiobook_listen
187 DownloadManager.sharedInstance.delegate = self
188 buttonType = .download_audiobook_loading
192 audiobookCell = BookDetailsButtonTableViewCell.instance(delegate: self, bookDetailsButtonType: buttonType, bookDetailsModel: bookDetails)
193 cellsArray.append(audiobookCell!)
196 // cellsArray.append(BookDetailsButtonTableViewCell.instance(delegate: self, bookDetailsButtonType: .support_us, bookDetailsModel: bookDetails))
200 func bookDetailsDownloaded(bookDetails: BookDetailsModel){
201 self.bookDetailsModel = bookDetails
202 tableView.isHidden = false
203 buttonsContainer.isHidden = false
205 topColor = self.bookDetailsModel!.bgColor
206 headerLabel.text = bookDetails.title
207 createCells(bookDetails: self.bookDetailsModel!)
208 tableView.reloadData()
211 func setFavourite(favourite: Bool){
212 isFavourite = favourite
213 refreshFavouriteButton()
215 syncManager.setFavouriteState(slug: bookSlug, favourite: favourite) {[weak self] (result) in
217 guard let strongSelf = self else{
221 strongSelf.refreshButton.setIndicatorButtonState(state: .hidden)
223 case .success/*(let model)*/:
224 strongSelf.isFavourite = favourite
225 case .failure/*(let error)*/:
232 func refreshFavouriteButton(){
233 favouriteButton.setImage(isFavourite ? #imageLiteral(resourceName: "icon_heart-fill-big") : #imageLiteral(resourceName: "icon_heart-outline-big"), for: .normal)
236 func refreshIsFavourite(){
237 guard syncManager.isLoggedIn() else { return }
239 syncManager.getFavouriteState(slug: bookSlug) {[weak self] (result) in
241 guard let strongSelf = self else{
246 case .success(let model):
247 strongSelf.isFavourite = (model as! LikeModel).likes
248 strongSelf.refreshFavouriteButton()
249 case .failure/*(let error)*/:
256 func refreshReadingState(){
258 guard syncManager.isLoggedIn() else {
259 readingState = .unknown
263 syncManager.getReadingState(slug: bookSlug, completionHandler: { [weak self] (result) in
265 guard let strongSelf = self else{
270 case .success(let model):
271 strongSelf.readingState = (model as! ReadingStateModel).state
272 case .failure/*(let error)*/:
280 var storedBook = false
281 if let downloadedModel = DatabaseManager.shared.getBookFromDownloaded(bookSlug: bookSlug) {
283 bookDetailsDownloaded(bookDetails: downloadedModel)
286 if storedBook == false {
287 refreshButton.setIndicatorButtonState(state: .loading)
290 syncManager.getBookDetails(bookSlug: bookSlug) {[weak self] (result) in
292 guard let strongSelf = self else{
296 strongSelf.refreshButton.setIndicatorButtonState(state: .hidden)
298 case .success(let model):
299 if let model = model as? BookDetailsModel{
300 model.slug = strongSelf.bookSlug
302 DatabaseManager.shared.addBookToDownloaded(bookDetailsModel: model)
304 strongSelf.bookDetailsDownloaded(bookDetails: model)
307 case .failure/*(let error)*/:
308 if storedBook == false {
309 strongSelf.refreshButton.setIndicatorButtonState(state: .button)
310 self?.view.makeToast("book_loading_error".localized, duration: 3.0, position: .bottom)
316 override func viewWillAppear(_ animated: Bool) {
317 super.viewWillAppear(animated)
319 navigationController?.setNavigationBarHidden(true, animated: true)
320 DownloadManager.sharedInstance.delegate = self
323 override func viewWillDisappear(_ animated: Bool) {
324 super.viewWillDisappear(animated)
326 navigationController?.setNavigationBarHidden(false, animated: true)
328 DownloadManager.sharedInstance.delegate = nil
331 func downloadEbook(){
332 guard let bookDetailsModel = bookDetailsModel, bookDetailsModel.epub.count > 0 else {return}
334 DatabaseManager.shared.addBookToDownloaded(bookDetailsModel: bookDetailsModel)
335 DownloadManager.sharedInstance.delegate = self
336 DownloadManager.sharedInstance.downloadEbook(bookDetailsModel: bookDetailsModel)
339 var openedPlayer = false
340 func downloadAudiobooks(){
341 guard let bookDetailsModel = bookDetailsModel, bookDetailsModel.getAudiobookFilesUrls().count > 0 else {return}
343 DatabaseManager.shared.addBookToDownloaded(bookDetailsModel: bookDetailsModel)
345 DownloadManager.sharedInstance.delegate = self
346 DownloadManager.sharedInstance.downloadAudiobooks(bookDetailsModel: bookDetailsModel)
349 func updateReadingStateIfNeeded(state: ReadingStateModel.ReadingState) {
350 if state == .reading && readingState != .not_started {
354 if state == .complete && readingState == .complete {
358 syncManager.setReadingState(slug: bookSlug, readingState: state, completionHandler: nil)
361 func openFolioReader() {
362 guard ebookExists(bookSlug: bookSlug) else {
366 updateReadingStateIfNeeded(state: .reading)
368 var array = parentNames()
369 array.append("Reader")
370 MatomoTracker.shared.track(view: array)
372 let config = WLReaderConfig()
373 let bookPath = FileType.ebook.pathForFileName(filename: bookSlug, bookSlug: bookSlug)
374 let bookDirectory = FileType.ebook.destinationPathWithSlug(bookSlug: bookSlug) + "unzipped/"
376 if !FileManager.default.fileExists(atPath: bookDirectory) {
377 try! FileManager.default.createDirectory(atPath: bookDirectory, withIntermediateDirectories: true, attributes: nil)
380 let folioReader = FolioReader()
381 folioReader.delegate = self
383 folioReader.presentReader(parentViewController: self.navigationController!, withEpubPath: bookPath, unzipPath: bookDirectory, andConfig: config, shouldRemoveEpub: false)
386 func openAudiobook(afterDownload: Bool) {
387 guard let bookDetailsModel = bookDetailsModel else { return }
389 updateReadingStateIfNeeded(state: .reading)
392 navigationController?.pushViewController(PlayerViewController.instance(bookDetailsModel: bookDetailsModel), animated: true)
399 extension BookDetailsViewController: UITableViewDataSource{
401 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
402 return cellsArray.count
405 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
406 return cellsArray[indexPath.row]
410 extension BookDetailsViewController: UITableViewDelegate{
411 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
412 return cellsArray[indexPath.row].getHeight()
415 func scrollViewDidScroll(_ scrollView: UIScrollView) {
416 print(scrollView.contentOffset)
418 var progress: CGFloat = 100
420 var newValue = bigHeaderHeight - scrollView.contentOffset.y
421 if newValue < smallHeaderHeight{
422 newValue = smallHeaderHeight
426 progress = (bigHeaderHeight - smallHeaderHeight - scrollView.contentOffset.y) / (bigHeaderHeight - smallHeaderHeight)
432 headerView.alpha = 1 - progress
433 buttonsContainer.alpha = progress
434 headerHeightConstraint.constant = newValue
435 headerProgress = progress
440 buttonsContainer.alpha = 0
441 headerHeightConstraint.constant = smallHeaderHeight
446 extension BookDetailsViewController: BookDetailsButtonTableViewCellDelegate{
448 func bookDetailsButtonTableViewCellButtonTapped(buttonType: BookDetailsButtonType){
450 case .download_ebook:
452 case .download_ebook_read:
454 case .download_audiobook:
456 case .download_audiobook_listen:
457 openAudiobook(afterDownload: false)
463 func bookDetailsButtonTableViewCellDeleteButtonTapped(buttonType: BookDetailsButtonType){
465 case .download_ebook_read, .download_ebook_loading:
466 DownloadManager.sharedInstance.deleteEbook(bookSlug: bookSlug)
467 readCell?.setup(bookDetailsButtonType: .download_ebook, progress: nil, bookDetailsModel: bookDetailsModel)
468 bookDetailsModel = (bookDetailsModel?.copy() as! BookDetailsModel)
469 let _ = DatabaseManager.shared.removeBookFromDownloaded(bookSlug: bookSlug)
470 case .download_audiobook_listen, .download_audiobook_loading:
471 if let bookDetails = PlayerController.shared.currentBookDetails, bookDetails.slug == bookSlug{
472 PlayerController.shared.stopAndClear()
474 DownloadManager.sharedInstance.clearDownloadingAudiobookFromQueue(bookSlug: bookSlug)
475 DownloadManager.sharedInstance.deleteAudiobook(bookSlug: bookSlug)
476 audiobookCell?.setup(bookDetailsButtonType: .download_audiobook, progress:nil, bookDetailsModel: bookDetailsModel)
477 bookDetailsModel = (bookDetailsModel?.copy() as! BookDetailsModel)
478 let _ = DatabaseManager.shared.removeBookFromDownloaded(bookSlug: bookSlug)
485 extension BookDetailsViewController: DownloadManagerDelegate{
487 func downloadManagerDownloadProgressChanged(model: MZDownloadModel, allProgress: Float, bookSlug: String) {
488 guard let bookDetailsModel = bookDetailsModel, bookDetailsModel.slug == bookSlug else { return }
490 if model.isAudiobook() {
491 audiobookCell?.setup(bookDetailsButtonType: .download_audiobook_loading, progress: allProgress, bookDetailsModel: bookDetailsModel)
493 else if model.isEbook() {
494 readCell?.setup(bookDetailsButtonType: .download_ebook_loading, progress: model.progress, bookDetailsModel: bookDetailsModel)
498 func downloadManagerDownloadFinished(model: MZDownloadModel, bookSlug: String) {
499 guard let bookDetailsModel = bookDetailsModel, bookDetailsModel.slug == bookSlug else { return }
501 if model.isAudiobook() {
503 bookDetailsModel.setInitialAudiobookChaptersValuesIfNeeded()
505 audiobookCell?.setup(bookDetailsButtonType: .download_audiobook_listen, progress: nil, bookDetailsModel: bookDetailsModel)
506 openAudiobook(afterDownload: true)
508 else if model.isEbook() {
509 readCell?.setup(bookDetailsButtonType: .download_ebook_read, progress: nil, bookDetailsModel: bookDetailsModel)
514 func downloadManagerDownloadFailed(model: MZDownloadModel, bookSlug: String) {
515 guard let bookDetailsModel = bookDetailsModel, bookDetailsModel.slug == bookSlug else { return }
517 if model.isAudiobook() {
518 view.makeToast("audiobook_download_error".localized, duration: 3.0, position: .bottom)
519 audiobookCell?.setup(bookDetailsButtonType: .download_audiobook, progress: nil, bookDetailsModel: bookDetailsModel)
521 else if model.isEbook() {
522 view.makeToast("book_download_error".localized, duration: 3.0, position: .bottom)
523 readCell?.setup(bookDetailsButtonType: .download_ebook, progress: nil, bookDetailsModel: bookDetailsModel)
528 extension BookDetailsViewController: FolioReaderDelegate{
529 @objc func folioReaderDidClose(_ folioReader: FolioReader) {
530 guard let bookDetailsModel = bookDetailsModel, let progressValues = folioReader.getProgressValues(), progressValues.currentPage > 0, progressValues.currentPage <= progressValues.totalPages else { return }
532 DatabaseManager.shared.updateCurrentChapters(bookDetailsModel: bookDetailsModel, currentChapter: progressValues.currentPage, totalChapters: progressValues.totalPages, currentAudioChapter: nil, totalAudioChapters: nil)
533 readCell?.setup(bookDetailsButtonType: .download_ebook_read, progress: nil, bookDetailsModel: bookDetailsModel)
535 if progressValues.currentPage == progressValues.totalPages && readingState == ReadingStateModel.ReadingState.reading{
536 updateReadingStateIfNeeded(state: .complete)