Files
Attractor/App/Backend/History/BrowserHistory.swift
2022-02-21 14:23:38 -08:00

103 lines
3.4 KiB
Swift

//
// BrowserHistory.swift
// App
//
// Created by James Magahern on 8/14/20.
//
import Foundation
import CoreData
class BrowserHistory
{
static public let shared = BrowserHistory()
lazy fileprivate var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "History")
container.loadPersistentStores { description, error in
assert(error == nil)
}
return container
}()
public func didNavigate(toURL url: URL, title: String) {
let dataContext = persistentContainer.viewContext
let entity = HistoryItemEntity(context: dataContext)
entity.url = url
entity.lastVisited = Date()
entity.title = title
entity.host = url.host
do { try dataContext.save() }
catch {
let nserror = error as NSError
fatalError("Failed saving persistent entity to store: \(nserror), \(nserror.userInfo)")
}
}
public func allHistory() -> [HistoryItem] {
let dataContext = persistentContainer.viewContext
let fetchRequest: NSFetchRequest<HistoryItemEntity> = HistoryItemEntity.fetchRequest()
let entities: [HistoryItemEntity] = (try? dataContext.fetch(fetchRequest)) ?? []
return entities.map { (entity) -> HistoryItem in
HistoryItem(entity: entity)
}
}
public func visitedToplevelHistoryItems(matching: String) -> [HistoryItem] {
let dataContext = persistentContainer.viewContext
let fetchRequest: NSFetchRequest<HistoryItemEntity> = HistoryItemEntity.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "host CONTAINS %@ OR title contains %@", matching, matching)
fetchRequest.fetchLimit = 200
fetchRequest.sortDescriptors = [ NSSortDescriptor(key: "visitCount", ascending: false) ]
let entities: [HistoryItemEntity] = (try? dataContext.fetch(fetchRequest)) ?? []
let allItems: [HistoryItem] = entities.map { HistoryItem(entity: $0) }
var topLevelItems: [URL: (HistoryItem, Int)] = [:]
for item in allItems {
if item.url.pathComponents.count <= 2 {
var score = 1
let topLevelURL = item.url.topLevelURL()
var topLevelItem = topLevelItems[topLevelURL] ?? (item, 0)
topLevelItem.0.url = topLevelURL
if item.url.path == "/" || item.url.path == "" {
score += 10
topLevelItem.0.title = item.title
}
topLevelItem.1 += score
topLevelItems[topLevelURL] = topLevelItem
if topLevelItems.count == 20 {
break
}
}
}
return topLevelItems.values.map { return $0.0 }.sorted { (item1, item2) -> Bool in
return topLevelItems[item1.url]!.1 > topLevelItems[item2.url]!.1
}
}
}
extension URL
{
public func topLevelURL() -> URL {
if var components = URLComponents(url: self, resolvingAgainstBaseURL: false) {
components.query = nil
components.queryItems = nil
components.fragment = nil
if let url = components.url {
return url
}
}
return self
}
}