Adds history browser view

This commit is contained in:
James Magahern
2023-01-20 17:28:15 -08:00
parent f374f3ebe8
commit 53efb5389e
9 changed files with 177 additions and 2 deletions

View File

@@ -37,10 +37,19 @@ class BrowserHistory
}
}
public func allHistory() -> [HistoryItem] {
public func allHistory(limit: Int? = nil) -> [HistoryItem] {
let dataContext = persistentContainer.viewContext
let fetchRequest: NSFetchRequest<HistoryItemEntity> = HistoryItemEntity.fetchRequest()
fetchRequest.sortDescriptors = [
// Sort by date
NSSortDescriptor(keyPath: \HistoryItemEntity.lastVisited, ascending: false)
]
if let limit {
fetchRequest.fetchLimit = limit
}
let entities: [HistoryItemEntity] = (try? dataContext.fetch(fetchRequest)) ?? []
return entities.map { (entity) -> HistoryItem in

View File

@@ -7,15 +7,25 @@
import Foundation
struct HistoryItem: Hashable
struct HistoryItem: Hashable, Identifiable
{
var url: URL
var title: String
var lastVisited: Date
var id: ObjectIdentifier
init(entity: HistoryItemEntity) {
self.url = entity.url ?? URL(string: "about:blank")!
self.lastVisited = entity.lastVisited ?? Date()
self.title = entity.title ?? ""
self.id = entity.id
}
// For testing/previews
public init(url: URL, title: String, lastVisited: Date) {
self.url = url
self.title = title
self.lastVisited = lastVisited
self.id = ObjectIdentifier(NSUUID())
}
}

View File

@@ -156,6 +156,10 @@ extension BrowserViewController: ShortcutResponder
showSettingsWindow()
}
func showHistory(_ sender: Any?) {
showHistoryWindow()
}
func toggleDarkMode(_ sender: Any?) {
self.darkModeEnabled = !self.darkModeEnabled
}
@@ -163,4 +167,14 @@ extension BrowserViewController: ShortcutResponder
func openInReaderMode(_ sender: Any?) {
showReaderWindow()
}
func handleOpenURL(_ sender: Any?, url: URL?) {
guard let url else { return }
if tab.url == nil {
tab.beginLoadingURL(url)
} else {
createNewTab(withURL: url)
}
}
}

View File

@@ -333,6 +333,12 @@ class BrowserViewController: UIViewController
showShareSheetForCurrentURL(fromViewController: documentControls)
}, for: .touchUpInside)
// History
documentControls.historyView.addAction(UIAction { [unowned self] action in
documentControls.dismiss(animated: false, completion: nil)
showHistory(action)
}, for: .touchUpInside)
present(documentControls, animated: true, completion: nil)
}), for: .touchUpInside)
@@ -463,6 +469,17 @@ class BrowserViewController: UIViewController
}
}
internal func showHistoryWindow() {
let historyViewController = HistoryBrowserViewController()
historyViewController.title = "History"
historyViewController.navigationItem.rightBarButtonItem = UIBarButtonItem(systemItem: .done, primaryAction: UIAction { _ in
historyViewController.dismiss(animated: true)
})
let navigationController = UINavigationController(rootViewController: historyViewController)
present(navigationController, animated: true)
}
internal func updateLoadProgress(forWebView webView: WKWebView) {
if let loadError = tab.loadError {
toolbarController.urlBar.loadProgress = .error(error: loadError)

View File

@@ -20,6 +20,7 @@ class DocumentControlViewController: UIViewController
let archiveView = DocumentControlItemView().title("Archive.today") .symbol("shippingbox")
let emailView = DocumentControlItemView().title("Email") .symbol("envelope")
let sharingView = DocumentControlItemView().title("Share") .symbol("square.and.arrow.up")
let historyView = DocumentControlItemView().title("History") .symbol("clock.arrow.circlepath")
let darkModeView = DocumentControlItemView().title("Dark Mode")
var observations: [NSKeyValueObservation] = []
@@ -46,6 +47,7 @@ class DocumentControlViewController: UIViewController
documentControlsView.stackView.addArrangedSubview(darkModeView)
documentControlsView.stackView.addArrangedSubview(readabilityView)
documentControlsView.stackView.addArrangedSubview(archiveView)
documentControlsView.stackView.addArrangedSubview(historyView)
documentControlsView.stackView.addArrangedSubview(settingsView)

View File

@@ -0,0 +1,20 @@
//
// HistoryBrowserViewController.swift
// App
//
// Created by James Magahern on 1/20/23.
//
import SwiftUI
import UIKit
@MainActor
class HistoryBrowserViewController: UIHostingController<HistoryView> {
public init() {
super.init(rootView: HistoryView(historyItems: BrowserHistory.shared.allHistory(limit: 500)))
}
required dynamic init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@@ -0,0 +1,72 @@
//
// HistoryView.swift
// App
//
// Created by James Magahern on 1/20/23.
//
import SwiftUI
import UniformTypeIdentifiers
struct HistoryView: View {
var historyItems: [HistoryItem]
private let dateFormatter: DateFormatter
@State public var selectedItems = Set<HistoryItem.ID>()
@Environment(\.dismiss) private var dismissAction
init(historyItems: [HistoryItem]) {
self.historyItems = historyItems
let formatter = DateFormatter()
formatter.locale = Locale.current
formatter.dateStyle = .medium
formatter.timeStyle = .short
self.dateFormatter = formatter
}
var body: some View {
Table(historyItems, selection: $selectedItems) {
TableColumn("Title", value: \.title)
TableColumn("URL") { item in
Text(item.url.absoluteString)
}
TableColumn("Last Visited") { item in
Text(dateFormatter.string(from: item.lastVisited))
}
}
.contextMenu(forSelectionType: HistoryItem.ID.self, menu: { items in
if let firstItem: HistoryItem.ID = items.first,
let historyItem = historyItems.first { $0.id == firstItem }
{
Button("Copy") {
UIPasteboard.general.addItems([
[ UTType.url.identifier : historyItem.url ]
])
}
// TODO: Delete?
}
}, primaryAction: { items in
if let firstItem: HistoryItem.ID = items.first,
let historyItem = historyItems.first(where: { $0.id == firstItem })
{
UIApplication.shared.open(historyItem.url)
dismissAction()
}
})
}
}
struct HistoryViewPreviewProvider: PreviewProvider {
static var previews: some View {
HistoryView(historyItems: [
HistoryItem(url: URL(string: "https://apple.com")!, title: "Apple", lastVisited: Date.now),
HistoryItem(url: URL(string: "https://google.com")!, title: "Google", lastVisited: Date.now)
])
.previewLayout(.fixed(width: 480.0, height: 800.0))
}
}

View File

@@ -50,6 +50,12 @@ protocol ShortcutResponder: AnyObject {
@objc
optional func openInReaderMode(_ sender: Any?)
@objc
optional func showHistory(_ sender: Any?)
@objc
optional func handleOpenURL(_ sender: Any?, url: URL?)
}
fileprivate extension Array {
@@ -166,6 +172,13 @@ public class KeyboardShortcuts {
title: "Go Forward",
action: #selector(ShortcutResponder.goForward)
),
UIKeyCommand(
modifiers: [.command, .shift],
input: "h",
title: "Show History…",
action: #selector(ShortcutResponder.showHistory)
),
]),
// Tab Navigation