mirror of
https://github.com/kiwix/kiwix-apple.git
synced 2025-09-24 04:03:03 -04:00
Apply LaunchViewModel on BrowserTab
This commit is contained in:
parent
7ad164d178
commit
d94c862110
@ -228,8 +228,6 @@ private struct Content<LaunchModel>: View where LaunchModel: LaunchProtocol {
|
|||||||
sortDescriptors: [NSSortDescriptor(keyPath: \ZimFile.size, ascending: false)],
|
sortDescriptors: [NSSortDescriptor(keyPath: \ZimFile.size, ascending: false)],
|
||||||
predicate: ZimFile.openedPredicate
|
predicate: ZimFile.openedPredicate
|
||||||
) private var zimFiles: FetchedResults<ZimFile>
|
) private var zimFiles: FetchedResults<ZimFile>
|
||||||
@State var isInitialLoad: Bool = true
|
|
||||||
|
|
||||||
/// this is still hacky a bit, as the change from here re-validates the view
|
/// this is still hacky a bit, as the change from here re-validates the view
|
||||||
/// which triggers the model to be revalidated
|
/// which triggers the model to be revalidated
|
||||||
@Default(.hasSeenCategories) private var hasSeenCategories
|
@Default(.hasSeenCategories) private var hasSeenCategories
|
||||||
@ -300,11 +298,6 @@ private struct Content<LaunchModel>: View where LaunchModel: LaunchProtocol {
|
|||||||
browser.refreshVideoState()
|
browser.refreshVideoState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: browser.isLoading) { isLoading in
|
|
||||||
if isLoading == false { // wait for the first full webpage load
|
|
||||||
isInitialLoad = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -141,7 +141,7 @@ final class SplitViewController: UISplitViewController {
|
|||||||
let controller = UIHostingController(rootView: Settings())
|
let controller = UIHostingController(rootView: Settings())
|
||||||
setViewController(UINavigationController(rootViewController: controller), for: .secondary)
|
setViewController(UINavigationController(rootViewController: controller), for: .secondary)
|
||||||
case .loading:
|
case .loading:
|
||||||
let controller = UIHostingController(rootView: LoadingView())
|
let controller = UIHostingController(rootView: LoadingDataView())
|
||||||
setViewController(UINavigationController(rootViewController: controller), for: .secondary)
|
setViewController(UINavigationController(rootViewController: controller), for: .secondary)
|
||||||
default:
|
default:
|
||||||
let controller = UIHostingController(rootView: Text("vc-not-implemented"))
|
let controller = UIHostingController(rootView: Text("vc-not-implemented"))
|
||||||
|
@ -14,18 +14,25 @@
|
|||||||
// along with Kiwix; If not, see https://www.gnu.org/licenses/.
|
// along with Kiwix; If not, see https://www.gnu.org/licenses/.
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Defaults
|
||||||
|
|
||||||
/// This is macOS and iPad only specific, not used on iPhone
|
/// This is macOS and iPad only specific, not used on iPhone
|
||||||
struct BrowserTab: View {
|
struct BrowserTab: View {
|
||||||
@Environment(\.scenePhase) private var scenePhase
|
@Environment(\.scenePhase) private var scenePhase
|
||||||
@EnvironmentObject private var browser: BrowserViewModel
|
@EnvironmentObject private var browser: BrowserViewModel
|
||||||
|
@EnvironmentObject private var library: LibraryViewModel
|
||||||
@StateObject private var search = SearchViewModel()
|
@StateObject private var search = SearchViewModel()
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Content().toolbar {
|
let model = if FeatureFlags.hasLibrary {
|
||||||
#if os(macOS)
|
CatalogLaunchViewModel(library: library, browser: browser)
|
||||||
|
} else {
|
||||||
|
NoCatalogLaunchViewModel(browser: browser)
|
||||||
|
}
|
||||||
|
Content(model: model).toolbar {
|
||||||
|
#if os(macOS)
|
||||||
ToolbarItemGroup(placement: .navigation) { NavigationButtons() }
|
ToolbarItemGroup(placement: .navigation) { NavigationButtons() }
|
||||||
#elseif os(iOS)
|
#elseif os(iOS)
|
||||||
ToolbarItemGroup(placement: .navigationBarLeading) {
|
ToolbarItemGroup(placement: .navigationBarLeading) {
|
||||||
if #unavailable(iOS 16) {
|
if #unavailable(iOS 16) {
|
||||||
Button {
|
Button {
|
||||||
@ -36,17 +43,17 @@ struct BrowserTab: View {
|
|||||||
}
|
}
|
||||||
NavigationButtons()
|
NavigationButtons()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
ToolbarItemGroup(placement: .primaryAction) {
|
ToolbarItemGroup(placement: .primaryAction) {
|
||||||
OutlineButton()
|
OutlineButton()
|
||||||
ExportButton()
|
ExportButton()
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
PrintButton()
|
PrintButton()
|
||||||
#endif
|
#endif
|
||||||
BookmarkButton()
|
BookmarkButton()
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
ContentSearchButton()
|
ContentSearchButton()
|
||||||
#endif
|
#endif
|
||||||
ArticleShortcutButtons(displayMode: .mainAndRandomArticle)
|
ArticleShortcutButtons(displayMode: .mainAndRandomArticle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,12 +69,12 @@ struct BrowserTab: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.modify { view in
|
.modify { view in
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
view.navigationTitle(browser.articleTitle.isEmpty ? Brand.appName : browser.articleTitle)
|
view.navigationTitle(browser.articleTitle.isEmpty ? Brand.appName : browser.articleTitle)
|
||||||
.navigationSubtitle(browser.zimFileName)
|
.navigationSubtitle(browser.zimFileName)
|
||||||
#elseif os(iOS)
|
#elseif os(iOS)
|
||||||
view
|
view
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
browser.updateLastOpened()
|
browser.updateLastOpened()
|
||||||
@ -78,11 +85,23 @@ struct BrowserTab: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Content: View {
|
private struct Content<LaunchModel>: View where LaunchModel: LaunchProtocol {
|
||||||
@Environment(\.isSearching) private var isSearching
|
@Environment(\.isSearching) private var isSearching
|
||||||
@EnvironmentObject private var browser: BrowserViewModel
|
@EnvironmentObject private var browser: BrowserViewModel
|
||||||
|
@FetchRequest(
|
||||||
|
sortDescriptors: [NSSortDescriptor(keyPath: \ZimFile.size, ascending: false)],
|
||||||
|
predicate: ZimFile.openedPredicate
|
||||||
|
) private var zimFiles: FetchedResults<ZimFile>
|
||||||
|
/// this is still hacky a bit, as the change from here re-validates the view
|
||||||
|
/// which triggers the model to be revalidated
|
||||||
|
@Default(.hasSeenCategories) private var hasSeenCategories
|
||||||
|
@ObservedObject var model: LaunchModel
|
||||||
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
let _ = model.updateWith(hasZimFiles: !zimFiles.isEmpty,
|
||||||
|
hasSeenCategories: hasSeenCategories)
|
||||||
|
let _ = debugPrint("model.state: \(model.state)")
|
||||||
GeometryReader { proxy in
|
GeometryReader { proxy in
|
||||||
Group {
|
Group {
|
||||||
if isSearching {
|
if isSearching {
|
||||||
@ -92,17 +111,32 @@ struct BrowserTab: View {
|
|||||||
#elseif os(iOS)
|
#elseif os(iOS)
|
||||||
.environment(\.horizontalSizeClass, proxy.size.width > 750 ? .regular : .compact)
|
.environment(\.horizontalSizeClass, proxy.size.width > 750 ? .regular : .compact)
|
||||||
#endif
|
#endif
|
||||||
} else if browser.url == nil && FeatureFlags.hasLibrary {
|
|
||||||
Welcome(showLibrary: nil)
|
|
||||||
} else {
|
} else {
|
||||||
WebView().ignoresSafeArea()
|
switch model.state {
|
||||||
#if os(macOS)
|
case .loadingData:
|
||||||
.overlay(alignment: .bottomTrailing) {
|
LoadingDataView()
|
||||||
ContentSearchBar(
|
case .webPage(let isLoading):
|
||||||
model: ContentSearchViewModel(findInWebPage: browser.webView.find(_:configuration:))
|
WebView()
|
||||||
)
|
.ignoresSafeArea()
|
||||||
}
|
.overlay {
|
||||||
#endif
|
if isLoading {
|
||||||
|
LoadingProgressView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if os(macOS)
|
||||||
|
.overlay(alignment: .bottomTrailing) {
|
||||||
|
ContentSearchBar(
|
||||||
|
model: ContentSearchViewModel(findInWebPage: browser.webView.find(_:configuration:))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
case .catalog(.fetching):
|
||||||
|
FetchingCatalogView()
|
||||||
|
case .catalog(.list):
|
||||||
|
LocalLibraryList()
|
||||||
|
case .catalog(.welcome(let welcomeViewState)):
|
||||||
|
WelcomeCatalog(viewState: welcomeViewState, showLibrary: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
67
Views/LocalLibraryList.swift
Normal file
67
Views/LocalLibraryList.swift
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// This file is part of Kiwix for iOS & macOS.
|
||||||
|
//
|
||||||
|
// Kiwix is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 3 of the License, or
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// Kiwix is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Kiwix; If not, see https://www.gnu.org/licenses/.
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import Combine
|
||||||
|
import Defaults
|
||||||
|
|
||||||
|
struct LocalLibraryList: View {
|
||||||
|
@EnvironmentObject private var browser: BrowserViewModel
|
||||||
|
@FetchRequest(
|
||||||
|
sortDescriptors: [NSSortDescriptor(keyPath: \Bookmark.created, ascending: false)],
|
||||||
|
animation: .easeInOut
|
||||||
|
) private var bookmarks: FetchedResults<Bookmark>
|
||||||
|
@FetchRequest(
|
||||||
|
sortDescriptors: [NSSortDescriptor(keyPath: \ZimFile.size, ascending: false)],
|
||||||
|
predicate: ZimFile.openedPredicate,
|
||||||
|
animation: .easeInOut
|
||||||
|
) private var zimFiles: FetchedResults<ZimFile>
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
LazyVGrid(
|
||||||
|
columns: ([GridItem(.adaptive(minimum: 250, maximum: 500), spacing: 12)]),
|
||||||
|
alignment: .leading,
|
||||||
|
spacing: 12
|
||||||
|
) {
|
||||||
|
GridSection(title: "welcome.main_page.title".localized) {
|
||||||
|
ForEach(zimFiles) { zimFile in
|
||||||
|
AsyncButtonView {
|
||||||
|
guard let url = await ZimFileService.shared
|
||||||
|
.getMainPageURL(zimFileID: zimFile.fileID) else { return }
|
||||||
|
browser.load(url: url)
|
||||||
|
} label: {
|
||||||
|
ZimFileCell(zimFile, prominent: .name)
|
||||||
|
} loading: {
|
||||||
|
ZimFileCell(zimFile, prominent: .name, isLoading: true)
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !bookmarks.isEmpty {
|
||||||
|
GridSection(title: "welcome.grid.bookmarks.title".localized) {
|
||||||
|
ForEach(bookmarks.prefix(6)) { bookmark in
|
||||||
|
Button {
|
||||||
|
browser.load(url: bookmark.articleURL)
|
||||||
|
} label: {
|
||||||
|
ArticleCell(bookmark: bookmark)
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
.modifier(BookmarkContextMenu(bookmark: bookmark))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.modifier(GridCommon(edges: .all))
|
||||||
|
}
|
||||||
|
}
|
@ -17,56 +17,6 @@ import SwiftUI
|
|||||||
import Combine
|
import Combine
|
||||||
import Defaults
|
import Defaults
|
||||||
|
|
||||||
|
|
||||||
struct LocalLibraryList: View {
|
|
||||||
@EnvironmentObject private var browser: BrowserViewModel
|
|
||||||
@FetchRequest(
|
|
||||||
sortDescriptors: [NSSortDescriptor(keyPath: \Bookmark.created, ascending: false)],
|
|
||||||
animation: .easeInOut
|
|
||||||
) private var bookmarks: FetchedResults<Bookmark>
|
|
||||||
@FetchRequest(
|
|
||||||
sortDescriptors: [NSSortDescriptor(keyPath: \ZimFile.size, ascending: false)],
|
|
||||||
predicate: ZimFile.openedPredicate,
|
|
||||||
animation: .easeInOut
|
|
||||||
) private var zimFiles: FetchedResults<ZimFile>
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
LazyVGrid(
|
|
||||||
columns: ([GridItem(.adaptive(minimum: 250, maximum: 500), spacing: 12)]),
|
|
||||||
alignment: .leading,
|
|
||||||
spacing: 12
|
|
||||||
) {
|
|
||||||
GridSection(title: "welcome.main_page.title".localized) {
|
|
||||||
ForEach(zimFiles) { zimFile in
|
|
||||||
AsyncButtonView {
|
|
||||||
guard let url = await ZimFileService.shared
|
|
||||||
.getMainPageURL(zimFileID: zimFile.fileID) else { return }
|
|
||||||
browser.load(url: url)
|
|
||||||
} label: {
|
|
||||||
ZimFileCell(zimFile, prominent: .name)
|
|
||||||
} loading: {
|
|
||||||
ZimFileCell(zimFile, prominent: .name, isLoading: true)
|
|
||||||
}
|
|
||||||
.buttonStyle(.plain)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !bookmarks.isEmpty {
|
|
||||||
GridSection(title: "welcome.grid.bookmarks.title".localized) {
|
|
||||||
ForEach(bookmarks.prefix(6)) { bookmark in
|
|
||||||
Button {
|
|
||||||
browser.load(url: bookmark.articleURL)
|
|
||||||
} label: {
|
|
||||||
ArticleCell(bookmark: bookmark)
|
|
||||||
}
|
|
||||||
.buttonStyle(.plain)
|
|
||||||
.modifier(BookmarkContextMenu(bookmark: bookmark))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.modifier(GridCommon(edges: .all))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WelcomeCatalog: View {
|
struct WelcomeCatalog: View {
|
||||||
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
Loading…
x
Reference in New Issue
Block a user