Book detail bookmark

This commit is contained in:
Chris Li 2016-09-29 15:28:30 -04:00
parent b39774f521
commit 8ab153b0e8
12 changed files with 379 additions and 124 deletions

View File

@ -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,6 +19,75 @@ 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
@ -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)
}
}

View File

@ -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)

View File

@ -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
}
}

View File

@ -49,7 +49,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.8.1369</string>
<string>1.8.1454</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>

View File

@ -13,7 +13,7 @@ function getSnippet() {
}
}
}
}
return null;
}
getSnippet();

View File

@ -19,10 +19,10 @@
<color key="sectionIndexBackgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="BookmarkCell" rowHeight="66" id="HDv-lO-b6r" customClass="BookmarkCell" customModule="Kiwix" customModuleProvider="target">
<rect key="frame" x="0.0" y="120" width="375" height="66"/>
<rect key="frame" x="0.0" y="119.5" width="375" height="66"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="HDv-lO-b6r" id="OAl-D1-ec7">
<frame key="frameInset" width="375" height="65"/>
<frame key="frameInset" width="375" height="65.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="U1x-fn-tr7">
@ -64,10 +64,10 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="BookmarkSnippetCell" rowHeight="149" id="o5A-Xv-pf5" customClass="BookmarkSnippetCell" customModule="Kiwix" customModuleProvider="target">
<rect key="frame" x="0.0" y="186" width="375" height="149"/>
<rect key="frame" x="0.0" y="185.5" width="375" height="149"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="o5A-Xv-pf5" id="KOC-sh-r2Q">
<frame key="frameInset" width="375" height="148"/>
<frame key="frameInset" width="375" height="148.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Iw0-U5-FqV">
@ -155,7 +155,7 @@
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="b4G-fm-Sda" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1853" y="1137"/>
<point key="canvasLocation" x="2791.1999999999998" y="1136.5817091454273"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="QEZ-t8-ULC">
@ -183,18 +183,118 @@
<scene sceneID="SVz-ni-k6U">
<objects>
<tableViewController storyboardIdentifier="BookmarkController" id="uLf-Kw-kC4" customClass="BookmarkController" customModule="Kiwix" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="e97-aT-3rg">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="e97-aT-3rg">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" id="jws-Kz-Krn">
<rect key="frame" x="0.0" y="28" width="375" height="44"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="BookmarkCell" rowHeight="66" id="5PG-FL-cxF" customClass="BookmarkCell" customModule="Kiwix" customModuleProvider="target">
<rect key="frame" x="0.0" y="119.5" width="375" height="66"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="jws-Kz-Krn" id="l4Q-UL-tNB">
<frame key="frameInset" width="375" height="43"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="5PG-FL-cxF" id="xaN-IW-A8Q">
<frame key="frameInset" width="375" height="65.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8C5-aP-W5f">
<constraints>
<constraint firstAttribute="height" constant="22" id="8Wa-GZ-Fdn"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kTX-Se-1Tr">
<fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="MLn-gx-0vX">
<constraints>
<constraint firstAttribute="width" constant="50" id="HZd-Cl-V3K"/>
<constraint firstAttribute="height" constant="50" id="uiV-Hq-hDX"/>
</constraints>
</imageView>
</subviews>
<constraints>
<constraint firstItem="8C5-aP-W5f" firstAttribute="leading" secondItem="MLn-gx-0vX" secondAttribute="trailing" constant="8" id="20T-Gb-4xa"/>
<constraint firstItem="8C5-aP-W5f" firstAttribute="leading" secondItem="xaN-IW-A8Q" secondAttribute="leadingMargin" constant="58" id="7x4-8l-TKY"/>
<constraint firstItem="kTX-Se-1Tr" firstAttribute="trailing" secondItem="xaN-IW-A8Q" secondAttribute="trailingMargin" id="CXv-MZ-OHC"/>
<constraint firstItem="8C5-aP-W5f" firstAttribute="top" secondItem="xaN-IW-A8Q" secondAttribute="topMargin" constant="2" id="JnU-bN-UVz"/>
<constraint firstItem="8C5-aP-W5f" firstAttribute="trailing" secondItem="xaN-IW-A8Q" secondAttribute="trailingMargin" id="LKT-j4-l4Q"/>
<constraint firstItem="MLn-gx-0vX" firstAttribute="centerY" secondItem="xaN-IW-A8Q" secondAttribute="centerY" id="W6U-dK-Zdl"/>
<constraint firstItem="kTX-Se-1Tr" firstAttribute="top" secondItem="8C5-aP-W5f" secondAttribute="bottom" constant="4" id="eQC-OL-FVO"/>
<constraint firstItem="kTX-Se-1Tr" firstAttribute="leading" secondItem="xaN-IW-A8Q" secondAttribute="leadingMargin" constant="58" id="vEP-KN-deT"/>
<constraint firstAttribute="bottomMargin" secondItem="kTX-Se-1Tr" secondAttribute="bottom" constant="2" id="vZz-lW-ir3"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="subtitleLabel" destination="kTX-Se-1Tr" id="t7e-sO-Zlg"/>
<outlet property="thumbImageView" destination="MLn-gx-0vX" id="ZqQ-DD-eY6"/>
<outlet property="titleLabel" destination="8C5-aP-W5f" id="PLm-CK-x4Q"/>
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="BookmarkSnippetCell" rowHeight="149" id="63j-xA-eDr" customClass="BookmarkSnippetCell" customModule="Kiwix" customModuleProvider="target">
<rect key="frame" x="0.0" y="185.5" width="375" height="149"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="63j-xA-eDr" id="vOo-g4-SUY">
<frame key="frameInset" width="375" height="148.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ucm-1R-oq2">
<constraints>
<constraint firstAttribute="height" constant="22" id="kPD-8s-fMo"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZiC-LW-CAz">
<constraints>
<constraint firstAttribute="height" constant="19.5" id="Z3G-7m-zmO"/>
</constraints>
<fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="fb7-cw-RPA">
<constraints>
<constraint firstAttribute="height" constant="50" id="BXW-1c-V8W"/>
<constraint firstAttribute="width" constant="50" id="Ur8-fD-YFg"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="justified" lineBreakMode="tailTruncation" numberOfLines="4" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="z7h-P0-nCy">
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption2"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="Ucm-1R-oq2" firstAttribute="leading" secondItem="vOo-g4-SUY" secondAttribute="leadingMargin" constant="58" id="3T4-1H-XTZ"/>
<constraint firstItem="ZiC-LW-CAz" firstAttribute="trailing" secondItem="vOo-g4-SUY" secondAttribute="trailingMargin" id="7OF-sG-pOp"/>
<constraint firstAttribute="topMargin" secondItem="fb7-cw-RPA" secondAttribute="top" id="I19-z3-SCp"/>
<constraint firstItem="Ucm-1R-oq2" firstAttribute="top" secondItem="vOo-g4-SUY" secondAttribute="topMargin" constant="2" id="Ipo-0d-gf4"/>
<constraint firstItem="Ucm-1R-oq2" firstAttribute="leading" secondItem="fb7-cw-RPA" secondAttribute="trailing" constant="8" id="JYn-IL-cun"/>
<constraint firstItem="z7h-P0-nCy" firstAttribute="bottom" secondItem="vOo-g4-SUY" secondAttribute="bottomMargin" id="JgH-Vt-DP2"/>
<constraint firstItem="z7h-P0-nCy" firstAttribute="trailing" secondItem="vOo-g4-SUY" secondAttribute="trailingMargin" id="Omn-e2-gEG"/>
<constraint firstItem="Ucm-1R-oq2" firstAttribute="trailing" secondItem="vOo-g4-SUY" secondAttribute="trailingMargin" id="Qra-DT-kjm"/>
<constraint firstItem="ZiC-LW-CAz" firstAttribute="top" secondItem="Ucm-1R-oq2" secondAttribute="bottom" constant="4" id="Wlg-pC-5Nu"/>
<constraint firstItem="z7h-P0-nCy" firstAttribute="top" secondItem="ZiC-LW-CAz" secondAttribute="bottom" constant="10.5" id="Y78-ff-8Lx"/>
<constraint firstItem="fb7-cw-RPA" firstAttribute="centerY" secondItem="vOo-g4-SUY" secondAttribute="centerY" id="fWN-qd-SxN"/>
<constraint firstAttribute="leadingMargin" secondItem="z7h-P0-nCy" secondAttribute="leading" id="gsJ-BE-Vvk"/>
<constraint firstItem="ZiC-LW-CAz" firstAttribute="leading" secondItem="vOo-g4-SUY" secondAttribute="leadingMargin" constant="58" id="sKe-A0-zxB"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="fWN-qd-SxN"/>
</mask>
</variation>
</tableViewCellContentView>
<connections>
<outlet property="snippetLabel" destination="z7h-P0-nCy" id="S6G-Ol-b0B"/>
<outlet property="subtitleLabel" destination="ZiC-LW-CAz" id="Q1y-pr-E0m"/>
<outlet property="thumbImageView" destination="fb7-cw-RPA" id="1WQ-SQ-QDN"/>
<outlet property="titleLabel" destination="Ucm-1R-oq2" id="uil-XC-kpb"/>
</connections>
</tableViewCell>
</prototypes>
<connections>
@ -202,10 +302,20 @@
<outlet property="delegate" destination="uLf-Kw-kC4" id="eAm-IZ-3SL"/>
</connections>
</tableView>
<toolbarItems/>
<navigationItem key="navigationItem" id="uvo-qe-heC">
<barButtonItem key="rightBarButtonItem" systemItem="edit" id="Vy8-bG-jxx">
<connections>
<action selector="editButtonTapped:" destination="uLf-Kw-kC4" id="WYd-PY-qFx"/>
</connections>
</barButtonItem>
</navigationItem>
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="kQh-IS-Ttn" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1455" y="1720"/>
<point key="canvasLocation" x="2676" y="1787"/>
</scene>
<!--BookmarkHUD-->
<scene sceneID="eMu-pk-jX5">
@ -315,7 +425,25 @@
</connections>
</tapGestureRecognizer>
</objects>
<point key="canvasLocation" x="2604" y="1137"/>
<point key="canvasLocation" x="3543.1999999999998" y="1136.5817091454273"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="pBB-bl-wh0">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="epu-ol-vd5" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="7Ri-xB-OG0">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="uLf-Kw-kC4" kind="relationship" relationship="rootViewController" id="oTY-iv-avG"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="qOY-S3-xOp" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1560.8" y="1764.4677661169417"/>
</scene>
</scenes>
<resources>

