NewsBlur/clients/ios/Classes/FeedsViewController.swift
David Sinclair fec83d79b6 #1720 (Grid view)
- Restored the legacy table view for the list layout.
- Still uses the modern SwiftUI view for the grid layout.
- Added an option in Preferences (below the “Story titles layout” for “List style”, to choose between “Standard” (aka the table) and “Experimental” (aka the grid-based list) so we can continue to improve the nicer modern list.
- Removed the swipe gesture from the grid view.
- Fixed a crash putting the app in the background.
- Fixed some other issues.
2023-05-25 21:47:34 -07:00

164 lines
5.4 KiB
Swift

//
// FeedsViewController.swift
// NewsBlur
//
// Created by David Sinclair on 2020-08-27.
// Copyright © 2020 NewsBlur. All rights reserved.
//
import UIKit
import StoreKit
///Sidebar listing all of the feeds.
class FeedsViewController: FeedsObjCViewController {
struct Keys {
static let reviewDate = "datePromptedForReview"
static let reviewVersion = "versionPromptedForReview"
}
var appearCount = 0
lazy var appVersion: String = {
let infoDictionaryKey = kCFBundleVersionKey as String
guard let currentVersion = Bundle.main.object(forInfoDictionaryKey: infoDictionaryKey) as? String else {
fatalError("Expected to find a bundle version in the info dictionary")
}
return currentVersion
}()
var datePromptedForReview: Date {
get {
guard let date = UserDefaults.standard.object(forKey: Keys.reviewDate) as? Date else {
let date = Date()
UserDefaults.standard.set(date, forKey: Keys.reviewDate)
return date
}
return date
}
set {
UserDefaults.standard.set(newValue, forKey: Keys.reviewDate)
}
}
var versionPromptedForReview: String {
get {
return UserDefaults.standard.string(forKey: Keys.reviewVersion) ?? ""
}
set {
UserDefaults.standard.set(newValue, forKey: Keys.reviewVersion)
}
}
@objc func addSubfolderFeeds() {
for folderName in appDelegate.dictFoldersArray {
if let folderName = folderName as? String {
addSubfolderFeeds(for: folderName)
}
}
}
private func addSubfolderFeeds(for folderTitle: String) {
guard let parentTitle = self.parentTitle(for: folderTitle) else {
return
}
guard let childFeeds = appDelegate.dictFolders[folderTitle] as? [AnyHashable],
let parentFeeds = appDelegate.dictFolders[parentTitle] as? [AnyHashable] else {
return
}
let existingSubfolders = appDelegate.dictSubfolders[parentTitle] as? [AnyHashable] ?? []
appDelegate.dictFolders[parentTitle] = unique(parentFeeds + childFeeds)
appDelegate.dictSubfolders[parentTitle] = unique(existingSubfolders + childFeeds)
addSubfolderFeeds(for: parentTitle)
}
private func unique(_ array: [AnyHashable]) -> [AnyHashable] {
var seen: Set<AnyHashable> = []
return array.filter { seen.insert($0).inserted }
}
@objc(parentTitleForFolderTitle:) func parentTitle(for folderTitle: String) -> String? {
guard let range = folderTitle.range(of: "", options: .backwards) else {
return nil
}
return String(folderTitle[..<range.lowerBound])
}
@objc(parentTitlesForFolderTitle:) func parentTitles(for folderTitle: String) -> [String] {
var parentTitles = [String]()
guard let parentTitle = parentTitle(for: folderTitle) else {
return []
}
parentTitles.append(parentTitle)
parentTitles += self.parentTitles(for: parentTitle)
return parentTitles
}
var loadWorkItem: DispatchWorkItem?
@objc func loadNotificationStory() {
loadWorkItem?.cancel()
let workItem = DispatchWorkItem { [weak self] in
guard let self else {
return
}
self.appDelegate.backgroundLoadNotificationStory()
}
loadWorkItem = workItem
DispatchQueue.main.asyncAfter(deadline: .now() + (isOffline ? .seconds(1) : .milliseconds(200)), execute: workItem)
}
var reloadWorkItem: DispatchWorkItem?
@objc func deferredUpdateFeedTitlesTable() {
reloadWorkItem?.cancel()
let workItem = DispatchWorkItem { [weak self] in
guard let self else {
return
}
self.updateFeedTitlesTable()
self.refreshHeaderCounts()
}
reloadWorkItem = workItem
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1), execute: workItem)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
appearCount += 1
let month: TimeInterval = 60 * 60 * 24 * 30
let promptedDate = datePromptedForReview
let currentVersion = appVersion
let promptedVersion = versionPromptedForReview
// We only get a few prompts per year, so only ask for a review if gone back to the Feeds list several times, it's been at least a month since the last prompt, and it's a different version.
if appearCount >= 5, -promptedDate.timeIntervalSinceNow > month, currentVersion != promptedVersion, let scene = view.window?.windowScene {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [navigationController] in
if navigationController?.topViewController is FeedsViewController {
SKStoreReviewController.requestReview(in: scene)
self.datePromptedForReview = Date()
self.versionPromptedForReview = currentVersion
}
}
}
}
}