2020-08-14 20:05:36 -07:00
|
|
|
//
|
|
|
|
|
// 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()
|
2023-01-20 16:20:36 -08:00
|
|
|
fetchRequest.predicate = NSPredicate(format: """
|
|
|
|
|
host CONTAINS[cd] %@
|
|
|
|
|
OR title CONTAINS[cd] %@
|
|
|
|
|
OR url ENDSWITH[cd] %@
|
|
|
|
|
""", matching, matching, matching)
|
2022-02-21 14:23:38 -08:00
|
|
|
fetchRequest.fetchLimit = 200
|
|
|
|
|
fetchRequest.sortDescriptors = [ NSSortDescriptor(key: "visitCount", ascending: false) ]
|
2020-08-14 20:05:36 -07:00
|
|
|
|
|
|
|
|
let entities: [HistoryItemEntity] = (try? dataContext.fetch(fetchRequest)) ?? []
|
|
|
|
|
let allItems: [HistoryItem] = entities.map { HistoryItem(entity: $0) }
|
|
|
|
|
|
|
|
|
|
var topLevelItems: [URL: (HistoryItem, Int)] = [:]
|
2022-02-18 18:11:34 -08:00
|
|
|
for item in allItems {
|
|
|
|
|
if item.url.pathComponents.count <= 2 {
|
2022-02-21 14:23:38 -08:00
|
|
|
var score = 1
|
2022-02-18 18:11:34 -08:00
|
|
|
let topLevelURL = item.url.topLevelURL()
|
|
|
|
|
var topLevelItem = topLevelItems[topLevelURL] ?? (item, 0)
|
|
|
|
|
topLevelItem.0.url = topLevelURL
|
|
|
|
|
if item.url.path == "/" || item.url.path == "" {
|
2022-02-21 14:23:38 -08:00
|
|
|
score += 10
|
2022-02-18 18:11:34 -08:00
|
|
|
topLevelItem.0.title = item.title
|
|
|
|
|
}
|
2022-02-21 14:23:38 -08:00
|
|
|
topLevelItem.1 += score
|
2022-02-18 18:11:34 -08:00
|
|
|
topLevelItems[topLevelURL] = topLevelItem
|
|
|
|
|
|
|
|
|
|
if topLevelItems.count == 20 {
|
|
|
|
|
break
|
|
|
|
|
}
|
2020-09-21 17:56:22 -07:00
|
|
|
}
|
2020-08-14 20:05:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return topLevelItems.values.map { return $0.0 }.sorted { (item1, item2) -> Bool in
|
2020-09-21 17:56:22 -07:00
|
|
|
return topLevelItems[item1.url]!.1 > topLevelItems[item2.url]!.1
|
2020-08-14 20:05:36 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension URL
|
|
|
|
|
{
|
|
|
|
|
public func topLevelURL() -> URL {
|
|
|
|
|
if var components = URLComponents(url: self, resolvingAgainstBaseURL: false) {
|
2020-09-21 17:56:22 -07:00
|
|
|
components.query = nil
|
|
|
|
|
components.queryItems = nil
|
|
|
|
|
components.fragment = nil
|
2020-08-14 20:05:36 -07:00
|
|
|
if let url = components.url {
|
|
|
|
|
return url
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return self
|
|
|
|
|
}
|
|
|
|
|
}
|