View File

@ -34,18 +34,23 @@ class BasicBookCell: UITableViewCell {
override func setHighlighted(highlighted: Bool, animated: Bool) {
super.setHighlighted(highlighted, animated: animated)
setHasPicIndicatorColor()
setIndicatorColor()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
setIndicatorColor()
}
// MARK: Shorthand properties
var hasPic: Bool = false {
didSet {
setHasPicIndicatorColor()
setIndicatorColor()
}
}
private func setHasPicIndicatorColor() {
private func setIndicatorColor() {
hasPicIndicator.backgroundColor = hasPic ? AppColors.hasPicTintColor : UIColor.lightGrayColor()
}
}

View File

@ -21,7 +21,7 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.8.1373</string>
<string>1.8.1458</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionMainStoryboard</key>

View File

@ -62,7 +62,7 @@
973DD4191D343F2F009D45DB /* libzim.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 973DD40E1D343F2F009D45DB /* libzim.a */; };
973DD4281D36E3E4009D45DB /* SettingSingleSwitchTBVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 973DD4271D36E3E4009D45DB /* SettingSingleSwitchTBVC.swift */; };
974F33C41D99BBEB00879D35 /* 1.8.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = 974F33C31D99BBEB00879D35 /* 1.8.xcmappingmodel */; };
974F33C61D99CAD700879D35 /* BookmarkMigrationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 974F33C51D99CAD700879D35 /* BookmarkMigrationOperation.swift */; };
974F33C61D99CAD700879D35 /* BookmarkOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 974F33C51D99CAD700879D35 /* BookmarkOperation.swift */; };
974F42821D47E19A00F8074C /* SettingWidgetBookmarksTBVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 974F42811D47E19A00F8074C /* SettingWidgetBookmarksTBVC.swift */; };
975227821D020560001D1DDE /* Indexer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 975227811D020560001D1DDE /* Indexer.storyboard */; };
975227B01D021539001D1DDE /* IndexerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 975227AF1D021539001D1DDE /* IndexerController.swift */; };
@ -283,7 +283,7 @@
973DD40E1D343F2F009D45DB /* libzim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libzim.a; path = Kiwix/libkiwix/iOS/libzim.a; sourceTree = "<group>"; };
973DD4271D36E3E4009D45DB /* SettingSingleSwitchTBVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SettingSingleSwitchTBVC.swift; path = "Kiwix-iOS/Controller/Setting/SettingSingleSwitchTBVC.swift"; sourceTree = SOURCE_ROOT; };
974F33C31D99BBEB00879D35 /* 1.8.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; name = 1.8.xcmappingmodel; path = Kiwix/CoreData/Migration/1.8.xcmappingmodel; sourceTree = SOURCE_ROOT; };
974F33C51D99CAD700879D35 /* BookmarkMigrationOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkMigrationOperation.swift; sourceTree = "<group>"; };
974F33C51D99CAD700879D35 /* BookmarkOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkOperation.swift; sourceTree = "<group>"; };
974F42811D47E19A00F8074C /* SettingWidgetBookmarksTBVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SettingWidgetBookmarksTBVC.swift; path = "Kiwix-iOS/Controller/Setting/SettingWidgetBookmarksTBVC.swift"; sourceTree = SOURCE_ROOT; };
975227811D020560001D1DDE /* Indexer.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Indexer.storyboard; path = "Kiwix-OSX/StoryBoards/Indexer.storyboard"; sourceTree = SOURCE_ROOT; };
975227AF1D021539001D1DDE /* IndexerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = IndexerController.swift; path = "Kiwix-OSX/Controllers/IndexerController.swift"; sourceTree = SOURCE_ROOT; };
@ -984,7 +984,7 @@
isa = PBXGroup;
children = (
9764CBD21D8083AA00072D6A /* ArticleOperation.swift */,
974F33C51D99CAD700879D35 /* BookmarkMigrationOperation.swift */,
974F33C51D99CAD700879D35 /* BookmarkOperation.swift */,
97DF259B1D6F7612001648A3 /* BookOperation.swift */,
97D6811C1D6F70AC00E5FA99 /* GlobalQueue.swift */,
97D6811D1D6F70AC00E5FA99 /* RefreshLibraryOperation.swift */,
@ -1514,7 +1514,7 @@
970E68B61D37E224001E8514 /* SettingSearchHistoryTBVC.swift in Sources */,
97C005D81D64B99E004352E8 /* LibrarySplitViewController.swift in Sources */,
97FDACC41D85A3B300DEDACB /* Language+CoreDataProperties.swift in Sources */,
974F33C61D99CAD700879D35 /* BookmarkMigrationOperation.swift in Sources */,
974F33C61D99CAD700879D35 /* BookmarkOperation.swift in Sources */,
97A8AD871D6CF38000584ED1 /* EmptyTableConfigExtension.swift in Sources */,
971A102E1D022AD5007FC62C /* TableViewCells.swift in Sources */,
97DF259D1D6F9053001648A3 /* URLSessionDownloadTaskOperation.swift in Sources */,

