Merge branch 'ios-candidate'
This commit is contained in:
commit
0c95428b31
|
@ -711,6 +711,10 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||
database.fetchStarredAndUnreadCount(for: flattenedWebFeeds().webFeedIDs(), completion: completion)
|
||||
}
|
||||
|
||||
public func fetchCountForStarredArticles() throws -> Int {
|
||||
return try database.fetchStarredArticlesCount(flattenedWebFeeds().webFeedIDs())
|
||||
}
|
||||
|
||||
public func fetchUnreadArticleIDs(_ completion: @escaping ArticleIDsCompletionBlock) {
|
||||
database.fetchUnreadArticleIDsAsync(completion: completion)
|
||||
}
|
||||
|
|
|
@ -393,6 +393,17 @@ public final class AccountManager: UnreadCountProvider {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Fetching Article Counts
|
||||
|
||||
public func fetchCountForStarredArticles() throws -> Int {
|
||||
precondition(Thread.isMainThread)
|
||||
var count = 0
|
||||
for account in activeAccounts {
|
||||
count += try account.fetchCountForStarredArticles()
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// MARK: - Caches
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
//
|
||||
// File.swift
|
||||
//
|
||||
//
|
||||
// Created by Maurice Parker on 9/22/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct CloudKitWebDocumentation {
|
||||
public static let limitationsAndSolutions = "https://netnewswire.com/help/iCloud"
|
||||
}
|
|
@ -114,6 +114,10 @@ public final class ArticlesDatabase {
|
|||
return try articlesTable.fetchStarredArticles(webFeedIDs, limit)
|
||||
}
|
||||
|
||||
public func fetchStarredArticlesCount(_ webFeedIDs: Set<String>) throws -> Int {
|
||||
return try articlesTable.fetchStarredArticlesCount(webFeedIDs)
|
||||
}
|
||||
|
||||
public func fetchArticlesMatching(_ searchString: String, _ webFeedIDs: Set<String>) throws -> Set<Article> {
|
||||
return try articlesTable.fetchArticlesMatching(searchString, webFeedIDs)
|
||||
}
|
||||
|
|
|
@ -32,7 +32,8 @@ final class ArticlesTable: DatabaseTable {
|
|||
let articleCutoffDate = Date().bySubtracting(days: 90)
|
||||
|
||||
private typealias ArticlesFetchMethod = (FMDatabase) -> Set<Article>
|
||||
|
||||
private typealias ArticlesCountFetchMethod = (FMDatabase) -> Int
|
||||
|
||||
init(name: String, accountID: String, queue: DatabaseQueue, retentionStyle: ArticlesDatabase.RetentionStyle) {
|
||||
|
||||
self.name = name
|
||||
|
@ -103,6 +104,10 @@ final class ArticlesTable: DatabaseTable {
|
|||
fetchArticlesAsync({ self.fetchStarredArticles(webFeedIDs, limit, $0) }, completion)
|
||||
}
|
||||
|
||||
func fetchStarredArticlesCount(_ webFeedIDs: Set<String>) throws -> Int {
|
||||
return try fetchArticlesCount{ self.fetchStarredArticlesCount(webFeedIDs, $0) }
|
||||
}
|
||||
|
||||
// MARK: - Fetching Search Articles
|
||||
|
||||
func fetchArticlesMatching(_ searchString: String) throws -> Set<Article> {
|
||||
|
@ -671,6 +676,23 @@ private extension ArticlesTable {
|
|||
return articles
|
||||
}
|
||||
|
||||
private func fetchArticlesCount(_ fetchMethod: @escaping ArticlesCountFetchMethod) throws -> Int {
|
||||
var articlesCount = 0
|
||||
var error: DatabaseError? = nil
|
||||
queue.runInDatabaseSync { databaseResult in
|
||||
switch databaseResult {
|
||||
case .success(let database):
|
||||
articlesCount = fetchMethod(database)
|
||||
case .failure(let databaseError):
|
||||
error = databaseError
|
||||
}
|
||||
}
|
||||
if let error = error {
|
||||
throw(error)
|
||||
}
|
||||
return articlesCount
|
||||
}
|
||||
|
||||
private func fetchArticlesAsync(_ fetchMethod: @escaping ArticlesFetchMethod, _ completion: @escaping ArticleSetResultBlock) {
|
||||
queue.runInDatabase { databaseResult in
|
||||
|
||||
|
@ -745,6 +767,19 @@ private extension ArticlesTable {
|
|||
return articlesWithSQL(sql, parameters, database)
|
||||
}
|
||||
|
||||
func fetchArticleCountsWithWhereClause(_ database: FMDatabase, whereClause: String, parameters: [AnyObject]) -> Int {
|
||||
let sql = "select count(*) from articles natural join statuses where \(whereClause);"
|
||||
guard let resultSet = database.executeQuery(sql, withArgumentsIn: parameters) else {
|
||||
return 0
|
||||
}
|
||||
var articlesCount = 0
|
||||
if resultSet.next() {
|
||||
articlesCount = resultSet.long(forColumnIndex: 0)
|
||||
}
|
||||
resultSet.close()
|
||||
return articlesCount
|
||||
}
|
||||
|
||||
func fetchArticlesMatching(_ searchString: String, _ database: FMDatabase) -> Set<Article> {
|
||||
let sql = "select rowid from search where search match ?;"
|
||||
let sqlSearchString = sqliteSearchString(with: searchString)
|
||||
|
@ -840,20 +875,31 @@ private extension ArticlesTable {
|
|||
return fetchArticlesWithWhereClause(database, whereClause: whereClause, parameters: parameters)
|
||||
}
|
||||
|
||||
func fetchStarredArticles(_ webFeedIDs: Set<String>, _ limit: Int?, _ database: FMDatabase) -> Set<Article> {
|
||||
// select * from articles natural join statuses where feedID in ('http://ranchero.com/xml/rss.xml') and starred=1;
|
||||
if webFeedIDs.isEmpty {
|
||||
return Set<Article>()
|
||||
}
|
||||
let parameters = webFeedIDs.map { $0 as AnyObject }
|
||||
let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(webFeedIDs.count))!
|
||||
var whereClause = "feedID in \(placeholders) and starred=1"
|
||||
if let limit = limit {
|
||||
whereClause.append(" order by coalesce(datePublished, dateModified, dateArrived) desc limit \(limit)")
|
||||
}
|
||||
return fetchArticlesWithWhereClause(database, whereClause: whereClause, parameters: parameters)
|
||||
}
|
||||
|
||||
func fetchStarredArticles(_ webFeedIDs: Set<String>, _ limit: Int?, _ database: FMDatabase) -> Set<Article> {
|
||||
// select * from articles natural join statuses where feedID in ('http://ranchero.com/xml/rss.xml') and starred=1;
|
||||
if webFeedIDs.isEmpty {
|
||||
return Set<Article>()
|
||||
}
|
||||
let parameters = webFeedIDs.map { $0 as AnyObject }
|
||||
let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(webFeedIDs.count))!
|
||||
var whereClause = "feedID in \(placeholders) and starred=1"
|
||||
if let limit = limit {
|
||||
whereClause.append(" order by coalesce(datePublished, dateModified, dateArrived) desc limit \(limit)")
|
||||
}
|
||||
return fetchArticlesWithWhereClause(database, whereClause: whereClause, parameters: parameters)
|
||||
}
|
||||
|
||||
func fetchStarredArticlesCount(_ webFeedIDs: Set<String>, _ database: FMDatabase) -> Int {
|
||||
// select count from articles natural join statuses where feedID in ('http://ranchero.com/xml/rss.xml') and starred=1;
|
||||
if webFeedIDs.isEmpty {
|
||||
return 0
|
||||
}
|
||||
let parameters = webFeedIDs.map { $0 as AnyObject }
|
||||
let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(webFeedIDs.count))!
|
||||
let whereClause = "feedID in \(placeholders) and starred=1"
|
||||
return fetchArticleCountsWithWhereClause(database, whereClause: whereClause, parameters: parameters)
|
||||
}
|
||||
|
||||
func fetchArticlesMatching(_ searchString: String, _ webFeedIDs: Set<String>, _ database: FMDatabase) -> Set<Article> {
|
||||
let articles = fetchArticlesMatching(searchString, database)
|
||||
// TODO: include the feedIDs in the SQL rather than filtering here.
|
||||
|
|
|
@ -19,23 +19,50 @@ public final class WidgetDataEncoder: Logging {
|
|||
|
||||
private let fetchLimit = 7
|
||||
|
||||
private var backgroundTaskID: UIBackgroundTaskIdentifier!
|
||||
private lazy var appGroup = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as! String
|
||||
private lazy var containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup)
|
||||
private lazy var imageContainer = containerURL?.appendingPathComponent("widgetImages", isDirectory: true)
|
||||
private lazy var dataURL = containerURL?.appendingPathComponent("widget-data.json")
|
||||
|
||||
static let shared = WidgetDataEncoder()
|
||||
private init () {
|
||||
private let encodeWidgetDataQueue = CoalescingQueue(name: "Encode the Widget Data", interval: 5.0)
|
||||
|
||||
init () {
|
||||
if imageContainer != nil {
|
||||
try? FileManager.default.createDirectory(at: imageContainer!, withIntermediateDirectories: true, attributes: nil)
|
||||
}
|
||||
if #available(iOS 14, *) {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
func encodeIfNecessary() {
|
||||
encodeWidgetDataQueue.performCallsImmediately()
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
@objc func statusesDidChange(_ note: Notification) {
|
||||
encodeWidgetDataQueue.add(self, #selector(performEncodeWidgetData))
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
@objc private func performEncodeWidgetData() {
|
||||
// We will be on the Main Thread when the encodeIfNecessary function is called. We want
|
||||
// block the main thread in that case so that the widget data is encoded. If it is on
|
||||
// a background Thread, it was called by the CoalescingQueue. In that case we need to
|
||||
// move it to the Main Thread and want to execute it async.
|
||||
if Thread.isMainThread {
|
||||
encodeWidgetData()
|
||||
} else {
|
||||
DispatchQueue.main.async {
|
||||
self.encodeWidgetData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
func encodeWidgetData() throws {
|
||||
private func encodeWidgetData() {
|
||||
flushSharedContainer()
|
||||
logger.debug("Started encoding widget data.")
|
||||
logger.debug("Starting encoding widget data.")
|
||||
|
||||
do {
|
||||
let unreadArticles = Array(try AccountManager.shared.fetchArticles(.unread(fetchLimit))).sortedByDate(.orderedDescending)
|
||||
|
@ -78,7 +105,7 @@ public final class WidgetDataEncoder: Logging {
|
|||
|
||||
let latestData = WidgetData(currentUnreadCount: SmartFeedsController.shared.unreadFeed.unreadCount,
|
||||
currentTodayCount: SmartFeedsController.shared.todayFeed.unreadCount,
|
||||
currentStarredCount: try! SmartFeedsController.shared.starredFeed.fetchArticles().count,
|
||||
currentStarredCount: try AccountManager.shared.fetchCountForStarredArticles(),
|
||||
unreadArticles: unread,
|
||||
starredArticles: starred,
|
||||
todayArticles:today,
|
||||
|
@ -88,10 +115,6 @@ public final class WidgetDataEncoder: Logging {
|
|||
DispatchQueue.global().async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
self.backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: "com.ranchero.NetNewsWire.Encode") {
|
||||
UIApplication.shared.endBackgroundTask(self.backgroundTaskID!)
|
||||
self.backgroundTaskID = .invalid
|
||||
}
|
||||
let encodedData = try? JSONEncoder().encode(latestData)
|
||||
|
||||
self.logger.debug("Finished encoding widget data.")
|
||||
|
@ -103,14 +126,11 @@ public final class WidgetDataEncoder: Logging {
|
|||
if FileManager.default.createFile(atPath: self.dataURL!.path, contents: encodedData, attributes: nil) {
|
||||
self.logger.debug("Wrote widget data to container.")
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
UIApplication.shared.endBackgroundTask(self.backgroundTaskID!)
|
||||
self.backgroundTaskID = .invalid
|
||||
} else {
|
||||
UIApplication.shared.endBackgroundTask(self.backgroundTaskID!)
|
||||
self.backgroundTaskID = .invalid
|
||||
}
|
||||
|
||||
}
|
||||
} catch {
|
||||
logger.error("WidgetDataEncoder failed to write the widget data.")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,18 @@
|
|||
# iOS Release Notes
|
||||
|
||||
### 6.1 TestFlight build 6105 = 6 July 2022
|
||||
|
||||
Write widget icons to the shared container
|
||||
Make crashes slightly less likely when building up widget data
|
||||
|
||||
### 6.1 TestFlight build 6104 - 6 April 2022
|
||||
|
||||
Building on a new machine and making sure all’s well
|
||||
Moved built-in themes to the app bundle so they’re always up to date
|
||||
Fixed a crash in the Feeds list related to updating the feed image
|
||||
Fixed a layout bug that could happen on returning to the Feeds list
|
||||
Fixed a bug where go-to-feed might not properly expand disclosure triangles
|
||||
|
||||
### 6.1 TestFlight build 6103 - 25 Jan 2022
|
||||
|
||||
* Fixed regression with keyboard shortcuts.
|
||||
|
|
|
@ -15,14 +15,14 @@ struct ArticleItemView: View {
|
|||
|
||||
var article: LatestArticle
|
||||
var deepLink: URL
|
||||
@State private var iconImage: UIImage?
|
||||
@State private var iconImage: Image?
|
||||
|
||||
var body: some View {
|
||||
Link(destination: deepLink, label: {
|
||||
HStack(alignment: .top, spacing: nil, content: {
|
||||
// Feed Icon
|
||||
if iconImage != nil {
|
||||
Image(uiImage: iconImage!)
|
||||
iconImage!
|
||||
.resizable()
|
||||
.frame(width: 30, height: 30)
|
||||
.cornerRadius(4)
|
||||
|
@ -51,22 +51,23 @@ struct ArticleItemView: View {
|
|||
}
|
||||
})
|
||||
}).onAppear {
|
||||
guard let feedIconPath = article.feedIconPath else {
|
||||
iconImage = thumbnail(nil)
|
||||
return
|
||||
}
|
||||
let path = URL(fileURLWithPath: feedIconPath)
|
||||
let data = try? Data(contentsOf: path)
|
||||
iconImage = thumbnail(data)
|
||||
iconImage = thumbnail(from: article.feedIconPath)
|
||||
}
|
||||
}
|
||||
|
||||
func thumbnail(_ data: Data?) -> UIImage {
|
||||
if data == nil {
|
||||
return UIImage(systemName: "globe")!
|
||||
} else {
|
||||
return UIImage(data: data!)!
|
||||
}
|
||||
func thumbnail(from path: String?) -> Image? {
|
||||
guard let imagePath = path else {
|
||||
return Image(uiImage: UIImage(systemName: "globe")!)
|
||||
}
|
||||
|
||||
let url = URL(fileURLWithPath: imagePath)
|
||||
|
||||
guard let data = try? Data(contentsOf: url),
|
||||
let uiImage = UIImage(data: data) else {
|
||||
return Image(uiImage: UIImage(systemName: "globe")!)
|
||||
}
|
||||
|
||||
return Image(uiImage: uiImage)
|
||||
}
|
||||
|
||||
func pubDate(_ dateString: String) -> String {
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<objects>
|
||||
<navigationController storyboardIdentifier="LocalAccountNavigationViewController" id="TMY-HB-vAu" customClass="ModalNavigationController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="p8g-7e-3f4">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
|
@ -29,7 +29,7 @@
|
|||
<objects>
|
||||
<navigationController storyboardIdentifier="FeedbinAccountNavigationViewController" id="sFg-MZ-PqJ" customClass="ModalNavigationController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="wq6-np-tNn">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
|
@ -314,7 +314,7 @@ Don’t have a Feedbin account?</string>
|
|||
<objects>
|
||||
<navigationController storyboardIdentifier="NewsBlurAccountNavigationViewController" id="eE3-pu-HdL" customClass="ModalNavigationController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="Fsp-NG-hoR">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
|
@ -678,7 +678,7 @@ Don’t have a Reader account?</string>
|
|||
<objects>
|
||||
<navigationController storyboardIdentifier="CloudKitAccountNavigationViewController" id="LhW-Dq-qqj" customClass="ModalNavigationController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="MVG-BZ-ALL">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
|
@ -706,10 +706,21 @@ Don’t have a Reader account?</string>
|
|||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vXG-7q-4qg">
|
||||
<rect key="frame" x="75" y="50.5" width="264" height="30"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
|
||||
<state key="normal" title="iCloud Syncing Limitations & Solutions"/>
|
||||
<connections>
|
||||
<action selector="openLimitationsAndSolutions:" destination="qj9-Vr-VIU" eventType="touchUpInside" id="JZ5-hQ-PLl"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemGroupedBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="vXG-7q-4qg" firstAttribute="centerX" secondItem="iYz-ri-yys" secondAttribute="centerX" id="8Pg-BU-zIj"/>
|
||||
<constraint firstItem="aFS-Y0-2MH" firstAttribute="leading" secondItem="iYz-ri-yys" secondAttribute="leading" constant="20" symbolic="YES" id="E97-lo-arw"/>
|
||||
<constraint firstItem="vXG-7q-4qg" firstAttribute="top" secondItem="aFS-Y0-2MH" secondAttribute="bottom" id="TEh-B3-9Ci"/>
|
||||
<constraint firstAttribute="trailing" secondItem="aFS-Y0-2MH" secondAttribute="trailing" constant="21" id="XUo-oQ-MbK"/>
|
||||
<constraint firstItem="aFS-Y0-2MH" firstAttribute="top" secondItem="iYz-ri-yys" secondAttribute="top" constant="8" id="xpj-LW-4l7"/>
|
||||
</constraints>
|
||||
|
@ -773,7 +784,7 @@ Don’t have a Reader account?</string>
|
|||
<objects>
|
||||
<navigationController storyboardIdentifier="ReaderAPIAccountNavigationViewController" id="Son-xT-GLx" customClass="ModalNavigationController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="sdL-X8-E6K">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import UIKit
|
||||
import SafariServices
|
||||
import Account
|
||||
|
||||
enum CloudKitAccountViewControllerError: LocalizedError {
|
||||
|
@ -30,7 +31,7 @@ class CloudKitAccountViewController: UITableViewController {
|
|||
}
|
||||
|
||||
private func setupFooter() {
|
||||
footerLabel.text = NSLocalizedString("Feeds in your iCloud account will be synced across your Mac and iOS devices.\n\nImportant note: while NetNewsWire itself is very fast, iCloud syncing is sometimes very slow. This can happen after adding a number of feeds and when setting it up on a new device.\n\nIf that happens to you, it may appear stuck. But don’t worry — it’s not. Just let it run.", comment: "iCloud")
|
||||
footerLabel.text = NSLocalizedString("NetNewsWire will use your iCloud account to sync your subscriptions across your Mac and iOS devices.", comment: "iCloud")
|
||||
}
|
||||
|
||||
@IBAction func cancel(_ sender: Any) {
|
||||
|
@ -63,4 +64,10 @@ class CloudKitAccountViewController: UITableViewController {
|
|||
}
|
||||
}
|
||||
|
||||
@IBAction func openLimitationsAndSolutions(_ sender: Any) {
|
||||
let vc = SFSafariViewController(url: URL(string: CloudKitWebDocumentation.limitationsAndSolutions)!)
|
||||
vc.modalPresentationStyle = .pageSheet
|
||||
present(vc, animated: true)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
var webFeedIconDownloader: WebFeedIconDownloader!
|
||||
var extensionContainersFile: ExtensionContainersFile!
|
||||
var extensionFeedAddRequestFile: ExtensionFeedAddRequestFile!
|
||||
var widgetDataEncoder: WidgetDataEncoder!
|
||||
|
||||
var unreadCount = 0 {
|
||||
didSet {
|
||||
|
@ -111,6 +112,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
extensionContainersFile = ExtensionContainersFile()
|
||||
extensionFeedAddRequestFile = ExtensionFeedAddRequestFile()
|
||||
|
||||
widgetDataEncoder = WidgetDataEncoder()
|
||||
|
||||
syncTimer = ArticleStatusSyncTimer()
|
||||
|
||||
#if DEBUG
|
||||
|
@ -169,6 +172,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
|
||||
func prepareAccountsForBackground() {
|
||||
extensionFeedAddRequestFile.suspend()
|
||||
widgetDataEncoder.encodeIfNecessary()
|
||||
syncTimer?.invalidate()
|
||||
scheduleBackgroundFeedRefresh()
|
||||
syncArticleStatus()
|
||||
|
@ -395,7 +399,6 @@ private extension AppDelegate {
|
|||
}
|
||||
AccountManager.shared.refreshAll(errorHandler: ErrorHandler.log) { [unowned self] in
|
||||
if !AccountManager.shared.isSuspended {
|
||||
try? WidgetDataEncoder.shared.encodeWidgetData()
|
||||
self.suspendApplication()
|
||||
self.logger.info("Account refresh operation completed.")
|
||||
task.setTaskCompleted(success: true)
|
||||
|
@ -440,7 +443,6 @@ private extension AppDelegate {
|
|||
self.prepareAccountsForBackground()
|
||||
account!.syncArticleStatus(completion: { [weak self] _ in
|
||||
if !AccountManager.shared.isSuspended {
|
||||
try? WidgetDataEncoder.shared.encodeWidgetData()
|
||||
self?.prepareAccountsForBackground()
|
||||
self?.suspendApplication()
|
||||
}
|
||||
|
@ -467,7 +469,6 @@ private extension AppDelegate {
|
|||
account!.markArticles(article!, statusKey: .starred, flag: true) { _ in }
|
||||
account!.syncArticleStatus(completion: { [weak self] _ in
|
||||
if !AccountManager.shared.isSuspended {
|
||||
try? WidgetDataEncoder.shared.encodeWidgetData()
|
||||
self?.prepareAccountsForBackground()
|
||||
self?.suspendApplication()
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import UIKit
|
||||
import SafariServices
|
||||
import Account
|
||||
|
||||
class AccountInspectorViewController: UITableViewController {
|
||||
|
@ -16,6 +17,7 @@ class AccountInspectorViewController: UITableViewController {
|
|||
@IBOutlet weak var nameTextField: UITextField!
|
||||
@IBOutlet weak var activeSwitch: UISwitch!
|
||||
@IBOutlet weak var deleteAccountButton: VibrantButton!
|
||||
@IBOutlet weak var limitationsAndSolutionsButton: UIButton!
|
||||
|
||||
var isModal = false
|
||||
weak var account: Account?
|
||||
|
@ -36,6 +38,10 @@ class AccountInspectorViewController: UITableViewController {
|
|||
deleteAccountButton.setTitle(NSLocalizedString("Remove Account", comment: "Remove Account"), for: .normal)
|
||||
}
|
||||
|
||||
if account.type != .cloudKit {
|
||||
limitationsAndSolutionsButton.isHidden = true
|
||||
}
|
||||
|
||||
if isModal {
|
||||
let doneBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(done))
|
||||
navigationItem.leftBarButtonItem = doneBarButtonItem
|
||||
|
@ -115,6 +121,13 @@ class AccountInspectorViewController: UITableViewController {
|
|||
|
||||
present(alertController, animated: true)
|
||||
}
|
||||
|
||||
@IBAction func openLimitationsAndSolutions(_ sender: Any) {
|
||||
let vc = SFSafariViewController(url: URL(string: CloudKitWebDocumentation.limitationsAndSolutions)!)
|
||||
vc.modalPresentationStyle = .pageSheet
|
||||
present(vc, animated: true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Table View
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
|
||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
|
@ -16,21 +16,41 @@
|
|||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="insetGrouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="xcc-i3-tPS">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<view key="tableFooterView" contentMode="scaleToFill" id="3V2-Cm-ezj">
|
||||
<rect key="frame" x="0.0" y="282" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dgD-uX-vcx">
|
||||
<rect key="frame" x="75" y="7" width="264" height="30"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
|
||||
<state key="normal" title="iCloud Syncing Limitations & Solutions"/>
|
||||
<connections>
|
||||
<action selector="openLimitationsAndSolutions:" destination="1m3-fZ-n7g" eventType="touchUpInside" id="DKt-dF-a6L"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="dgD-uX-vcx" firstAttribute="centerX" secondItem="3V2-Cm-ezj" secondAttribute="centerX" id="IDg-5p-aZs"/>
|
||||
<constraint firstItem="dgD-uX-vcx" firstAttribute="centerY" secondItem="3V2-Cm-ezj" secondAttribute="centerY" id="w0c-Qa-ADC"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<sections>
|
||||
<tableViewSection id="vec-ab-Ylg">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="4Ue-UW-e0l" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="18" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="18" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="4Ue-UW-e0l" id="9E1-ww-kYn">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="19" translatesAutoresizingMaskIntoConstraints="NO" id="mQa-0W-eVS">
|
||||
<rect key="frame" x="20" y="12.5" width="334" height="18.5"/>
|
||||
<rect key="frame" x="20" y="11" width="334" height="22"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Name (Optional)" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="LUW-uv-piz">
|
||||
<rect key="frame" x="0.0" y="0.0" width="334" height="18.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="334" height="22"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="words"/>
|
||||
</textField>
|
||||
|
@ -45,14 +65,14 @@
|
|||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="zQY-gY-BOY" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="62" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="61.5" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="zQY-gY-BOY" id="dBp-J5-ZsY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Active" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zf0-Gm-p4F">
|
||||
<rect key="frame" x="20" y="13.5" width="40" height="17"/>
|
||||
<rect key="frame" x="20" y="11.5" width="47.5" height="20.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
@ -75,14 +95,14 @@
|
|||
<tableViewSection id="9UW-s0-NPI">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="FsT-vH-rTo" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="142" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="141" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="FsT-vH-rTo" id="rJW-6J-9DM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="TYD-py-8IF" customClass="VibrantButton" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
<rect key="frame" x="0.0" y="-0.5" width="374" height="44.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="WmU-wG-RLQ"/>
|
||||
</constraints>
|
||||
|
@ -107,14 +127,14 @@
|
|||
<tableViewSection id="mgY-wW-xiO">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="lgY-im-vCo" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="222" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="220.5" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="lgY-im-vCo" id="fIH-xP-nza">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="obv-a5-Pl6" customClass="VibrantButton" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
<rect key="frame" x="0.0" y="-0.5" width="374" height="44.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="WtN-fp-Ldt"/>
|
||||
</constraints>
|
||||
|
@ -154,6 +174,7 @@
|
|||
<connections>
|
||||
<outlet property="activeSwitch" destination="6YV-K0-yPS" id="d9M-GP-aTR"/>
|
||||
<outlet property="deleteAccountButton" destination="obv-a5-Pl6" id="idW-gm-BIJ"/>
|
||||
<outlet property="limitationsAndSolutionsButton" destination="dgD-uX-vcx" id="5ti-AM-xms"/>
|
||||
<outlet property="nameTextField" destination="LUW-uv-piz" id="e2P-Hq-guh"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
|
@ -179,7 +200,7 @@
|
|||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Name" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="ZdA-rl-9eP">
|
||||
<rect key="frame" x="20" y="13" width="334" height="18.5"/>
|
||||
<rect key="frame" x="20" y="11" width="334" height="22"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="words"/>
|
||||
</textField>
|
||||
|
@ -199,7 +220,7 @@
|
|||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Notify About New Articles" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YV2-gG-lMP">
|
||||
<rect key="frame" x="24" y="13.5" width="167.5" height="17"/>
|
||||
<rect key="frame" x="24" y="11.5" width="196.5" height="20.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
@ -229,7 +250,7 @@
|
|||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Always Use Reader View" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Bf4-3X-Rfr">
|
||||
<rect key="frame" x="24" y="13.5" width="158.5" height="17"/>
|
||||
<rect key="frame" x="24" y="11.5" width="187" height="20.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
@ -256,20 +277,20 @@
|
|||
<tableViewSection headerTitle="Home Page" id="dTd-6q-SZd">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="0zc-o6-Sjh" customClass="VibrantBasicTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="198.5" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="204.5" width="374" height="22.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="0zc-o6-Sjh" id="vJs-XK-ebf">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="22.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="characterWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HUP-Cu-FGT">
|
||||
<rect key="frame" x="20" y="11" width="301" height="22"/>
|
||||
<rect key="frame" x="20" y="11" width="301" height="0.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="safari" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="p82-kn-lfh">
|
||||
<rect key="frame" x="329" y="10" width="25" height="24"/>
|
||||
<rect key="frame" x="329" y="-1" width="25" height="24.5"/>
|
||||
<color key="tintColor" name="primaryAccentColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="25" id="Suu-bu-Lar"/>
|
||||
|
@ -300,14 +321,14 @@
|
|||
<tableViewSection headerTitle="Feed URL" id="MtQ-oG-lrU">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="fKD-Vi-B8O">
|
||||
<rect key="frame" x="20" y="292" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="283" width="374" height="22.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="fKD-Vi-B8O" id="2G0-9f-qwN">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="22.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="characterWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rOV-XS-bNW" customClass="InteractiveLabel" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="11" width="334" height="22"/>
|
||||
<rect key="frame" x="20" y="11" width="334" height="0.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
@ -353,7 +374,7 @@
|
|||
<objects>
|
||||
<navigationController storyboardIdentifier="FeedInspectorNavigationViewController" id="Ybm-En-qAg" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="YQK-Se-EBX">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
|
@ -369,7 +390,7 @@
|
|||
<objects>
|
||||
<navigationController storyboardIdentifier="AccountInspectorNavigationViewController" id="5wr-gz-V2t" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="3Os-JI-n4e">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
|
@ -391,14 +412,14 @@
|
|||
<tableViewSection id="hGI-fH-ovr">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="hx7-HU-HV2" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="18" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="18" width="374" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="hx7-HU-HV2" id="fEV-cR-i6h">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YJL-5w-N2S">
|
||||
<rect key="frame" x="20" y="11" width="334" height="22"/>
|
||||
<rect key="frame" x="20" y="11" width="334" height="21"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
@ -417,14 +438,14 @@
|
|||
<tableViewSection id="OcC-FF-jGv">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="k4j-va-uaO" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="98" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="97" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="k4j-va-uaO" id="bQ8-mc-QAj">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IhW-3B-PM7" customClass="VibrantButton" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
<rect key="frame" x="0.0" y="-0.5" width="374" height="44.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="qwy-Nb-VrG"/>
|
||||
</constraints>
|
||||
|
@ -474,8 +495,8 @@
|
|||
<designable name="rOV-XS-bNW"/>
|
||||
</designables>
|
||||
<resources>
|
||||
<image name="safari" catalog="system" width="128" height="121"/>
|
||||
<image name="safari.fill" catalog="system" width="128" height="121"/>
|
||||
<image name="safari" catalog="system" width="128" height="123"/>
|
||||
<image name="safari.fill" catalog="system" width="128" height="123"/>
|
||||
<namedColor name="deleteBackgroundColor">
|
||||
<color red="1" green="0.23100003600120544" blue="0.18799999356269836" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</namedColor>
|
||||
|
@ -485,6 +506,9 @@
|
|||
<namedColor name="secondaryAccentColor">
|
||||
<color red="0.031372549019607843" green="0.41568627450980394" blue="0.93333333333333335" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</namedColor>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemRedColor">
|
||||
<color red="1" green="0.23137254901960785" blue="0.18823529411764706" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
|
|
|
@ -459,6 +459,13 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner, Ma
|
|||
}
|
||||
}
|
||||
|
||||
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
|
||||
if action == #selector(UIResponder.delete(_:)) {
|
||||
return isFirstResponder
|
||||
}
|
||||
return super.canPerformAction(action, withSender: sender)
|
||||
}
|
||||
|
||||
@objc func expandSelectedRows(_ sender: Any?) {
|
||||
if let indexPath = coordinator.currentFeedIndexPath, let node = coordinator.nodeFor(indexPath) {
|
||||
coordinator.expand(node)
|
||||
|
|
|
@ -66,7 +66,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, Logging {
|
|||
}
|
||||
|
||||
func sceneDidEnterBackground(_ scene: UIScene) {
|
||||
try? WidgetDataEncoder.shared.encodeWidgetData()
|
||||
ArticleStringFormatter.emptyCaches()
|
||||
appDelegate.prepareAccountsForBackground()
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
// High Level Settings common to both the iOS application and any extensions we bundle with it
|
||||
MARKETING_VERSION = 6.1
|
||||
CURRENT_PROJECT_VERSION = 6103
|
||||
CURRENT_PROJECT_VERSION = 6105
|
||||
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
|
||||
|
|
Loading…
Reference in New Issue