Compare commits

..

No commits in common. "197edc5e1a8b2fbdd8f7d3612384447c270bf61d" and "7d4f5ccba3e804b30cbe9ef8a83d7e601e9068c3" have entirely different histories.

14 changed files with 80 additions and 58 deletions

View File

@ -5,7 +5,7 @@
// Created by Shadowfacts on 10/29/21. // Created by Shadowfacts on 10/29/21.
// //
import Foundation @preconcurrency import Foundation
public struct Feed: Decodable, Sendable { public struct Feed: Decodable, Sendable {
public let id: FervorID public let id: FervorID

View File

@ -5,7 +5,7 @@
// Created by Shadowfacts on 11/25/21. // Created by Shadowfacts on 11/25/21.
// //
import Foundation @preconcurrency import Foundation
public actor FervorClient: Sendable { public actor FervorClient: Sendable {

View File

@ -5,7 +5,7 @@
// Created by Shadowfacts on 10/29/21. // Created by Shadowfacts on 10/29/21.
// //
import Foundation @preconcurrency import Foundation
public struct Group: Decodable, Sendable { public struct Group: Decodable, Sendable {
public let id: FervorID public let id: FervorID

View File

@ -5,7 +5,7 @@
// Created by Shadowfacts on 10/29/21. // Created by Shadowfacts on 10/29/21.
// //
import Foundation @preconcurrency import Foundation
public struct Instance: Decodable, Sendable { public struct Instance: Decodable, Sendable {
public let name: String public let name: String

View File

@ -5,7 +5,7 @@
// Created by Shadowfacts on 10/29/21. // Created by Shadowfacts on 10/29/21.
// //
import Foundation @preconcurrency import Foundation
public struct Item: Decodable, Sendable { public struct Item: Decodable, Sendable {
public let id: FervorID public let id: FervorID

View File

@ -5,7 +5,7 @@
// Created by Shadowfacts on 1/9/22. // Created by Shadowfacts on 1/9/22.
// //
import Foundation @preconcurrency import Foundation
public struct ItemsSyncUpdate: Decodable, Sendable { public struct ItemsSyncUpdate: Decodable, Sendable {

View File

@ -508,7 +508,7 @@
attributes = { attributes = {
BuildIndependentTargetsInParallel = 1; BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1320; LastSwiftUpdateCheck = 1320;
LastUpgradeCheck = 1400; LastUpgradeCheck = 1320;
TargetAttributes = { TargetAttributes = {
D684090C279486BF00E327D2 = { D684090C279486BF00E327D2 = {
CreatedOnToolsVersion = 13.2; CreatedOnToolsVersion = 13.2;
@ -616,7 +616,7 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/bash; shellPath = /bin/bash;
shellScript = "pushd \"$PROJECT_DIR/lol-html/c-api/\"\n\nbuild() {\n echo \"Building lol-html with CARGO_TARGET: $1\"\n\n ~/.cargo/bin/cargo build --release --target $1\n}\n\nbuild_std() {\n echo \"Building lol-html with CARGO_TARGET: $1\"\n echo \"Building std enabled\"\n \n ~/.cargo/bin/cargo +nightly build -Z build-std=panic_abort,std --release --target $1\n}\n\nif [ \"$PLATFORM_NAME\" == \"iphonesimulator\" ]; then\n if [ \"$ARCHS\" == \"arm64\" ]; then\n build_std \"aarch64-apple-ios-sim\"\n elif [ \"$ARCHS\" == \"x86_64\" ]; then\n build \"x86_64-apple-ios\"\n else\n echo \"error: unknown value for \\$ARCHS\"\n exit 1\n fi\nelif [ \"$PLATFORM_NAME\" == \"iphoneos\" ]; then\n build_std \"aarch64-apple-ios\"\nelif [ \"$PLATFORM_NAME\" == \"macosx\" ]; then\n if grep -q \"arm64\" <<< \"$ARCHS\"; then\n build_std \"aarch64-apple-ios-macabi\"\n fi\n if grep -q \"x86_64\" <<< \"$ARCHS\"; then\n build_std \"x86_64-apple-ios-macabi\"\n fi\nelse\n echo \"error: unknown value for \\$PLATFORM_NAME\"\n exit 1\nfi\n"; shellScript = "pushd \"$PROJECT_DIR/lol-html/c-api/\"\n\nbuild() {\n echo \"Building lol-html with CARGO_TARGET: $1\"\n\n ~/.cargo/bin/cargo build --release --target $1\n}\n\nbuild_std() {\n echo \"Building lol-html with CARGO_TARGET: $1\"\n echo \"Building std enabled\"\n \n ~/.cargo/bin/cargo +nightly build -Z build-std=panic_abort,std --release --target $1\n}\n\nif [ \"$PLATFORM_NAME\" == \"iphonesimulator\" ]; then\n if [ \"$ARCHS\" == \"arm64\" ]; then\n build \"aarch64-apple-ios-sim\"\n elif [ \"$ARCHS\" == \"x86_64\" ]; then\n build \"x86_64-apple-ios\"\n else\n echo \"error: unknown value for \\$ARCHS\"\n exit 1\n fi\nelif [ \"$PLATFORM_NAME\" == \"iphoneos\" ]; then\n build \"aarch64-apple-ios\"\nelif [ \"$PLATFORM_NAME\" == \"macosx\" ]; then\n if grep -q \"arm64\" <<< \"$ARCHS\"; then\n build_std \"aarch64-apple-ios-macabi\"\n fi\n if grep -q \"x86_64\" <<< \"$ARCHS\"; then\n build_std \"x86_64-apple-ios-macabi\"\n fi\nelse\n echo \"error: unknown value for \\$PLATFORM_NAME\"\n exit 1\nfi\n";
}; };
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
@ -743,7 +743,6 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = ZPBBSK8L8B; DEVELOPMENT_TEAM = ZPBBSK8L8B;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSHumanReadableCopyright = "";
@ -774,7 +773,6 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = ZPBBSK8L8B; DEVELOPMENT_TEAM = ZPBBSK8L8B;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSHumanReadableCopyright = "";

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1400" LastUpgradeVersion = "1320"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"

View File

@ -5,9 +5,9 @@
// Created by Shadowfacts on 11/25/21. // Created by Shadowfacts on 11/25/21.
// //
import Foundation @preconcurrency import Foundation
import Fervor import Fervor
import OSLog @preconcurrency import OSLog
import Combine import Combine
actor FervorController { actor FervorController {

View File

@ -5,7 +5,7 @@
// Created by Shadowfacts on 10/29/21. // Created by Shadowfacts on 10/29/21.
// //
import Foundation @preconcurrency import Foundation
import UIKit import UIKit
import OSLog import OSLog

View File

@ -59,6 +59,7 @@ class AppSplitViewController: UISplitViewController {
let nav = viewController(for: .compact) as! UINavigationController let nav = viewController(for: .compact) as! UINavigationController
let top = nav.topViewController! let top = nav.topViewController!
if top is ReadViewController || top is ItemsViewController { if top is ReadViewController || top is ItemsViewController {
print(top.userActivity?.activityType)
return top.userActivity return top.userActivity
} }
} else { } else {

View File

@ -75,9 +75,21 @@ class ItemsViewController: UIViewController {
]) ])
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "published", ascending: false)] fetchRequest.sortDescriptors = [NSSortDescriptor(key: "published", ascending: false)]
fetchItems() do {
let ids = try fervorController.persistentContainer.viewContext.fetch(fetchRequest)
var snapshot = NSDiffableDataSourceSnapshot<Section, NSManagedObjectID>()
snapshot.appendSections([.items])
snapshot.appendItems(ids, toSection: .items)
dataSource.apply(snapshot, animatingDifferences: false)
NotificationCenter.default.addObserver(self, selector: #selector(managedObjectsDidChange), name: .NSManagedObjectContextObjectsDidChange, object: fervorController.persistentContainer.viewContext) NotificationCenter.default.addObserver(self, selector: #selector(managedObjectsDidChange), name: .NSManagedObjectContextObjectsDidChange, object: fervorController.persistentContainer.viewContext)
} catch {
let alert = UIAlertController(title: "Error fetching items", message: error.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
present(alert, animated: true)
}
} }
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, NSManagedObjectID> { private func createDataSource() -> UICollectionViewDiffableDataSource<Section, NSManagedObjectID> {
@ -92,59 +104,73 @@ class ItemsViewController: UIViewController {
} }
} }
private func fetchItems() {
do {
let ids = try fervorController.persistentContainer.viewContext.fetch(fetchRequest)
var snapshot = NSDiffableDataSourceSnapshot<Section, NSManagedObjectID>()
snapshot.appendSections([.items])
snapshot.appendItems(ids, toSection: .items)
dataSource.apply(snapshot, animatingDifferences: false)
} catch {
let alert = UIAlertController(title: "Error fetching items", message: error.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
present(alert, animated: true)
}
}
private var updateId = 0
@objc private func managedObjectsDidChange(_ notification: Notification) { @objc private func managedObjectsDidChange(_ notification: Notification) {
let id = updateId
updateId += 1
print("\(id) Managed objects did change")
let inserted = notification.userInfo?[NSInsertedObjectsKey] as? NSSet let inserted = notification.userInfo?[NSInsertedObjectsKey] as? NSSet
let updated = notification.userInfo?[NSUpdatedObjectsKey] as? NSSet let updated = notification.userInfo?[NSUpdatedObjectsKey] as? NSSet
let deleted = notification.userInfo?[NSDeletedObjectsKey] as? NSSet let deleted = notification.userInfo?[NSDeletedObjectsKey] as? NSSet
let hasInsertedItems = inserted?.lazy.compactMap { $0 as? Item }.contains { fetchRequest.predicate?.evaluate(with: $0) ?? true } ?? false // managed objectss from the notification are tied to the thread it was delivered on
// so get the published dates and evaluate the predicate here
if hasInsertedItems { let insertedItems = inserted?.compactMap { $0 as? Item }.filter { fetchRequest.predicate?.evaluate(with: $0) ?? true }.map { ($0, $0.published) }
print("\(id) Has inserted items, skipping merge")
// if any items were inserted, just refetch everything. it's more expensive than it's worth to try and merge the changes into the current snapshot
self.fetchItems()
return
}
var snapshot = self.dataSource.snapshot() var snapshot = self.dataSource.snapshot()
// the itemIdentifiers getter takes a lot of time in profiles, so only call the getter once
let snapshotItems = snapshot.itemIdentifiers
if let updated = updated { if let updated = updated {
print("\(id) Updated: \(updated.count)") let knownUpdated = updated.compactMap { ($0 as? Item)?.objectID }.filter { snapshot.itemIdentifiers.contains($0) }
let knownUpdated = updated.compactMap { ($0 as? Item)?.objectID }.filter { snapshotItems.contains($0) }
snapshot.reconfigureItems(knownUpdated) snapshot.reconfigureItems(knownUpdated)
} }
if let deleted = deleted { if let deleted = deleted {
print("\(id) Deleted: \(deleted.count)") let knownDeleted = deleted.compactMap { ($0 as? Item)?.objectID }.filter { snapshot.itemIdentifiers.contains($0) }
let knownDeleted = deleted.compactMap { ($0 as? Item)?.objectID }.filter { snapshotItems.contains($0) }
snapshot.deleteItems(knownDeleted) snapshot.deleteItems(knownDeleted)
} }
print("\(id) Applying snapshot") if let insertedItems = insertedItems {
self.fervorController.persistentContainer.performBackgroundTask { ctx in
// for newly inserted items, the ctx doesn't have the published date so we check the data we got from the notification
func publishedForItem(_ id: NSManagedObjectID) -> Date? {
if let (_, pub) = insertedItems.first(where: { $0.0.objectID == id }) {
return pub
} else {
return (ctx.object(with: id) as! Item).published
}
}
// todo: this feels inefficient
for (inserted, insertedPublished) in insertedItems {
// todo: uhh, what does sql do if the published date is nil?
guard let insertedPublished = insertedPublished else {
snapshot.insertItems([inserted.objectID], beforeItem: snapshot.itemIdentifiers.first!)
continue
}
var index = 0
while index < snapshot.itemIdentifiers.count,
let pub = publishedForItem(snapshot.itemIdentifiers[index]),
insertedPublished < pub {
index += 1
}
// index is the index of the first item which the inserted one was published after
// (i.e., the item that should appear immediately after inserted in the list)
if index == snapshot.itemIdentifiers.count {
snapshot.appendItems([inserted.objectID], toSection: .items)
} else {
snapshot.insertItems([inserted.objectID], beforeItem: snapshot.itemIdentifiers[index])
}
}
DispatchQueue.main.async {
self.dataSource.apply(snapshot, animatingDifferences: false) self.dataSource.apply(snapshot, animatingDifferences: false)
} }
}
} else {
DispatchQueue.main.async {
self.dataSource.apply(snapshot, animatingDifferences: false)
}
}
}
} }

View File

@ -5,7 +5,7 @@
// Created by Shadowfacts on 11/25/21. // Created by Shadowfacts on 11/25/21.
// //
import Foundation @preconcurrency import Foundation
import UIKit import UIKit
import AuthenticationServices import AuthenticationServices
import Fervor import Fervor

View File

@ -70,9 +70,6 @@ class ReadViewController: UIViewController {
// transparent background required to prevent white flash in dark mode, just using .appBackground doesn't work // transparent background required to prevent white flash in dark mode, just using .appBackground doesn't work
webView.isOpaque = false webView.isOpaque = false
webView.backgroundColor = .clear webView.backgroundColor = .clear
if #available(iOS 16.0, *) {
webView.isFindInteractionEnabled = true
}
if let content = itemContentHTML() { if let content = itemContentHTML() {
webView.loadHTMLString(content, baseURL: item.url) webView.loadHTMLString(content, baseURL: item.url)
} }