View File

@ -33,53 +33,5 @@
endingLineNumber = "37">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Kiwix/Operations/BookmarkMigrationOperation.swift"
timestampString = "496692057.397729"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "27"
endingLineNumber = "27"
landmarkName = "execute()"
landmarkType = "7">
<Locations>
<Location
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "Kiwix.BookmarkMigrationOperation.(execute () -&gt; ()).(closure #1)"
moduleName = "Kiwix"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Volumes/Data/Developer/Kiwix/Kiwix/Operations/BookmarkMigrationOperation.swift"
timestampString = "496794457.332421"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "27"
endingLineNumber = "27"
offsetFromSymbolStart = "28">
</Location>
<Location
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "Kiwix.BookmarkMigrationOperation.(execute () -&gt; ()).(closure #1).(closure #1)"
moduleName = "Kiwix"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Volumes/Data/Developer/Kiwix/Kiwix/Operations/BookmarkMigrationOperation.swift"
timestampString = "496794457.333823"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "27"
endingLineNumber = "27"
offsetFromSymbolStart = "36">
</Location>
</Locations>
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>

View File

@ -1,38 +0,0 @@
//
// BookmarkMigrationOperation.swift
// Kiwix
//
// Created by Chris Li on 9/26/16.
// Copyright © 2016 Chris. All rights reserved.
//
import CoreData
import Operations
class BookmarkMigrationOperation: Operation {
private let context: NSManagedObjectContext
override init() {
self.context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
context.parentContext = NSManagedObjectContext.mainQueueContext
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
super.init()
addCondition(MutuallyExclusive<GlobalQueue>())
name = String(self)
}
override func execute() {
context.performBlockAndWait {
let pids = Book.fetchLocal(self.context).flatMap({$1.pid})
for pid in pids {
var books = Book.fetch(pid: pid, context: self.context)
let latestBook = books.removeFirst()
for book in books {
book.articles.forEach({$0.book = latestBook})
}
}
if self.context.hasChanges {_ = try? self.context.save()}
}
}
}

