diff --git a/Kiwix-iOS/Controller/Bookmark/BookmarkController.swift b/Kiwix-iOS/Controller/Bookmark/BookmarkController.swift index 1f25f46f..ea52671c 100644 --- a/Kiwix-iOS/Controller/Bookmark/BookmarkController.swift +++ b/Kiwix-iOS/Controller/Bookmark/BookmarkController.swift @@ -8,8 +8,10 @@ import UIKit import CoreData +import Operations +import DZNEmptyDataSet -class BookmarkController: UITableViewController, NSFetchedResultsControllerDelegate { +class BookmarkController: UITableViewController, NSFetchedResultsControllerDelegate, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate { var book: Book? @@ -17,8 +19,77 @@ class BookmarkController: UITableViewController, NSFetchedResultsControllerDeleg override func viewDidLoad() { super.viewDidLoad() + title = LocalizedStrings.bookmarks + clearsSelectionOnViewWillAppear = true + tableView.estimatedRowHeight = 66.0 + tableView.rowHeight = UITableViewAutomaticDimension + tableView.allowsMultipleSelectionDuringEditing = true + tableView.emptyDataSetSource = self + tableView.emptyDataSetDelegate = self } + override func setEditing(editing: Bool, animated: Bool) { + super.setEditing(editing, animated: animated) + navigationItem.leftBarButtonItem = editing ? UIBarButtonItem(barButtonSystemItem: .Trash) : nil + navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: editing ? .Done : .Edit) + navigationItem.leftBarButtonItem?.target = self + navigationItem.leftBarButtonItem?.action = #selector(BookmarkController.trashButtonTapped(_:)) + navigationItem.rightBarButtonItem?.target = self + navigationItem.rightBarButtonItem?.action = #selector(BookmarkController.editButtonTapped(_:)) + } + + // MARK: - Action + + func trash(articles articles: [Article]) { + let operation = BookmarkTrashOperation(articles: articles) + operation.addObserver(DidFinishObserver { _ in + NSOperationQueue.mainQueue().addOperationWithBlock({ + guard self.fetchedResultController.fetchedObjects?.count == 0 else {return} + self.navigationController?.popViewControllerAnimated(true) + }) + }) + GlobalQueue.shared.addOperation(operation) + } + + func trashButtonTapped(sender: UIBarButtonItem) { + guard editing else {return} + guard let selectedIndexPathes = tableView.indexPathsForSelectedRows else {return} + let articles = selectedIndexPathes.flatMap() {fetchedResultController.objectAtIndexPath($0) as? Article} + trash(articles: articles) + } + + @IBAction func editButtonTapped(sender: UIBarButtonItem) { + setEditing(!editing, animated: true) + } + + // MARK: - Empty table datasource & delegate + + func imageForEmptyDataSet(scrollView: UIScrollView!) -> UIImage! { + return UIImage(named: "BookmarkColor") + } + + func titleForEmptyDataSet(scrollView: UIScrollView!) -> NSAttributedString! { + let text = NSLocalizedString("Bookmarks", comment: "Bookmarks view title") + let attributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(18.0), + NSForegroundColorAttributeName: UIColor.darkGrayColor()] + return NSAttributedString(string: text, attributes: attributes) + } + + func descriptionForEmptyDataSet(scrollView: UIScrollView!) -> NSAttributedString! { + let text = NSLocalizedString("To add a bookmark, long press the star button when reading an article", comment: "Bookmarks view message") + let style = NSMutableParagraphStyle() + style.lineBreakMode = .ByWordWrapping + style.alignment = .Center + let attributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(14.0), + NSForegroundColorAttributeName: UIColor.lightGrayColor(), + NSParagraphStyleAttributeName: style] + return NSAttributedString(string: text, attributes: attributes) + } + + func spaceHeightForEmptyDataSet(scrollView: UIScrollView!) -> CGFloat { + return 30.0 + } + // MARK: - Table view data source override func numberOfSectionsInTableView(tableView: UITableView) -> Int { @@ -32,9 +103,15 @@ class BookmarkController: UITableViewController, NSFetchedResultsControllerDeleg override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let article = fetchedResultController.objectAtIndexPath(indexPath) as? Article - let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) - cell.textLabel?.text = article?.title - return cell + if let _ = article?.snippet { + let cell = tableView.dequeueReusableCellWithIdentifier("BookmarkSnippetCell", forIndexPath: indexPath) + configureSnippetCell(cell, atIndexPath: indexPath) + return cell + } else { + let cell = tableView.dequeueReusableCellWithIdentifier("BookmarkCell", forIndexPath: indexPath) + configureCell(cell, atIndexPath: indexPath) + return cell + } } func configureCell(cell: UITableViewCell, atIndexPath indexPath: NSIndexPath) { @@ -49,6 +126,49 @@ class BookmarkController: UITableViewController, NSFetchedResultsControllerDeleg cell.subtitleLabel.text = article.book?.title } + func configureSnippetCell(cell: UITableViewCell, atIndexPath indexPath: NSIndexPath) { + configureCell(cell, atIndexPath: indexPath) + + guard let cell = cell as? BookmarkSnippetCell else {return} + guard let article = fetchedResultController.objectAtIndexPath(indexPath) as? Article else {return} + cell.snippetLabel.text = article.snippet + } + + // MARK: - Table view delegate + + override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { + guard !tableView.editing else {return} + defer {dismissViewControllerAnimated(true, completion: nil)} + guard let article = fetchedResultController.objectAtIndexPath(indexPath) as? Article, + let url = article.url else {return} + trash(articles: [article]) + + let operation = ArticleLoadOperation(url: url) + GlobalQueue.shared.add(load: operation) + } + + override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { + return true + } + + override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {} + + override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? { + let remove = UITableViewRowAction(style: .Destructive, title: LocalizedStrings.remove) { (action, indexPath) -> Void in + guard let article = self.fetchedResultController.objectAtIndexPath(indexPath) as? Article else {return} + let context = NSManagedObjectContext.mainQueueContext + context.performBlockAndWait({ () -> Void in + article.isBookmarked = false + }) + self.trash(articles: [article]) + } + return [remove] + } + + override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return CGFloat.min + } + // MARK: - Fetched Result Controller Delegate let managedObjectContext = NSManagedObjectContext.mainQueueContext @@ -93,17 +213,17 @@ class BookmarkController: UITableViewController, NSFetchedResultsControllerDeleg switch type { case .Insert: guard let newIndexPath = newIndexPath else {return} - tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade) + tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Left) case .Delete: guard let indexPath = indexPath else {return} - tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) + tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Right) case .Update: guard let indexPath = indexPath, let cell = tableView.cellForRowAtIndexPath(indexPath) else {return} configureCell(cell, atIndexPath: indexPath) case .Move: guard let indexPath = indexPath, let newIndexPath = newIndexPath else {return} - tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) - tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade) + tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Right) + tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Left) } } diff --git a/Kiwix-iOS/Controller/Library/BookDetailController.swift b/Kiwix-iOS/Controller/Library/BookDetailController.swift index 8c9a69f0..2f812977 100644 --- a/Kiwix-iOS/Controller/Library/BookDetailController.swift +++ b/Kiwix-iOS/Controller/Library/BookDetailController.swift @@ -24,7 +24,10 @@ class BookDetailController: UITableViewController, DZNEmptyDataSetSource, DZNEmp private(set) var sectionHeaders = [String?]() private(set) var sectionFooters = [String?]() private(set) var cellTitles = [[String]]() - private(set) var bookmarkCount = 0 + var bookmarkCount: Int? { + guard let book = book else {return nil} + return Article.fetchBookmarked(in: book, with: NSManagedObjectContext.mainQueueContext).count + } override func viewDidLoad() { super.viewDidLoad() @@ -41,8 +44,10 @@ class BookDetailController: UITableViewController, DZNEmptyDataSetSource, DZNEmp override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) - configureViews() book?.addObserver(self, forKeyPath: "stateRaw", options: .New, context: context) + configureViews() + tableView.reloadEmptyDataSet() + tableView.reloadData() } override func viewWillDisappear(animated: Bool) { @@ -104,7 +109,6 @@ class BookDetailController: UITableViewController, DZNEmptyDataSetSource, DZNEmp } func configureBookmarkSection(with book: Book) { - bookmarkCount = Article.fetchBookmarked(in: book, with: NSManagedObjectContext.mainQueueContext).count guard bookmarkCount > 0 else {return} sectionHeaders.append(nil) sectionFooters.append(nil) @@ -143,6 +147,10 @@ class BookDetailController: UITableViewController, DZNEmptyDataSetSource, DZNEmp } func configureViews() { + sectionHeaders.removeAll() + sectionFooters.removeAll() + cellTitles.removeAll() + guard let book = book else {return} configureStaticHeader(with: book) configureIndicators(with: book) @@ -152,7 +160,6 @@ class BookDetailController: UITableViewController, DZNEmptyDataSetSource, DZNEmp configureBookInfoSection(with: book) configurePIDSection(with: book) configureURLSection(with: book) - tableView.reloadEmptyDataSet() } // MARK: - Table view data source @@ -194,7 +201,7 @@ class BookDetailController: UITableViewController, DZNEmptyDataSetSource, DZNEmp case LocalizedStrings.bookmarks: let cell = tableView.dequeueReusableCellWithIdentifier("DetailSegueCell", forIndexPath: indexPath) cell.textLabel?.text = title - cell.detailTextLabel?.text = "\(bookmarkCount)" + cell.detailTextLabel?.text = String(bookmarkCount ?? 0) return cell default: let cell = tableView.dequeueReusableCellWithIdentifier("RightDetailCell", forIndexPath: indexPath) diff --git a/Kiwix-iOS/Controller/Main/JSInjection.swift b/Kiwix-iOS/Controller/Main/JSInjection.swift index e6cf9fd9..f5eab7a3 100644 --- a/Kiwix-iOS/Controller/Main/JSInjection.swift +++ b/Kiwix-iOS/Controller/Main/JSInjection.swift @@ -54,6 +54,6 @@ class JSInjection { let path = NSBundle.mainBundle().pathForResource("getSnippet", ofType: "js"), let jString = try? String(contentsOfFile: path), let snippet = context.evaluateScript(jString).toString() else {return nil} - return snippet + return snippet == "null" ? nil : snippet } } diff --git a/Kiwix-iOS/Info.plist b/Kiwix-iOS/Info.plist index ad26be19..eac698ed 100644 --- a/Kiwix-iOS/Info.plist +++ b/Kiwix-iOS/Info.plist @@ -49,7 +49,7 @@ CFBundleVersion - 1.8.1369 + 1.8.1454 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/Kiwix-iOS/JavaScripts/getSnippet.js b/Kiwix-iOS/JavaScripts/getSnippet.js index 20acba76..d508e013 100644 --- a/Kiwix-iOS/JavaScripts/getSnippet.js +++ b/Kiwix-iOS/JavaScripts/getSnippet.js @@ -13,7 +13,7 @@ function getSnippet() { } } } - } + return null; } -getSnippet(); \ No newline at end of file +getSnippet(); diff --git a/Kiwix-iOS/Storyboard/Bookmark.storyboard b/Kiwix-iOS/Storyboard/Bookmark.storyboard index c0edf51e..522cb765 100644 --- a/Kiwix-iOS/Storyboard/Bookmark.storyboard +++ b/Kiwix-iOS/Storyboard/Bookmark.storyboard @@ -19,10 +19,10 @@ - + - + - + - +