hide tabbar on settings detail views, add swipe to delete profiles, refine module menu, cleanup xcstrings, tests

This commit is contained in:
Dominic Drees 2025-05-01 22:03:44 +02:00
parent 0022451add
commit fb7a591d56
14 changed files with 211 additions and 67 deletions

View file

@ -33,23 +33,6 @@
}
}
},
"%@ - Episode %lld" : {
"extractionState" : "stale",
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "%1$@ - Episode %2$lld"
}
},
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "%@ - Episode %lld"
}
}
}
},
"%@ %@" : {
"localizations" : {
"de" : {
@ -64,7 +47,8 @@
"value" : "%@ %@"
}
}
}
},
"shouldTranslate" : false
},
"%lld" : {
"localizations" : {
@ -96,7 +80,8 @@
"value" : "%lld-%lld"
}
}
}
},
"shouldTranslate" : false
},
"%llds" : {
"localizations" : {
@ -226,6 +211,22 @@
}
}
},
"Add Modules" : {
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Module hinzufügen"
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Add Modules"
}
}
}
},
"Adjust the number of media items per row in portrait and landscape modes." : {
"localizations" : {
"de" : {
@ -867,21 +868,17 @@
}
},
"Download Stream" : {
},
"Downloads" : {
"extractionState" : "stale",
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Downloads"
"value" : "Stream Herunterladen"
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Downloads"
"value" : "Download Stream"
}
}
}
@ -935,7 +932,20 @@
}
},
"Enter HLS URL" : {
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "HLS Link einfügen"
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Enter HLS URL"
}
}
}
},
"Episode %lld" : {
"localizations" : {
@ -1259,7 +1269,20 @@
}
},
"HLS Downloader" : {
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "HLS Manager"
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "HLS Downloader"
}
}
}
},
"Hold Speed:" : {
"localizations" : {
@ -1977,8 +2000,37 @@
}
}
},
"No modules available" : {
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Keine Module verfügbar"
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "No Modules available"
}
}
}
},
"No offline content available" : {
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Kein Offline Inhalt verfügbar"
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "No offline content available"
}
}
}
},
"No Results Found" : {
"localizations" : {
@ -2061,7 +2113,20 @@
}
},
"Play Offline Content" : {
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Offline Inhalt abspielen"
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Play Offline Content"
}
}
}
},
"Player" : {
"localizations" : {

View file

@ -23,3 +23,18 @@ struct SeparatorAlignmentModifier: ViewModifier {
}
}
}
struct HideToolbarModifier: ViewModifier {
func body(content: Content) -> some View {
if #available(iOS 18.0, *) {
content
.toolbarVisibility(.hidden, for: .tabBar)
} else if #available(iOS 16.0, *) {
content
.toolbar(.hidden, for: .tabBar)
} else {
content
}
}
}

View file