View File

@ -0,0 +1,81 @@
//
// BookmarkMigrationOperation.swift
// Kiwix
//
// Created by Chris Li on 9/26/16.
// Copyright © 2016 Chris. All rights reserved.
//
import CoreData
import Operations
class BookmarkMigrationOperation: Operation {
private let context: NSManagedObjectContext
override init() {
self.context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
context.parentContext = NSManagedObjectContext.mainQueueContext
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
super.init()
addCondition(MutuallyExclusive<GlobalQueue>())
name = String(self)
}
override func execute() {
context.performBlockAndWait {
let pids = Book.fetchLocal(self.context).flatMap({$1.pid})
for pid in pids {
var books = Book.fetch(pid: pid, context: self.context)
let latestBook = books.removeFirst()
for book in books {
book.articles.forEach({$0.book = latestBook})
}
}
if self.context.hasChanges {_ = try? self.context.save()}
}
finish()
}
}
class BookmarkTrashOperation: Operation {
private let context: NSManagedObjectContext
private let articles: [Article]
init(articles: [Article]) {
self.context = NSManagedObjectContext.mainQueueContext
self.articles = articles
super.init()
addCondition(MutuallyExclusive<BookmarkController>())
name = String(self)
}
override func execute() {
context.performBlock {
self.articles.forEach() {
$0.isBookmarked = false
$0.bookmarkDate = nil
}
// Get books whose zim file removed, but are retain by bookmarks, and whose bookmarks are all removed
let books = Set(self.articles.flatMap({$0.book}))
.filter({Article.fetchBookmarked(in: $0, with: self.context).count == 0 && $0.state == .Retained})
books.forEach({ (book) in
if let _ = book.meta4URL {
book.state = .Cloud
} else {
self.context.deleteObject(book)
}
})
if self.context.hasChanges {_ = try? self.context.save()}
}
if articles.count > 0 {
produceOperation(UpdateWidgetDataSourceOperation())
}
finish()
}
}