mirror of
https://github.com/cranci1/Sora.git
synced 2026-03-11 17:45:37 +00:00
Fix simulator crash & add conditional support for iOS 26+ native TabView (#209)
* add ability to use native tabbar on ios 26 or later * add tabview lcalization * fix library / home tab label and localization * add missing localization & german translation
This commit is contained in:
parent
82eec0688f
commit
78df4df73b
7 changed files with 121 additions and 87 deletions
|
|
@ -16,48 +16,57 @@ struct ContentView_Previews: PreviewProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
|
@AppStorage("useNativeTabBar") private var useNativeTabBar: Bool = false
|
||||||
@StateObject private var tabBarController = TabBarController()
|
@StateObject private var tabBarController = TabBarController()
|
||||||
@State var selectedTab: Int = 0
|
@State var selectedTab: Int = 0
|
||||||
@State var lastTab: Int = 0
|
@State var lastTab: Int = 0
|
||||||
@State private var searchQuery: String = ""
|
@State private var searchQuery: String = ""
|
||||||
|
|
||||||
let tabs: [TabItem] = [
|
let tabs: [TabItem] = [
|
||||||
TabItem(icon: "square.stack", title: ""),
|
TabItem(icon: "square.stack", title: NSLocalizedString("LibraryTab", comment: "")),
|
||||||
TabItem(icon: "arrow.down.circle", title: ""),
|
TabItem(icon: "arrow.down.circle", title: NSLocalizedString("DownloadsTab", comment: "")),
|
||||||
TabItem(icon: "gearshape", title: ""),
|
TabItem(icon: "gearshape", title: NSLocalizedString("SettingsTab", comment: "")),
|
||||||
TabItem(icon: "magnifyingglass", title: "")
|
TabItem(icon: "magnifyingglass", title: NSLocalizedString("SearchTab", comment: ""))
|
||||||
]
|
]
|
||||||
|
|
||||||
var body: some View {
|
private func tabView(for index: Int) -> some View {
|
||||||
ZStack(alignment: .bottom) {
|
switch index {
|
||||||
switch selectedTab {
|
case 1: return AnyView(DownloadView())
|
||||||
case 0:
|
case 2: return AnyView(SettingsView())
|
||||||
LibraryView()
|
case 3: return AnyView(SearchView(searchQuery: $searchQuery))
|
||||||
.environmentObject(tabBarController)
|
default: return AnyView(LibraryView())
|
||||||
case 1:
|
}
|
||||||
DownloadView()
|
}
|
||||||
.environmentObject(tabBarController)
|
|
||||||
case 2:
|
var body: some View {
|
||||||
SettingsView()
|
if #available(iOS 26, *), useNativeTabBar == true {
|
||||||
.environmentObject(tabBarController)
|
TabView {
|
||||||
case 3:
|
ForEach(Array(tabs.enumerated()), id: \.offset) { index, item in
|
||||||
SearchView(searchQuery: $searchQuery)
|
Tab(item.title, systemImage: item.icon, role: index == 3 ? .search : nil) {
|
||||||
.environmentObject(tabBarController)
|
tabView(for: index)
|
||||||
default:
|
}
|
||||||
LibraryView()
|
}
|
||||||
.environmentObject(tabBarController)
|
}
|
||||||
}
|
.searchable(text: $searchQuery)
|
||||||
|
.environmentObject(tabBarController)
|
||||||
TabBar(
|
} else {
|
||||||
tabs: tabs,
|
ZStack(alignment: .bottom) {
|
||||||
selectedTab: $selectedTab,
|
Group {
|
||||||
lastTab: $lastTab,
|
tabView(for: selectedTab)
|
||||||
searchQuery: $searchQuery,
|
}
|
||||||
controller: tabBarController
|
.environmentObject(tabBarController)
|
||||||
)
|
|
||||||
|
TabBar(
|
||||||
|
tabs: tabs,
|
||||||
|
selectedTab: $selectedTab,
|
||||||
|
lastTab: $lastTab,
|
||||||
|
searchQuery: $searchQuery,
|
||||||
|
controller: tabBarController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
|
||||||
|
.ignoresSafeArea(.keyboard, edges: .bottom)
|
||||||
|
.padding(.bottom, -20)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
|
|
||||||
.ignoresSafeArea(.keyboard, edges: .bottom)
|
|
||||||
.padding(.bottom, -20)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@
|
||||||
"Copied to Clipboard" = "In die Zwischenablage kopiert";
|
"Copied to Clipboard" = "In die Zwischenablage kopiert";
|
||||||
"Copy to Clipboard" = "In die Zwischenablage kopieren";
|
"Copy to Clipboard" = "In die Zwischenablage kopieren";
|
||||||
"Copy URL" = "URL kopieren";
|
"Copy URL" = "URL kopieren";
|
||||||
|
"Collections" = "Sammlungen";
|
||||||
|
"No Collections" = "Keine Sammlungen vorhanden";
|
||||||
|
"Create a collection to organize your bookmarks" = "Erstelle eine Sammlung für mehr Organisation";
|
||||||
|
|
||||||
/* Episodes */
|
/* Episodes */
|
||||||
"%lld Episodes" = "%lld Folgen";
|
"%lld Episodes" = "%lld Folgen";
|
||||||
|
|
@ -84,7 +87,7 @@
|
||||||
"Download Episode" = "Folge herunterladen";
|
"Download Episode" = "Folge herunterladen";
|
||||||
"Download Summary" = "Download-Übersicht";
|
"Download Summary" = "Download-Übersicht";
|
||||||
"Download This Episode" = "Diese Folge herunterladen";
|
"Download This Episode" = "Diese Folge herunterladen";
|
||||||
"Downloaded" = "Heruntergeladen";
|
"Downloaded" = "Geladen";
|
||||||
"Downloaded Shows" = "Heruntergeladene Serien";
|
"Downloaded Shows" = "Heruntergeladene Serien";
|
||||||
"Downloading" = "Lädt herunter";
|
"Downloading" = "Lädt herunter";
|
||||||
"Downloads" = "Downloads";
|
"Downloads" = "Downloads";
|
||||||
|
|
@ -115,6 +118,7 @@
|
||||||
"General events and activities." = "Allgemeine Aktivitäten.";
|
"General events and activities." = "Allgemeine Aktivitäten.";
|
||||||
"General Preferences" = "Allgemeine Einstellungen";
|
"General Preferences" = "Allgemeine Einstellungen";
|
||||||
"Hide Splash Screen" = "Startbildschirm ausblenden";
|
"Hide Splash Screen" = "Startbildschirm ausblenden";
|
||||||
|
"Use Native Tab Bar" = "System Bar verwenden";
|
||||||
"HLS video downloading." = "HLS Video-Downloads.";
|
"HLS video downloading." = "HLS Video-Downloads.";
|
||||||
"Hold Speed" = "Geschwindigkeit halten";
|
"Hold Speed" = "Geschwindigkeit halten";
|
||||||
|
|
||||||
|
|
@ -383,6 +387,12 @@
|
||||||
"Library cleared successfully" = "Bibliothek erfolgreich geleert";
|
"Library cleared successfully" = "Bibliothek erfolgreich geleert";
|
||||||
"All downloads deleted successfully" = "Alle Downloads erfolgreich gelöscht";
|
"All downloads deleted successfully" = "Alle Downloads erfolgreich gelöscht";
|
||||||
|
|
||||||
|
/* TabView */
|
||||||
|
"LibraryTab" = "Bibliothek";
|
||||||
|
"DownloadsTab" = "Downloads";
|
||||||
|
"SettingsTab" = "Einstellungen";
|
||||||
|
"SearchTab" = "Suchen";
|
||||||
|
|
||||||
/* New additions */
|
/* New additions */
|
||||||
"Recent searches" = "Letzte Suchanfragen";
|
"Recent searches" = "Letzte Suchanfragen";
|
||||||
"me frfr" = "Ich, ohne Witz";
|
"me frfr" = "Ich, ohne Witz";
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@
|
||||||
"Copied to Clipboard" = "Copied to Clipboard";
|
"Copied to Clipboard" = "Copied to Clipboard";
|
||||||
"Copy to Clipboard" = "Copy to Clipboard";
|
"Copy to Clipboard" = "Copy to Clipboard";
|
||||||
"Copy URL" = "Copy URL";
|
"Copy URL" = "Copy URL";
|
||||||
|
"Collections" = "Collections";
|
||||||
|
"No Collections" = "No Collections";
|
||||||
|
"Create a collection to organize your bookmarks" = "Create a collection to organize your bookmarks";
|
||||||
|
|
||||||
/* Episodes */
|
/* Episodes */
|
||||||
"%lld Episodes" = "%lld Episodes";
|
"%lld Episodes" = "%lld Episodes";
|
||||||
|
|
@ -115,6 +118,7 @@
|
||||||
"General events and activities." = "General events and activities.";
|
"General events and activities." = "General events and activities.";
|
||||||
"General Preferences" = "General Preferences";
|
"General Preferences" = "General Preferences";
|
||||||
"Hide Splash Screen" = "Hide Splash Screen";
|
"Hide Splash Screen" = "Hide Splash Screen";
|
||||||
|
"Use Native Tab Bar" = "Use Native Tabs";
|
||||||
"HLS video downloading." = "HLS video downloading.";
|
"HLS video downloading." = "HLS video downloading.";
|
||||||
"Hold Speed" = "Hold Speed";
|
"Hold Speed" = "Hold Speed";
|
||||||
|
|
||||||
|
|
@ -384,6 +388,12 @@
|
||||||
"Library cleared successfully" = "Library cleared successfully";
|
"Library cleared successfully" = "Library cleared successfully";
|
||||||
"All downloads deleted successfully" = "All downloads deleted successfully";
|
"All downloads deleted successfully" = "All downloads deleted successfully";
|
||||||
|
|
||||||
|
/* TabView */
|
||||||
|
"LibraryTab" = "Library";
|
||||||
|
"DownloadsTab" = "Downloads";
|
||||||
|
"SettingsTab" = "Settings";
|
||||||
|
"SearchTab" = "Search";
|
||||||
|
|
||||||
/* New additions */
|
/* New additions */
|
||||||
"Recent searches" = "Recent searches";
|
"Recent searches" = "Recent searches";
|
||||||
"me frfr" = "me frfr";
|
"me frfr" = "me frfr";
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,17 @@ class DownloadManager: NSObject, ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func initializeDownloadSession() {
|
private func initializeDownloadSession() {
|
||||||
let configuration = URLSessionConfiguration.background(withIdentifier: "hls-downloader")
|
#if targetEnvironment(simulator)
|
||||||
assetDownloadURLSession = AVAssetDownloadURLSession(
|
Logger.shared.log("Download Sessions are not available on Simulator", type: "Error")
|
||||||
configuration: configuration,
|
#else
|
||||||
assetDownloadDelegate: self,
|
let configuration = URLSessionConfiguration.background(withIdentifier: "hls-downloader")
|
||||||
delegateQueue: .main
|
|
||||||
)
|
assetDownloadURLSession = AVAssetDownloadURLSession(
|
||||||
|
configuration: configuration,
|
||||||
|
assetDownloadDelegate: self,
|
||||||
|
delegateQueue: .main
|
||||||
|
)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func downloadAsset(from url: URL) {
|
func downloadAsset(from url: URL) {
|
||||||
|
|
|
||||||
|
|
@ -43,24 +43,29 @@ extension JSController {
|
||||||
private static var progressUpdateTimer: Timer?
|
private static var progressUpdateTimer: Timer?
|
||||||
|
|
||||||
func initializeDownloadSession() {
|
func initializeDownloadSession() {
|
||||||
// Create a unique identifier for the background session
|
#if targetEnvironment(simulator)
|
||||||
let sessionIdentifier = "hls-downloader-\(UUID().uuidString)"
|
Logger.shared.log("Download Sessions are not available on Simulator", type: "Error")
|
||||||
|
#else
|
||||||
|
// Create a unique identifier for the background session
|
||||||
|
let sessionIdentifier = "hls-downloader-\(UUID().uuidString)"
|
||||||
|
|
||||||
let configuration = URLSessionConfiguration.background(withIdentifier: sessionIdentifier)
|
let configuration = URLSessionConfiguration.background(withIdentifier: sessionIdentifier)
|
||||||
|
|
||||||
// Configure session
|
// Configure session
|
||||||
configuration.allowsCellularAccess = true
|
configuration.allowsCellularAccess = true
|
||||||
configuration.shouldUseExtendedBackgroundIdleMode = true
|
configuration.shouldUseExtendedBackgroundIdleMode = true
|
||||||
configuration.waitsForConnectivity = true
|
configuration.waitsForConnectivity = true
|
||||||
|
|
||||||
// Create session with configuration
|
// Create session with configuration
|
||||||
downloadURLSession = AVAssetDownloadURLSession(
|
downloadURLSession = AVAssetDownloadURLSession(
|
||||||
configuration: configuration,
|
configuration: configuration,
|
||||||
assetDownloadDelegate: self,
|
assetDownloadDelegate: self,
|
||||||
delegateQueue: .main
|
delegateQueue: .main
|
||||||
)
|
)
|
||||||
|
|
||||||
|
print("Download session initialized with ID: \(sessionIdentifier)")
|
||||||
|
#endif
|
||||||
|
|
||||||
print("Download session initialized with ID: \(sessionIdentifier)")
|
|
||||||
loadSavedAssets()
|
loadSavedAssets()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -227,33 +227,14 @@ struct TabBar: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
if tab.title.isEmpty {
|
Image(systemName: tab.icon + (selectedTab == index ? ".fill" : ""))
|
||||||
Image(systemName: tab.icon + (selectedTab == index ? ".fill" : ""))
|
.frame(width: 28, height: 28)
|
||||||
.frame(width: 28, height: 28)
|
.matchedGeometryEffect(id: tab.icon, in: animation)
|
||||||
.matchedGeometryEffect(id: tab.icon, in: animation)
|
.foregroundStyle(selectedTab == index ? .black : .gray)
|
||||||
.foregroundStyle(selectedTab == index ? .black : .gray)
|
|
||||||
.padding(.vertical, 8)
|
|
||||||
.padding(.horizontal, 10)
|
|
||||||
.frame(width: 70)
|
|
||||||
.opacity(selectedTab == index ? 1 : 0.5)
|
|
||||||
} else {
|
|
||||||
VStack {
|
|
||||||
Image(systemName: tab.icon + (selectedTab == index ? ".fill" : ""))
|
|
||||||
.frame(width: 36, height: 18)
|
|
||||||
.matchedGeometryEffect(id: tab.icon, in: animation)
|
|
||||||
.foregroundStyle(selectedTab == index ? .black : .gray)
|
|
||||||
|
|
||||||
Text(tab.title)
|
|
||||||
.font(.caption)
|
|
||||||
.frame(width: 60)
|
|
||||||
.lineLimit(1)
|
|
||||||
.truncationMode(.tail)
|
|
||||||
}
|
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
.padding(.horizontal, 10)
|
.padding(.horizontal, 10)
|
||||||
.frame(width: 80)
|
.frame(width: 70)
|
||||||
.opacity(selectedTab == index ? 1 : 0.5)
|
.opacity(selectedTab == index ? 1 : 0.5)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.background(
|
.background(
|
||||||
selectedTab == index ?
|
selectedTab == index ?
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,7 @@ struct SettingsViewGeneral: View {
|
||||||
@AppStorage("fetchEpisodeMetadata") private var fetchEpisodeMetadata: Bool = true
|
@AppStorage("fetchEpisodeMetadata") private var fetchEpisodeMetadata: Bool = true
|
||||||
@AppStorage("analyticsEnabled") private var analyticsEnabled: Bool = false
|
@AppStorage("analyticsEnabled") private var analyticsEnabled: Bool = false
|
||||||
@AppStorage("hideSplashScreen") private var hideSplashScreenEnable: Bool = false
|
@AppStorage("hideSplashScreen") private var hideSplashScreenEnable: Bool = false
|
||||||
|
@AppStorage("useNativeTabBar") private var useNativeTabBar: Bool = false
|
||||||
@AppStorage("metadataProvidersOrder") private var metadataProvidersOrderData: Data = {
|
@AppStorage("metadataProvidersOrder") private var metadataProvidersOrderData: Data = {
|
||||||
try! JSONEncoder().encode(["TMDB","AniList"])
|
try! JSONEncoder().encode(["TMDB","AniList"])
|
||||||
}()
|
}()
|
||||||
|
|
@ -172,6 +173,10 @@ struct SettingsViewGeneral: View {
|
||||||
@EnvironmentObject var settings: Settings
|
@EnvironmentObject var settings: Settings
|
||||||
@State private var showRestartAlert = false
|
@State private var showRestartAlert = false
|
||||||
|
|
||||||
|
private let isiOS26OrLater: Bool = {
|
||||||
|
if #available(iOS 26, *) { return true } else { return false }
|
||||||
|
}()
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView(showsIndicators: false) {
|
ScrollView(showsIndicators: false) {
|
||||||
VStack(spacing: 24) {
|
VStack(spacing: 24) {
|
||||||
|
|
@ -194,8 +199,17 @@ struct SettingsViewGeneral: View {
|
||||||
icon: "wand.and.rays.inverse",
|
icon: "wand.and.rays.inverse",
|
||||||
title: NSLocalizedString("Hide Splash Screen", comment: ""),
|
title: NSLocalizedString("Hide Splash Screen", comment: ""),
|
||||||
isOn: $hideSplashScreenEnable,
|
isOn: $hideSplashScreenEnable,
|
||||||
showDivider: false
|
showDivider: isiOS26OrLater
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if isiOS26OrLater {
|
||||||
|
SettingsToggleRow(
|
||||||
|
icon: "inset.filled.bottomthird.rectangle",
|
||||||
|
title: NSLocalizedString("Use Native Tab Bar", comment: ""),
|
||||||
|
isOn: $useNativeTabBar,
|
||||||
|
showDivider: false
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsSection(title: NSLocalizedString("Language", comment: "")) {
|
SettingsSection(title: NSLocalizedString("Language", comment: "")) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue