mirror of
https://github.com/kiwix/kiwix-apple.git
synced 2025-09-27 13:59:04 -04:00
Merge pull request #684 from kiwix/680-no-language-selected
Fix language selection initial state UI problems
This commit is contained in:
commit
7989cab2ac
@ -34,7 +34,6 @@ extension Defaults.Keys {
|
|||||||
static let libraryAutoRefresh = Key<Bool>("libraryAutoRefresh", default: true)
|
static let libraryAutoRefresh = Key<Bool>("libraryAutoRefresh", default: true)
|
||||||
static let libraryUsingOldISOLangCodes = Key<Bool>("libraryUsingOldISOLangCodes", default: true)
|
static let libraryUsingOldISOLangCodes = Key<Bool>("libraryUsingOldISOLangCodes", default: true)
|
||||||
static let libraryLastRefresh = Key<Date?>("libraryLastRefresh")
|
static let libraryLastRefresh = Key<Date?>("libraryLastRefresh")
|
||||||
static let libraryLastRefreshTime = Key<Date?>("libraryLastRefreshTime")
|
|
||||||
|
|
||||||
static let isFirstLaunch = Key<Bool>("isFirstLaunch", default: true)
|
static let isFirstLaunch = Key<Bool>("isFirstLaunch", default: true)
|
||||||
static let downloadUsingCellular = Key<Bool>("downloadUsingCellular", default: false)
|
static let downloadUsingCellular = Key<Bool>("downloadUsingCellular", default: false)
|
||||||
|
@ -11,16 +11,23 @@ import os
|
|||||||
|
|
||||||
import Defaults
|
import Defaults
|
||||||
|
|
||||||
|
public enum LibraryState {
|
||||||
|
case initial
|
||||||
|
case inProgress
|
||||||
|
case complete
|
||||||
|
}
|
||||||
|
|
||||||
public class LibraryViewModel: ObservableObject {
|
public class LibraryViewModel: ObservableObject {
|
||||||
@Published var selectedZimFile: ZimFile?
|
@Published var selectedZimFile: ZimFile?
|
||||||
@MainActor @Published public private(set) var error: Error?
|
@MainActor @Published public private(set) var error: Error?
|
||||||
@MainActor @Published public private(set) var isInProgress = false
|
@MainActor @Published public private(set) var state: LibraryState
|
||||||
|
|
||||||
private let urlSession: URLSession
|
private let urlSession: URLSession
|
||||||
private let context: NSManagedObjectContext
|
private let context: NSManagedObjectContext
|
||||||
private var insertionCount = 0
|
private var insertionCount = 0
|
||||||
private var deletionCount = 0
|
private var deletionCount = 0
|
||||||
|
|
||||||
|
@MainActor
|
||||||
public init(urlSession: URLSession? = nil) {
|
public init(urlSession: URLSession? = nil) {
|
||||||
self.urlSession = urlSession ?? URLSession.shared
|
self.urlSession = urlSession ?? URLSession.shared
|
||||||
|
|
||||||
@ -28,6 +35,11 @@ public class LibraryViewModel: ObservableObject {
|
|||||||
context.persistentStoreCoordinator = Database.shared.container.persistentStoreCoordinator
|
context.persistentStoreCoordinator = Database.shared.container.persistentStoreCoordinator
|
||||||
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
|
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
|
||||||
context.undoManager = nil
|
context.undoManager = nil
|
||||||
|
if Defaults[.libraryLastRefresh] == nil {
|
||||||
|
state = .initial
|
||||||
|
} else {
|
||||||
|
state = .complete
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func start(isUserInitiated: Bool) {
|
public func start(isUserInitiated: Bool) {
|
||||||
@ -36,19 +48,22 @@ public class LibraryViewModel: ObservableObject {
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public func start(isUserInitiated: Bool) async {
|
public func start(isUserInitiated: Bool) async {
|
||||||
|
guard state != .inProgress else { return }
|
||||||
|
let oldState = state
|
||||||
do {
|
do {
|
||||||
guard !isInProgress else { return }
|
|
||||||
isInProgress = true
|
|
||||||
defer { isInProgress = false }
|
|
||||||
|
|
||||||
// decide if refresh should proceed
|
// decide if refresh should proceed
|
||||||
let lastRefresh: Date? = Defaults[.libraryLastRefresh]
|
let lastRefresh: Date? = Defaults[.libraryLastRefresh]
|
||||||
let hasAutoRefresh: Bool = Defaults[.libraryAutoRefresh]
|
let hasAutoRefresh: Bool = Defaults[.libraryAutoRefresh]
|
||||||
let isStale = (lastRefresh?.timeIntervalSinceNow ?? -3600) <= -3600
|
let isStale = (lastRefresh?.timeIntervalSinceNow ?? -3600) <= -3600
|
||||||
guard isUserInitiated || (hasAutoRefresh && isStale) else { return }
|
guard isUserInitiated || (hasAutoRefresh && isStale) else { return }
|
||||||
|
|
||||||
|
state = .inProgress
|
||||||
|
|
||||||
// refresh library
|
// refresh library
|
||||||
guard let data = try await fetchData() else { return }
|
guard let data = try await fetchData() else {
|
||||||
|
state = oldState
|
||||||
|
return
|
||||||
|
}
|
||||||
let parser = try await parse(data: data)
|
let parser = try await parse(data: data)
|
||||||
// delete all old ISO Lang Code entries if needed, by passing in an empty parser
|
// delete all old ISO Lang Code entries if needed, by passing in an empty parser
|
||||||
if Defaults[.libraryUsingOldISOLangCodes] {
|
if Defaults[.libraryUsingOldISOLangCodes] {
|
||||||
@ -68,12 +83,14 @@ public class LibraryViewModel: ObservableObject {
|
|||||||
|
|
||||||
// reset error
|
// reset error
|
||||||
error = nil
|
error = nil
|
||||||
|
state = .complete
|
||||||
|
|
||||||
// logging
|
// logging
|
||||||
os_log("Refresh finished -- addition: %d, deletion: %d, total: %d",
|
os_log("Refresh finished -- addition: %d, deletion: %d, total: %d",
|
||||||
log: Log.OPDS, type: .default, insertionCount, deletionCount, parser.zimFileIDs.count)
|
log: Log.OPDS, type: .default, insertionCount, deletionCount, parser.zimFileIDs.count)
|
||||||
} catch {
|
} catch {
|
||||||
self.error = error
|
self.error = error
|
||||||
|
state = oldState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ struct ZimFilesNew: View {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
ToolbarItem {
|
ToolbarItem {
|
||||||
if viewModel.isInProgress {
|
if viewModel.state == .inProgress {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
.scaleEffect(0.5)
|
.scaleEffect(0.5)
|
||||||
|
@ -14,6 +14,7 @@ import Defaults
|
|||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
struct LanguageSelector: View {
|
struct LanguageSelector: View {
|
||||||
@Default(.libraryLanguageCodes) private var selected
|
@Default(.libraryLanguageCodes) private var selected
|
||||||
|
@EnvironmentObject private var library: LibraryViewModel
|
||||||
@State private var languages = [Language]()
|
@State private var languages = [Language]()
|
||||||
@State private var sortOrder = [KeyPathComparator(\Language.count, order: .reverse)]
|
@State private var sortOrder = [KeyPathComparator(\Language.count, order: .reverse)]
|
||||||
|
|
||||||
@ -35,9 +36,20 @@ struct LanguageSelector: View {
|
|||||||
Text(language.count.formatted())
|
Text(language.count.formatted())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.opacity( library.state == .complete ? 1.0 : 0.3)
|
||||||
.tableStyle(.bordered(alternatesRowBackgrounds: true))
|
.tableStyle(.bordered(alternatesRowBackgrounds: true))
|
||||||
.onChange(of: sortOrder) { languages.sort(using: $0) }
|
.onChange(of: sortOrder) { languages.sort(using: $0) }
|
||||||
.task {
|
.onChange(of: library.state) { state in
|
||||||
|
guard state != .inProgress else { return }
|
||||||
|
reloadLanguages()
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
reloadLanguages()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func reloadLanguages() {
|
||||||
|
Task {
|
||||||
languages = await Languages.fetch()
|
languages = await Languages.fetch()
|
||||||
languages.sort(using: sortOrder)
|
languages.sort(using: sortOrder)
|
||||||
}
|
}
|
||||||
|
@ -57,12 +57,12 @@ struct LibrarySettings: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
SettingSection(name: "library_settings.catalog.title".localized) {
|
SettingSection(name: "library_settings.catalog.title".localized, alignment: .top) {
|
||||||
HStack(spacing: 6) {
|
HStack(spacing: 6) {
|
||||||
Button("library_settings.button.refresh_now".localized) {
|
Button("library_settings.button.refresh_now".localized) {
|
||||||
library.start(isUserInitiated: true)
|
library.start(isUserInitiated: true)
|
||||||
}.disabled(library.isInProgress)
|
}.disabled(library.state == .inProgress)
|
||||||
if library.isInProgress {
|
if library.state == .inProgress {
|
||||||
ProgressView().progressViewStyle(.circular).scaleEffect(0.5).frame(height: 1)
|
ProgressView().progressViewStyle(.circular).scaleEffect(0.5).frame(height: 1)
|
||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
@ -173,7 +173,7 @@ struct Settings: View {
|
|||||||
LanguageSelector()
|
LanguageSelector()
|
||||||
} label: {
|
} label: {
|
||||||
SelectedLanaguageLabel()
|
SelectedLanaguageLabel()
|
||||||
}
|
}.disabled(library.state != .complete)
|
||||||
Toggle("library_settings.toggle.cellular".localized, isOn: $downloadUsingCellular)
|
Toggle("library_settings.toggle.cellular".localized, isOn: $downloadUsingCellular)
|
||||||
} header: {
|
} header: {
|
||||||
Text("library_settings.tab.library.title".localized)
|
Text("library_settings.tab.library.title".localized)
|
||||||
@ -189,7 +189,7 @@ struct Settings: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
LibraryLastRefreshTime().foregroundColor(.secondary)
|
LibraryLastRefreshTime().foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
if library.isInProgress {
|
if library.state == .inProgress {
|
||||||
HStack {
|
HStack {
|
||||||
Text("catalog_settings.refreshing.text".localized).foregroundColor(.secondary)
|
Text("catalog_settings.refreshing.text".localized).foregroundColor(.secondary)
|
||||||
Spacer()
|
Spacer()
|
||||||
|
@ -44,8 +44,8 @@ struct Welcome: View {
|
|||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
.onChange(of: library.isInProgress) { isInProgress in
|
.onChange(of: library.state) { state in
|
||||||
guard !isInProgress else { return }
|
guard state != .inProgress else { return }
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
navigation.currentItem = .categories
|
navigation.currentItem = .categories
|
||||||
#elseif os(iOS)
|
#elseif os(iOS)
|
||||||
@ -70,7 +70,8 @@ struct Welcome: View {
|
|||||||
GridSection(title: "welcome.main_page.title".localized) {
|
GridSection(title: "welcome.main_page.title".localized) {
|
||||||
ForEach(zimFiles) { zimFile in
|
ForEach(zimFiles) { zimFile in
|
||||||
Button {
|
Button {
|
||||||
guard let url = ZimFileService.shared.getMainPageURL(zimFileID: zimFile.fileID) else { return }
|
guard let url = ZimFileService.shared
|
||||||
|
.getMainPageURL(zimFileID: zimFile.fileID) else { return }
|
||||||
browser.load(url: url)
|
browser.load(url: url)
|
||||||
} label: {
|
} label: {
|
||||||
ZimFileCell(zimFile, prominent: .name)
|
ZimFileCell(zimFile, prominent: .name)
|
||||||
@ -121,7 +122,7 @@ struct Welcome: View {
|
|||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
if library.isInProgress {
|
if library.state == .inProgress {
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
Text("welcome.button.status.fetching.text".localized)
|
Text("welcome.button.status.fetching.text".localized)
|
||||||
#elseif os(iOS)
|
#elseif os(iOS)
|
||||||
@ -135,7 +136,7 @@ struct Welcome: View {
|
|||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
}.padding(6)
|
}.padding(6)
|
||||||
}.disabled(library.isInProgress)
|
}.disabled(library.state == .inProgress)
|
||||||
}
|
}
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.buttonStyle(.bordered)
|
.buttonStyle(.bordered)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user