Apply LaunchViewModel on BrowserTab

This commit is contained in:
Balazs Perlaki-Horvath 2024-10-17 09:25:38 +02:00
parent 7ad164d178
commit d94c862110
5 changed files with 124 additions and 80 deletions

View File

@ -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

View File

@ -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"))

View File

@ -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)
}
} }
} }
} }

View 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))
}
}

View File

@ -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