@ -75,16 +75,18 @@ class ProfileStore: ObservableObject {
setCurrentProfile(profiles[index])
}
public func deleteCurrentProfile() {
if profiles.count == 1 { return }
public func deleteProfile(removalID: UUID?) {
guard let removalID,
profiles.count == 1
else { return }
if let suite = UserDefaults(suiteName: currentProfile.id.uuidString) {
if let suite = UserDefaults(suiteName: removalID.uuidString) {
for key in suite.dictionaryRepresentation().keys {
suite.removeObject(forKey: key)
}
}
profiles.removeAll { $0.id == currentProfile.id }
profiles.removeAll { $0.id == removalID }
if let firstProfile = profiles.first {
saveProfiles()

View file

@ -32,6 +32,7 @@ struct ExploreView: View {
@State private var isLandscape: Bool = UIDevice.current.orientation.isLandscape
@State private var isModuleSelectorPresented = false
@State private var showProfileSettings = false
@State private var showModuleSettings = false
@State private var isLoading = false
private var selectedModule: ScrapingModule? {
@ -149,6 +150,13 @@ struct ExploreView: View {
label: { EmptyView() }
)
.hidden()
NavigationLink(
destination: SettingsViewModule(),
isActive: $showModuleSettings,
label: { EmptyView() }
)
.hidden()
}
.navigationTitle("Explore")
.navigationBarTitleDisplayMode(.large)
@ -189,22 +197,35 @@ struct ExploreView: View {
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
ForEach(getModuleLanguageGroups(), id: \.self) { language in
Menu(language) {
ForEach(getModulesForLanguage(language), id: \.id) { module in
Button {
selectedModuleId = module.id.uuidString
} label: {
HStack {
KFImage(URL(string: module.metadata.iconUrl))
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20, height: 20)
.cornerRadius(4)
Text(module.metadata.sourceName)
if module.id.uuidString == selectedModuleId {
Image(systemName: "checkmark")
.foregroundColor(.accentColor)
if getModuleLanguageGroups().isEmpty {
Button("No modules available") { }
.disabled(true)
Divider()
Button {
showModuleSettings = true
} label: {
Label("Add Modules", systemImage: "plus.app")
}
} else {
ForEach(getModuleLanguageGroups(), id: \.self) { language in
Menu(language) {
ForEach(getModulesForLanguage(language), id: \.id) { module in
Button {
selectedModuleId = module.id.uuidString
} label: {
HStack {
KFImage(URL(string: module.metadata.iconUrl))
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20, height: 20)
.cornerRadius(4)
Text(module.metadata.sourceName)
if module.id.uuidString == selectedModuleId {
Image(systemName: "checkmark")
.foregroundColor(.accentColor)
}
}
}
}

View file

@ -34,6 +34,7 @@ struct SearchView: View {
@State private var isLandscape: Bool = UIDevice.current.orientation.isLandscape
@State private var isModuleSelectorPresented = false
@State private var showProfileSettings = false
@State private var showModuleSettings = false
private var selectedModule: ScrapingModule? {
guard let id = selectedModuleId else { return nil }
@ -168,6 +169,13 @@ struct SearchView: View {
label: { EmptyView() }
)
.hidden()
NavigationLink(
destination: SettingsViewModule(),
isActive: $showModuleSettings,
label: { EmptyView() }
)
.hidden()
}
.navigationTitle("Search")
.navigationBarTitleDisplayMode(.large)
@ -207,22 +215,35 @@ struct SearchView: View {
}
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
ForEach(getModuleLanguageGroups(), id: \.self) { language in
Menu(language) {
ForEach(getModulesForLanguage(language), id: \.id) { module in
Button {
selectedModuleId = module.id.uuidString
} label: {
HStack {
KFImage(URL(string: module.metadata.iconUrl))
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20, height: 20)
.cornerRadius(4)
Text(module.metadata.sourceName)
if module.id.uuidString == selectedModuleId {
Image(systemName: "checkmark")
.foregroundColor(.accentColor)
if getModuleLanguageGroups().isEmpty {
Button("No modules available") { }
.disabled(true)
Divider()
Button {
showModuleSettings = true
} label: {
Label("Add Modules", systemImage: "plus.app")
}
} else {
ForEach(getModuleLanguageGroups(), id: \.self) { language in
Menu(language) {
ForEach(getModulesForLanguage(language), id: \.id) { module in
Button {
selectedModuleId = module.id.uuidString
} label: {
HStack {
KFImage(URL(string: module.metadata.iconUrl))
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20, height: 20)
.cornerRadius(4)
Text(module.metadata.sourceName)
if module.id.uuidString == selectedModuleId {
Image(systemName: "checkmark")
.foregroundColor(.accentColor)
}
}
}
}

View file

@ -52,9 +52,9 @@ struct SettingsViewAlternateAppIconPicker: View {
}
.padding()
}
Spacer()
}
.modifier(HideToolbarModifier())
}
private func setAppIcon(named iconName: String) {

View file

@ -54,6 +54,7 @@ struct SettingsViewData: View {
}
.navigationTitle("App Data")
.navigationViewStyle(StackNavigationViewStyle())
.modifier(HideToolbarModifier())
}
func eraseAppData() {

View file

@ -153,5 +153,6 @@ struct SettingsViewGeneral: View {
SettingsViewAlternateAppIconPicker(isPresented: $showAppIconPicker)
}
}
.modifier(HideToolbarModifier())
}
}

View file

@ -54,5 +54,6 @@ struct SettingsViewLogger: View {
}
}
}
.modifier(HideToolbarModifier())
}
}

View file

@ -83,5 +83,6 @@ struct SettingsViewLoggerFilter: View {
}
}
.navigationTitle("Log Filters")
.modifier(HideToolbarModifier())
}
}

View file

@ -180,6 +180,7 @@ struct SettingsViewModule: View {
} message: {
Text(errorMessage ?? "Unknown error")
}
.modifier(HideToolbarModifier())
}
func showAddModuleAlert() {

View file

@ -108,6 +108,7 @@ struct SettingsViewPlayer: View {
SubtitleSettingsSection()
}
.navigationTitle("Player")
.modifier(HideToolbarModifier())
}
}

View file

@ -41,6 +41,7 @@ struct SettingsViewProfile: View {
@EnvironmentObject var profileStore: ProfileStore
@State private var showDeleteAlert = false
@State private var profileIDToRemove: UUID?
var body: some View {
Form {
@ -53,6 +54,16 @@ struct SettingsViewProfile: View {
isSelected: profile.id == profileStore.currentProfile.id
)
}
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
if profileStore.profiles.count > 1 {
Button(role: .destructive) {
profileIDToRemove = profile.id
showDeleteAlert = true
} label: {
Label("Delete", systemImage: "trash")
}
}
}
}
}
@ -92,6 +103,7 @@ struct SettingsViewProfile: View {
if profileStore.profiles.count > 1 {
Button(action: {
profileIDToRemove = profileStore.currentProfile.id
showDeleteAlert = true
}) {
Text("Delete Selected Profile")
@ -107,7 +119,7 @@ struct SettingsViewProfile: View {
title: Text("Delete Profile"),
message: Text("Are you sure you want to delete this profile? This action cannot be undone."),
primaryButton: .destructive(Text("Delete")) {
profileStore.deleteCurrentProfile()
profileStore.deleteProfile(removalID: profileIDToRemove)
},
secondaryButton: .cancel()
)
@ -133,5 +145,6 @@ struct SettingsViewProfile: View {
}
}
}
.modifier(HideToolbarModifier())
}
}

View file

@ -118,6 +118,7 @@ struct SettingsViewTrackers: View {
.onDisappear {
removeNotificationObservers()
}
.modifier(HideToolbarModifier())
}
func removeNotificationObservers() {