mirror of
https://github.com/cranci1/Sora.git
synced 2026-04-12 20:40:21 +00:00
hide tabbar on settings detail views, add swipe to delete profiles, refine module menu, cleanup xcstrings, tests
This commit is contained in:
parent
0022451add
commit
fb7a591d56
14 changed files with 211 additions and 67 deletions
|
|
@ -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" : {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,9 +52,9 @@ struct SettingsViewAlternateAppIconPicker: View {
|
|||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.modifier(HideToolbarModifier())
|
||||
}
|
||||
|
||||
private func setAppIcon(named iconName: String) {
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ struct SettingsViewData: View {
|
|||
}
|
||||
.navigationTitle("App Data")
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.modifier(HideToolbarModifier())
|
||||
}
|
||||
|
||||
func eraseAppData() {
|
||||
|
|
|
|||
|
|
@ -153,5 +153,6 @@ struct SettingsViewGeneral: View {
|
|||
SettingsViewAlternateAppIconPicker(isPresented: $showAppIconPicker)
|
||||
}
|
||||
}
|
||||
.modifier(HideToolbarModifier())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,5 +54,6 @@ struct SettingsViewLogger: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.modifier(HideToolbarModifier())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,5 +83,6 @@ struct SettingsViewLoggerFilter: View {
|
|||
}
|
||||
}
|
||||
.navigationTitle("Log Filters")
|
||||
.modifier(HideToolbarModifier())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@ struct SettingsViewModule: View {
|
|||
} message: {
|
||||
Text(errorMessage ?? "Unknown error")
|
||||
}
|
||||
.modifier(HideToolbarModifier())
|
||||
}
|
||||
|
||||
func showAddModuleAlert() {
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ struct SettingsViewPlayer: View {
|
|||
SubtitleSettingsSection()
|
||||
}
|
||||
.navigationTitle("Player")
|
||||
.modifier(HideToolbarModifier())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ struct SettingsViewTrackers: View {
|
|||
.onDisappear {
|
||||
removeNotificationObservers()
|
||||
}
|
||||
.modifier(HideToolbarModifier())
|
||||
}
|
||||
|
||||
func removeNotificationObservers() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue