diff --git a/App/CompactViewController.swift b/App/CompactViewController.swift index 03bfebbd..a810a74d 100644 --- a/App/CompactViewController.swift +++ b/App/CompactViewController.swift @@ -228,8 +228,6 @@ private struct Content: View where LaunchModel: LaunchProtocol { sortDescriptors: [NSSortDescriptor(keyPath: \ZimFile.size, ascending: false)], predicate: ZimFile.openedPredicate ) private var zimFiles: FetchedResults - @State var isInitialLoad: Bool = true - /// 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 @@ -300,11 +298,6 @@ private struct Content: View where LaunchModel: LaunchProtocol { browser.refreshVideoState() } } - .onChange(of: browser.isLoading) { isLoading in - if isLoading == false { // wait for the first full webpage load - isInitialLoad = false - } - } } } #endif diff --git a/App/SplitViewController.swift b/App/SplitViewController.swift index 85f51973..cefd138e 100644 --- a/App/SplitViewController.swift +++ b/App/SplitViewController.swift @@ -141,7 +141,7 @@ final class SplitViewController: UISplitViewController { let controller = UIHostingController(rootView: Settings()) setViewController(UINavigationController(rootViewController: controller), for: .secondary) case .loading: - let controller = UIHostingController(rootView: LoadingView()) + let controller = UIHostingController(rootView: LoadingDataView()) setViewController(UINavigationController(rootViewController: controller), for: .secondary) default: let controller = UIHostingController(rootView: Text("vc-not-implemented")) diff --git a/Views/BrowserTab.swift b/Views/BrowserTab.swift index 23d0f307..1d84cb10 100644 --- a/Views/BrowserTab.swift +++ b/Views/BrowserTab.swift @@ -14,18 +14,25 @@ // along with Kiwix; If not, see https://www.gnu.org/licenses/. import SwiftUI +import Defaults /// This is macOS and iPad only specific, not used on iPhone struct BrowserTab: View { @Environment(\.scenePhase) private var scenePhase @EnvironmentObject private var browser: BrowserViewModel + @EnvironmentObject private var library: LibraryViewModel @StateObject private var search = SearchViewModel() var body: some View { - Content().toolbar { - #if os(macOS) + let model = if FeatureFlags.hasLibrary { + CatalogLaunchViewModel(library: library, browser: browser) + } else { + NoCatalogLaunchViewModel(browser: browser) + } + Content(model: model).toolbar { +#if os(macOS) ToolbarItemGroup(placement: .navigation) { NavigationButtons() } - #elseif os(iOS) +#elseif os(iOS) ToolbarItemGroup(placement: .navigationBarLeading) { if #unavailable(iOS 16) { Button { @@ -36,17 +43,17 @@ struct BrowserTab: View { } NavigationButtons() } - #endif +#endif ToolbarItemGroup(placement: .primaryAction) { OutlineButton() ExportButton() - #if os(macOS) +#if os(macOS) PrintButton() - #endif +#endif BookmarkButton() - #if os(iOS) +#if os(iOS) ContentSearchButton() - #endif +#endif ArticleShortcutButtons(displayMode: .mainAndRandomArticle) } } @@ -62,12 +69,12 @@ struct BrowserTab: View { } } .modify { view in - #if os(macOS) +#if os(macOS) view.navigationTitle(browser.articleTitle.isEmpty ? Brand.appName : browser.articleTitle) .navigationSubtitle(browser.zimFileName) - #elseif os(iOS) +#elseif os(iOS) view - #endif +#endif } .onAppear { browser.updateLastOpened() @@ -78,11 +85,23 @@ struct BrowserTab: View { } } - struct Content: View { + private struct Content: View where LaunchModel: LaunchProtocol { @Environment(\.isSearching) private var isSearching @EnvironmentObject private var browser: BrowserViewModel + @FetchRequest( + sortDescriptors: [NSSortDescriptor(keyPath: \ZimFile.size, ascending: false)], + predicate: ZimFile.openedPredicate + ) private var zimFiles: FetchedResults + /// 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 { + let _ = model.updateWith(hasZimFiles: !zimFiles.isEmpty, + hasSeenCategories: hasSeenCategories) + let _ = debugPrint("model.state: \(model.state)") GeometryReader { proxy in Group { if isSearching { @@ -92,17 +111,32 @@ struct BrowserTab: View { #elseif os(iOS) .environment(\.horizontalSizeClass, proxy.size.width > 750 ? .regular : .compact) #endif - } else if browser.url == nil && FeatureFlags.hasLibrary { - Welcome(showLibrary: nil) } else { - WebView().ignoresSafeArea() - #if os(macOS) - .overlay(alignment: .bottomTrailing) { - ContentSearchBar( - model: ContentSearchViewModel(findInWebPage: browser.webView.find(_:configuration:)) - ) - } - #endif + switch model.state { + case .loadingData: + LoadingDataView() + case .webPage(let isLoading): + WebView() + .ignoresSafeArea() + .overlay { + 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) + } } } } diff --git a/Views/LocalLibraryList.swift b/Views/LocalLibraryList.swift new file mode 100644 index 00000000..e3f2a17b --- /dev/null +++ b/Views/LocalLibraryList.swift @@ -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 + @FetchRequest( + sortDescriptors: [NSSortDescriptor(keyPath: \ZimFile.size, ascending: false)], + predicate: ZimFile.openedPredicate, + animation: .easeInOut + ) private var zimFiles: FetchedResults + + 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)) + } +} diff --git a/Views/Welcome.swift b/Views/WelcomeCatalog.swift similarity index 75% rename from Views/Welcome.swift rename to Views/WelcomeCatalog.swift index 8488e792..68c1c59d 100644 --- a/Views/Welcome.swift +++ b/Views/WelcomeCatalog.swift @@ -17,56 +17,6 @@ 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 - @FetchRequest( - sortDescriptors: [NSSortDescriptor(keyPath: \ZimFile.size, ascending: false)], - predicate: ZimFile.openedPredicate, - animation: .easeInOut - ) private var zimFiles: FetchedResults - - 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 { @Environment(\.horizontalSizeClass) private var horizontalSizeClass @Environment(\.verticalSizeClass) private var verticalSizeClass