mirror of
https://github.com/kiwix/kiwix-apple.git
synced 2025-09-17 08:13:32 -04:00
Bookmark view (#416)
* OutlineView loading state * setup * model * empty content view * display * action * update the css
This commit is contained in:
parent
855d7bf1e4
commit
16d2110ae6
@ -29,6 +29,7 @@
|
||||
974FFE5B2352704600F6BF28 /* ZimFileManagerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 974FFE5A2352704600F6BF28 /* ZimFileManagerController.swift */; };
|
||||
974FFE612352744800F6BF28 /* WebKitWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 974FFE602352744800F6BF28 /* WebKitWebViewController.swift */; };
|
||||
974FFE6A23533E8D00F6BF28 /* NavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 974FFE6823533E8D00F6BF28 /* NavigationController.swift */; };
|
||||
97566A562728F58000DE7165 /* BookmarksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97566A552728F58000DE7165 /* BookmarksViewController.swift */; };
|
||||
975AEBD32597EACF00392988 /* Kiwix_logo_v3_320px.png in Resources */ = {isa = PBXBuildFile; fileRef = 975AEBD22597EACF00392988 /* Kiwix_logo_v3_320px.png */; };
|
||||
9763EDCB201FCC3A00F3A6D5 /* About.html in Resources */ = {isa = PBXBuildFile; fileRef = 9763EDCA201FCC3900F3A6D5 /* About.html */; };
|
||||
976771791F4378FC007ED0C2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 976771741F4378FC007ED0C2 /* Main.storyboard */; };
|
||||
@ -78,7 +79,6 @@
|
||||
977D506D2621EAD1005345D1 /* LibraryPrimaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 977D506C2621EAD1005345D1 /* LibraryPrimaryView.swift */; };
|
||||
977D50772623580D005345D1 /* LibrarySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 977D50762623580D005345D1 /* LibrarySettingsView.swift */; };
|
||||
977D509626268AAB005345D1 /* LibrarySearchResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 977D509526268AAB005345D1 /* LibrarySearchResultView.swift */; };
|
||||
97802D7025789B1800A21352 /* SidebarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97802D6F25789B1800A21352 /* SidebarViewController.swift */; };
|
||||
9789C26424F9600D00072090 /* Platform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97B399912468502500BC6F5B /* Platform.swift */; };
|
||||
9789C26524F9601400072090 /* ZimFileService.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9779A5A92456793500F6F6FF /* ZimFileService.mm */; };
|
||||
9789C26624F9601700072090 /* ZimFileService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9779A5AA2456793500F6F6FF /* ZimFileService.swift */; };
|
||||
@ -239,6 +239,7 @@
|
||||
974FFE5A2352704600F6BF28 /* ZimFileManagerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZimFileManagerController.swift; sourceTree = "<group>"; };
|
||||
974FFE602352744800F6BF28 /* WebKitWebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebKitWebViewController.swift; sourceTree = "<group>"; };
|
||||
974FFE6823533E8D00F6BF28 /* NavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationController.swift; sourceTree = "<group>"; };
|
||||
97566A552728F58000DE7165 /* BookmarksViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksViewController.swift; sourceTree = "<group>"; };
|
||||
9759D4211F4F278800705779 /* Kiwix.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Kiwix.entitlements; sourceTree = "<group>"; };
|
||||
975AEBD22597EACF00392988 /* Kiwix_logo_v3_320px.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Kiwix_logo_v3_320px.png; sourceTree = "<group>"; };
|
||||
975FDAD91F6082DA00A10E8C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
@ -486,6 +487,7 @@
|
||||
97B5D3E82596398400D12FEE /* SidebarController.swift */,
|
||||
97802D6F25789B1800A21352 /* SidebarViewController.swift */,
|
||||
97A2AE2125866D9200DEF81D /* ButtonProvider.swift */,
|
||||
97566A552728F58000DE7165 /* BookmarksViewController.swift */,
|
||||
97C14E222707F98400EF4CF2 /* OutlineViewController.swift */,
|
||||
977D50622621E47D005345D1 /* LibraryViewController.swift */,
|
||||
9797435B257BF33600D30F03 /* WebViewController.swift */,
|
||||
@ -1085,6 +1087,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
97566A562728F58000DE7165 /* BookmarksViewController.swift in Sources */,
|
||||
97B3998E246759E400BC6F5B /* SearchResult.m in Sources */,
|
||||
97225AA2261A006A00D8CB32 /* LibraryCategoryView.swift in Sources */,
|
||||
9779A5BC2456793600F6F6FF /* OPDSStreamParser.swift in Sources */,
|
||||
@ -1125,7 +1128,6 @@
|
||||
97952EF3264F557B00FC9237 /* SearchResultsView.swift in Sources */,
|
||||
9779A5BA2456793600F6F6FF /* OPDSRefreshOperation.swift in Sources */,
|
||||
9779A5BE2456793600F6F6FF /* OPDSStreamParser.mm in Sources */,
|
||||
97802D7025789B1800A21352 /* SidebarViewController.swift in Sources */,
|
||||
9779A7B12456796B00F6F6FF /* WebKitHandler.swift in Sources */,
|
||||
97FD2F5F251EA07B0034927C /* FeatureFlags.swift in Sources */,
|
||||
977D50772623580D005345D1 /* LibrarySettingsView.swift in Sources */,
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import RealmSwift
|
||||
|
||||
class Bookmark: Object{
|
||||
class Bookmark: Object, ObjectKeyIdentifiable {
|
||||
@objc dynamic var path = ""
|
||||
@objc dynamic var zimFile: ZimFile?
|
||||
|
||||
@ -16,5 +16,4 @@ class Bookmark: Object{
|
||||
@objc dynamic var snippet: String?
|
||||
@objc dynamic var thumbImagePath: String?
|
||||
@objc dynamic var date: Date?
|
||||
|
||||
}
|
||||
|
86
iOS/Controller/BookmarksViewController.swift
Normal file
86
iOS/Controller/BookmarksViewController.swift
Normal file
@ -0,0 +1,86 @@
|
||||
//
|
||||
// BookmarksViewController.swift
|
||||
// Kiwix
|
||||
//
|
||||
// Created by Chris Li on 10/26/21.
|
||||
// Copyright © 2021 Chris Li. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
import RealmSwift
|
||||
|
||||
|
||||
class BookmarksViewController: UIHostingController<BookmarksView> {
|
||||
convenience init() {
|
||||
self.init(rootView: BookmarksView())
|
||||
navigationItem.leftBarButtonItem = UIBarButtonItem(
|
||||
title: "Done", style: .done, target: self, action: #selector(dismissController)
|
||||
)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
if splitViewController != nil {
|
||||
navigationController?.navigationBar.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func dismissController() {
|
||||
dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
struct BookmarksView: View {
|
||||
@ObservedResults(
|
||||
Bookmark.self,
|
||||
sortDescriptor: SortDescriptor(keyPath: "date", ascending: false)
|
||||
) private var bookmarks
|
||||
|
||||
var selected: (Bookmark) -> Void = { _ in }
|
||||
|
||||
var body: some View {
|
||||
if bookmarks.isEmpty {
|
||||
VStack(spacing: 20) {
|
||||
ZStack {
|
||||
Image(systemName: "star.fill")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.padding(20)
|
||||
.foregroundColor(.secondary)
|
||||
Circle().foregroundColor(.secondary).opacity(0.2)
|
||||
}.frame(width: 75, height: 75, alignment: .center)
|
||||
VStack(spacing: 6) {
|
||||
Text("No bookmarked article.").font(Font.headline)
|
||||
Text("To add, long press the bookmark button on the tool bar when reading an article.")
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
}.padding(.horizontal)
|
||||
}.navigationBarTitle("Bookmarks")
|
||||
} else {
|
||||
List {
|
||||
ForEach(bookmarks) { bookmark in
|
||||
Button { selected(bookmark) } label: {
|
||||
HStack(alignment: bookmark.thumbImagePath == nil ? .center : .top) {
|
||||
if let zimFile = bookmark.zimFile,
|
||||
let path = bookmark.thumbImagePath,
|
||||
let content = ZimFileService.shared.getURLContent(zimFileID: zimFile.fileID, contentPath: path) {
|
||||
Favicon(data: content.data)
|
||||
} else {
|
||||
Favicon(data: bookmark.zimFile?.faviconData)
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
Text(bookmark.title).fontWeight(.medium).lineLimit(1)
|
||||
if let snippet = bookmark.snippet { Text(snippet).font(.caption).lineLimit(4) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.navigationBarTitle("Bookmarks")
|
||||
}
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ class OutlineViewController: UIHostingController<OutlineView> {
|
||||
struct OutlineView: View {
|
||||
@ObservedObject var viewModel: ViewModel
|
||||
|
||||
var outlineItemSelected: (OutlineItem) -> Void = { _ in }
|
||||
var selected: (OutlineItem) -> Void = { _ in }
|
||||
|
||||
init(webView: WKWebView) {
|
||||
self.viewModel = ViewModel(webView: webView)
|
||||
@ -45,7 +45,7 @@ struct OutlineView: View {
|
||||
content.toolbar {
|
||||
ToolbarItem(placement: .principal) {
|
||||
if let title = viewModel.title {
|
||||
Button { outlineItemSelected(title) } label: {
|
||||
Button { selected(title) } label: {
|
||||
Text(title.text).fontWeight(.semibold).foregroundColor(.primary)
|
||||
}
|
||||
} else {
|
||||
@ -60,7 +60,7 @@ struct OutlineView: View {
|
||||
|
||||
@ViewBuilder
|
||||
var content: some View {
|
||||
if viewModel.items.isEmpty {
|
||||
if let items = viewModel.items, items.isEmpty {
|
||||
VStack(spacing: 30) {
|
||||
ZStack {
|
||||
Image(systemName: "list.bullet")
|
||||
@ -72,15 +72,17 @@ struct OutlineView: View {
|
||||
}.frame(width: 75, height: 75, alignment: .center)
|
||||
Text("Table of content not available.").font(Font.headline)
|
||||
}
|
||||
} else {
|
||||
TableView(items: viewModel.items, outlineItemSelected: outlineItemSelected)
|
||||
} else if let items = viewModel.items {
|
||||
TableView(items: items, selected: selected)
|
||||
.edgesIgnoringSafeArea(.bottom)
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
|
||||
class ViewModel: ObservableObject {
|
||||
@Published private(set) var title: OutlineItem?
|
||||
@Published private(set) var items = [OutlineItem]()
|
||||
@Published private(set) var items: [OutlineItem]?
|
||||
var showTitleInList = false
|
||||
|
||||
private weak var webView: WKWebView?
|
||||
@ -125,7 +127,7 @@ struct OutlineView: View {
|
||||
|
||||
struct TableView: UIViewRepresentable {
|
||||
let items: [OutlineItem]
|
||||
let outlineItemSelected: (OutlineItem) -> Void
|
||||
let selected: (OutlineItem) -> Void
|
||||
|
||||
func makeUIView(context: Context) -> UITableView {
|
||||
let tableView = UITableView(frame: .zero)
|
||||
@ -137,7 +139,7 @@ struct OutlineView: View {
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(outlineItemSelected: outlineItemSelected)
|
||||
Coordinator(selected: selected)
|
||||
}
|
||||
|
||||
func updateUIView(_ tableView: UITableView, context: Context) {
|
||||
@ -146,11 +148,11 @@ struct OutlineView: View {
|
||||
}
|
||||
|
||||
class Coordinator: NSObject, UITableViewDataSource, UITableViewDelegate {
|
||||
let outlineItemSelected: (OutlineItem) -> Void
|
||||
let selected: (OutlineItem) -> Void
|
||||
var items: [OutlineItem] = []
|
||||
|
||||
init(outlineItemSelected: @escaping (OutlineItem) -> Void) {
|
||||
self.outlineItemSelected = outlineItemSelected
|
||||
init(selected: @escaping (OutlineItem) -> Void) {
|
||||
self.selected = selected
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
@ -170,7 +172,7 @@ struct OutlineView: View {
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
outlineItemSelected(items[indexPath.row])
|
||||
selected(items[indexPath.row])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ class RootViewController: UIViewController, UISearchControllerDelegate, UISplitV
|
||||
|
||||
@objc func outlineButtonTapped() {
|
||||
let controller = OutlineViewController(webView: webViewController.webView)
|
||||
controller.rootView.outlineItemSelected = { [unowned self] item in
|
||||
controller.rootView.selected = { [unowned self] item in
|
||||
let javascript = "document.querySelectorAll(\"h1, h2, h3, h4, h5, h6\")[\(item.index)].scrollIntoView()"
|
||||
self.webViewController.webView.evaluateJavaScript(javascript)
|
||||
if let sidebarController = controller.splitViewController as? SidebarController,
|
||||
@ -242,26 +242,30 @@ class RootViewController: UIViewController, UISearchControllerDelegate, UISplitV
|
||||
}
|
||||
|
||||
@objc func bookmarkButtonTapped() {
|
||||
let bookmarksController = BookmarksViewController()
|
||||
bookmarksController.bookmarkTapped = { [weak self] url in self?.openURL(url) }
|
||||
let controller = BookmarksViewController()
|
||||
controller.rootView.selected = { [unowned self] bookmark in
|
||||
guard let zimFile = bookmark.zimFile,
|
||||
let url = URL(zimFileID: zimFile.fileID, contentPath: bookmark.path) else { return }
|
||||
self.openURL(url)
|
||||
}
|
||||
if #available(iOS 14.0, *), traitCollection.horizontalSizeClass == .regular {
|
||||
if sidebarController.displayMode == .secondaryOnly {
|
||||
sidebarController.showSidebar(bookmarksController)
|
||||
sidebarController.showSidebar(controller)
|
||||
} else if !(sidebarController.viewController(for: .primary) is BookmarksViewController) {
|
||||
sidebarController.setViewController(bookmarksController, for: .primary)
|
||||
sidebarController.setViewController(controller, for: .primary)
|
||||
} else {
|
||||
sidebarController.hideSidebar()
|
||||
}
|
||||
} else if traitCollection.horizontalSizeClass == .regular {
|
||||
if sidebarController.displayMode == .primaryHidden {
|
||||
sidebarController.showSidebar(bookmarksController)
|
||||
sidebarController.showSidebar(controller)
|
||||
} else if !(sidebarController.viewControllers.first is BookmarksViewController) {
|
||||
sidebarController.viewControllers[0] = bookmarksController
|
||||
sidebarController.viewControllers[0] = controller
|
||||
} else {
|
||||
sidebarController.hideSidebar()
|
||||
}
|
||||
} else {
|
||||
let navigationController = UINavigationController(rootViewController: bookmarksController)
|
||||
let navigationController = UINavigationController(rootViewController: controller)
|
||||
present(navigationController, animated: true)
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,17 @@
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body { color: #ffffff; background-color: #000000; }
|
||||
#content{ background-color: #000000; }
|
||||
#content { background-color: #000000; }
|
||||
|
||||
a { color: #4183c4; }
|
||||
a:visited { color: #295887; }
|
||||
table { color: #ffffff; background-color: #000000; }
|
||||
|
||||
.content table { color: #ffffff; background-color: #000000; }
|
||||
.content table tr, table tr td { background: none !important; }
|
||||
.content table.wikitable > * > tr > th { background-color: #333333; }
|
||||
.content table.infobox { color: #ffffff; background-color: #222222; }
|
||||
.content table.sidebar { background: none; background-color: #222222; }
|
||||
|
||||
.hatnote, .dablink, .rellink { color: #aaaaaa; background-color: #000000; }
|
||||
.thumbinner .thumbcaption { color: #aaaaaa; }
|
||||
dl img { background-color: #ffffff; }
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user