Adds history browser view
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
20
App/History UI/HistoryBrowserViewController.swift
Normal file
20
App/History UI/HistoryBrowserViewController.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
72
App/History UI/HistoryView.swift
Normal file
72
App/History UI/HistoryView.swift
Normal 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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user