mirror of
https://github.com/cranci1/Sora.git
synced 2026-04-21 08:32:00 +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" : {
|
"localizations" : {
|
||||||
"de" : {
|
"de" : {
|
||||||
|
|
@ -64,7 +47,8 @@
|
||||||
"value" : "%@ %@"
|
"value" : "%@ %@"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"shouldTranslate" : false
|
||||||
},
|
},
|
||||||
"%lld" : {
|
"%lld" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|
@ -96,7 +80,8 @@
|
||||||
"value" : "%lld-%lld"
|
"value" : "%lld-%lld"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"shouldTranslate" : false
|
||||||
},
|
},
|
||||||
"%llds" : {
|
"%llds" : {
|
||||||
"localizations" : {
|
"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." : {
|
"Adjust the number of media items per row in portrait and landscape modes." : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"de" : {
|
"de" : {
|
||||||
|
|
@ -867,21 +868,17 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Download Stream" : {
|
"Download Stream" : {
|
||||||
|
|
||||||
},
|
|
||||||
"Downloads" : {
|
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"de" : {
|
"de" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
"state" : "translated",
|
"state" : "translated",
|
||||||
"value" : "Downloads"
|
"value" : "Stream Herunterladen"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
"state" : "translated",
|
"state" : "translated",
|
||||||
"value" : "Downloads"
|
"value" : "Download Stream"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -935,7 +932,20 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Enter HLS URL" : {
|
"Enter HLS URL" : {
|
||||||
|
"localizations" : {
|
||||||
|
"de" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "HLS Link einfügen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Enter HLS URL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Episode %lld" : {
|
"Episode %lld" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|
@ -1259,7 +1269,20 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"HLS Downloader" : {
|
"HLS Downloader" : {
|
||||||
|
"localizations" : {
|
||||||
|
"de" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "HLS Manager"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "HLS Downloader"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Hold Speed:" : {
|
"Hold Speed:" : {
|
||||||
"localizations" : {
|
"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" : {
|
"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" : {
|
"No Results Found" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|
@ -2061,7 +2113,20 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Play Offline Content" : {
|
"Play Offline Content" : {
|
||||||
|
"localizations" : {
|
||||||
|
"de" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Offline Inhalt abspielen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Play Offline Content"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Player" : {
|
"Player" : {
|
||||||
"localizations" : {
|
"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])
|
setCurrentProfile(profiles[index])
|
||||||
}
|
}
|
||||||
|
|
||||||
public func deleteCurrentProfile() {
|
public func deleteProfile(removalID: UUID?) {
|
||||||
if profiles.count == 1 { return }
|
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 {
|
for key in suite.dictionaryRepresentation().keys {
|
||||||
suite.removeObject(forKey: key)
|
suite.removeObject(forKey: key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
profiles.removeAll { $0.id == currentProfile.id }
|
profiles.removeAll { $0.id == removalID }
|
||||||
|
|
||||||
if let firstProfile = profiles.first {
|
if let firstProfile = profiles.first {
|
||||||
saveProfiles()
|
saveProfiles()
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ struct ExploreView: View {
|
||||||
@State private var isLandscape: Bool = UIDevice.current.orientation.isLandscape
|
@State private var isLandscape: Bool = UIDevice.current.orientation.isLandscape
|
||||||
@State private var isModuleSelectorPresented = false
|
@State private var isModuleSelectorPresented = false
|
||||||
@State private var showProfileSettings = false
|
@State private var showProfileSettings = false
|
||||||
|
@State private var showModuleSettings = false
|
||||||
@State private var isLoading = false
|
@State private var isLoading = false
|
||||||
|
|
||||||
private var selectedModule: ScrapingModule? {
|
private var selectedModule: ScrapingModule? {
|
||||||
|
|
@ -149,6 +150,13 @@ struct ExploreView: View {
|
||||||
label: { EmptyView() }
|
label: { EmptyView() }
|
||||||
)
|
)
|
||||||
.hidden()
|
.hidden()
|
||||||
|
|
||||||
|
NavigationLink(
|
||||||
|
destination: SettingsViewModule(),
|
||||||
|
isActive: $showModuleSettings,
|
||||||
|
label: { EmptyView() }
|
||||||
|
)
|
||||||
|
.hidden()
|
||||||
}
|
}
|
||||||
.navigationTitle("Explore")
|
.navigationTitle("Explore")
|
||||||
.navigationBarTitleDisplayMode(.large)
|
.navigationBarTitleDisplayMode(.large)
|
||||||
|
|
@ -189,22 +197,35 @@ struct ExploreView: View {
|
||||||
|
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||||||
Menu {
|
Menu {
|
||||||
ForEach(getModuleLanguageGroups(), id: \.self) { language in
|
if getModuleLanguageGroups().isEmpty {
|
||||||
Menu(language) {
|
Button("No modules available") { }
|
||||||
ForEach(getModulesForLanguage(language), id: \.id) { module in
|
.disabled(true)
|
||||||
Button {
|
|
||||||
selectedModuleId = module.id.uuidString
|
Divider()
|
||||||
} label: {
|
|
||||||
HStack {
|
Button {
|
||||||
KFImage(URL(string: module.metadata.iconUrl))
|
showModuleSettings = true
|
||||||
.resizable()
|
} label: {
|
||||||
.aspectRatio(contentMode: .fit)
|
Label("Add Modules", systemImage: "plus.app")
|
||||||
.frame(width: 20, height: 20)
|
}
|
||||||
.cornerRadius(4)
|
} else {
|
||||||
Text(module.metadata.sourceName)
|
ForEach(getModuleLanguageGroups(), id: \.self) { language in
|
||||||
if module.id.uuidString == selectedModuleId {
|
Menu(language) {
|
||||||
Image(systemName: "checkmark")
|
ForEach(getModulesForLanguage(language), id: \.id) { module in
|
||||||
.foregroundColor(.accentColor)
|
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 isLandscape: Bool = UIDevice.current.orientation.isLandscape
|
||||||
@State private var isModuleSelectorPresented = false
|
@State private var isModuleSelectorPresented = false
|
||||||
@State private var showProfileSettings = false
|
@State private var showProfileSettings = false
|
||||||
|
@State private var showModuleSettings = false
|
||||||
|
|
||||||
private var selectedModule: ScrapingModule? {
|
private var selectedModule: ScrapingModule? {
|
||||||
guard let id = selectedModuleId else { return nil }
|
guard let id = selectedModuleId else { return nil }
|
||||||
|
|
@ -168,6 +169,13 @@ struct SearchView: View {
|
||||||
label: { EmptyView() }
|
label: { EmptyView() }
|
||||||
)
|
)
|
||||||
.hidden()
|
.hidden()
|
||||||
|
|
||||||
|
NavigationLink(
|
||||||
|
destination: SettingsViewModule(),
|
||||||
|
isActive: $showModuleSettings,
|
||||||
|
label: { EmptyView() }
|
||||||
|
)
|
||||||
|
.hidden()
|
||||||
}
|
}
|
||||||
.navigationTitle("Search")
|
.navigationTitle("Search")
|
||||||
.navigationBarTitleDisplayMode(.large)
|
.navigationBarTitleDisplayMode(.large)
|
||||||
|
|
@ -207,22 +215,35 @@ struct SearchView: View {
|
||||||
}
|
}
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||||||
Menu {
|
Menu {
|
||||||
ForEach(getModuleLanguageGroups(), id: \.self) { language in
|
if getModuleLanguageGroups().isEmpty {
|
||||||
Menu(language) {
|
Button("No modules available") { }
|
||||||
ForEach(getModulesForLanguage(language), id: \.id) { module in
|
.disabled(true)
|
||||||
Button {
|
|
||||||
selectedModuleId = module.id.uuidString
|
Divider()
|
||||||
} label: {
|
|
||||||
HStack {
|
Button {
|
||||||
KFImage(URL(string: module.metadata.iconUrl))
|
showModuleSettings = true
|
||||||
.resizable()
|
} label: {
|
||||||
.aspectRatio(contentMode: .fit)
|
Label("Add Modules", systemImage: "plus.app")
|
||||||
.frame(width: 20, height: 20)
|
}
|
||||||
.cornerRadius(4)
|
} else {
|
||||||
Text(module.metadata.sourceName)
|
ForEach(getModuleLanguageGroups(), id: \.self) { language in
|
||||||
if module.id.uuidString == selectedModuleId {
|
Menu(language) {
|
||||||
Image(systemName: "checkmark")
|
ForEach(getModulesForLanguage(language), id: \.id) { module in
|
||||||
.foregroundColor(.accentColor)
|
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()
|
.padding()
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
.modifier(HideToolbarModifier())
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setAppIcon(named iconName: String) {
|
private func setAppIcon(named iconName: String) {
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ struct SettingsViewData: View {
|
||||||
}
|
}
|
||||||
.navigationTitle("App Data")
|
.navigationTitle("App Data")
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
.navigationViewStyle(StackNavigationViewStyle())
|
||||||
|
.modifier(HideToolbarModifier())
|
||||||
}
|
}
|
||||||
|
|
||||||
func eraseAppData() {
|
func eraseAppData() {
|
||||||
|
|
|
||||||
|
|
@ -153,5 +153,6 @@ struct SettingsViewGeneral: View {
|
||||||
SettingsViewAlternateAppIconPicker(isPresented: $showAppIconPicker)
|
SettingsViewAlternateAppIconPicker(isPresented: $showAppIconPicker)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.modifier(HideToolbarModifier())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,5 +54,6 @@ struct SettingsViewLogger: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.modifier(HideToolbarModifier())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,5 +83,6 @@ struct SettingsViewLoggerFilter: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Log Filters")
|
.navigationTitle("Log Filters")
|
||||||
|
.modifier(HideToolbarModifier())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,7 @@ struct SettingsViewModule: View {
|
||||||
} message: {
|
} message: {
|
||||||
Text(errorMessage ?? "Unknown error")
|
Text(errorMessage ?? "Unknown error")
|
||||||
}
|
}
|
||||||
|
.modifier(HideToolbarModifier())
|
||||||
}
|
}
|
||||||
|
|
||||||
func showAddModuleAlert() {
|
func showAddModuleAlert() {
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@ struct SettingsViewPlayer: View {
|
||||||
SubtitleSettingsSection()
|
SubtitleSettingsSection()
|
||||||
}
|
}
|
||||||
.navigationTitle("Player")
|
.navigationTitle("Player")
|
||||||
|
.modifier(HideToolbarModifier())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ struct SettingsViewProfile: View {
|
||||||
@EnvironmentObject var profileStore: ProfileStore
|
@EnvironmentObject var profileStore: ProfileStore
|
||||||
|
|
||||||
@State private var showDeleteAlert = false
|
@State private var showDeleteAlert = false
|
||||||
|
@State private var profileIDToRemove: UUID?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
|
|
@ -53,6 +54,16 @@ struct SettingsViewProfile: View {
|
||||||
isSelected: profile.id == profileStore.currentProfile.id
|
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 {
|
if profileStore.profiles.count > 1 {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
|
profileIDToRemove = profileStore.currentProfile.id
|
||||||
showDeleteAlert = true
|
showDeleteAlert = true
|
||||||
}) {
|
}) {
|
||||||
Text("Delete Selected Profile")
|
Text("Delete Selected Profile")
|
||||||
|
|
@ -107,7 +119,7 @@ struct SettingsViewProfile: View {
|
||||||
title: Text("Delete Profile"),
|
title: Text("Delete Profile"),
|
||||||
message: Text("Are you sure you want to delete this profile? This action cannot be undone."),
|
message: Text("Are you sure you want to delete this profile? This action cannot be undone."),
|
||||||
primaryButton: .destructive(Text("Delete")) {
|
primaryButton: .destructive(Text("Delete")) {
|
||||||
profileStore.deleteCurrentProfile()
|
profileStore.deleteProfile(removalID: profileIDToRemove)
|
||||||
},
|
},
|
||||||
secondaryButton: .cancel()
|
secondaryButton: .cancel()
|
||||||
)
|
)
|
||||||
|
|
@ -133,5 +145,6 @@ struct SettingsViewProfile: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.modifier(HideToolbarModifier())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,7 @@ struct SettingsViewTrackers: View {
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
removeNotificationObservers()
|
removeNotificationObservers()
|
||||||
}
|
}
|
||||||
|
.modifier(HideToolbarModifier())
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeNotificationObservers() {
|
func removeNotificationObservers() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue