added whats new maybe + UI fixes

This commit is contained in:
cranci1 2025-06-25 11:23:56 +02:00
parent 4b4bb0ad7b
commit 0eef3dd918
6 changed files with 174 additions and 42 deletions

View file

@ -4,7 +4,9 @@
// //
// Created by Francesco on 06/01/25. // Created by Francesco on 06/01/25.
// //
import SwiftUI import SwiftUI
import SlideOverCard
struct ContentView_Previews: PreviewProvider { struct ContentView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
@ -17,10 +19,12 @@ struct ContentView_Previews: PreviewProvider {
struct ContentView: View { struct ContentView: View {
@AppStorage("useNativeTabBar") private var useNativeTabBar: Bool = false @AppStorage("useNativeTabBar") private var useNativeTabBar: Bool = false
@AppStorage("lastVersionPrompt") private var lastVersionPrompt: String = ""
@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 = ""
@State private var showWhatsNew: Bool = false
let tabs: [TabItem] = [ let tabs: [TabItem] = [
TabItem(icon: "square.stack", title: NSLocalizedString("LibraryTab", comment: "")), TabItem(icon: "square.stack", title: NSLocalizedString("LibraryTab", comment: "")),
@ -28,46 +32,58 @@ struct ContentView: View {
TabItem(icon: "gearshape", title: NSLocalizedString("SettingsTab", comment: "")), TabItem(icon: "gearshape", title: NSLocalizedString("SettingsTab", comment: "")),
TabItem(icon: "magnifyingglass", title: NSLocalizedString("SearchTab", comment: "")) TabItem(icon: "magnifyingglass", title: NSLocalizedString("SearchTab", comment: ""))
] ]
private let currentVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "1.0.0"
private func tabView(for index: Int) -> some View { private func tabView(for index: Int) -> some View {
switch index { switch index {
case 1: return AnyView(DownloadView()) case 1: return AnyView(DownloadView())
case 2: return AnyView(SettingsView()) case 2: return AnyView(SettingsView())
case 3: return AnyView(SearchView(searchQuery: $searchQuery)) case 3: return AnyView(SearchView(searchQuery: $searchQuery))
default: return AnyView(LibraryView()) default: return AnyView(LibraryView())
} }
} }
var body: some View { var body: some View {
if #available(iOS 26, *), useNativeTabBar == true { Group {
TabView { if #available(iOS 26, *), useNativeTabBar == true {
ForEach(Array(tabs.enumerated()), id: \.offset) { index, item in TabView {
tabView(for: index) ForEach(Array(tabs.enumerated()), id: \.offset) { index, item in
.tabItem { tabView(for: index)
Label(item.title, systemImage: item.icon) .tabItem {
} Label(item.title, systemImage: item.icon)
} }
} }
.searchable(text: $searchQuery)
.environmentObject(tabBarController)
} else {
ZStack(alignment: .bottom) {
Group {
tabView(for: selectedTab)
} }
.searchable(text: $searchQuery)
.environmentObject(tabBarController) .environmentObject(tabBarController)
} else {
TabBar( ZStack(alignment: .bottom) {
tabs: tabs, Group {
selectedTab: $selectedTab, tabView(for: selectedTab)
lastTab: $lastTab, }
searchQuery: $searchQuery, .environmentObject(tabBarController)
controller: 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) .onAppear {
.padding(.bottom, -20) if lastVersionPrompt != currentVersion {
showWhatsNew = true
}
}
.slideOverCard(isPresented: $showWhatsNew, options: SOCOptions()) {
WhatsNewView(isPresented: $showWhatsNew)
} }
} }
} }

View file

@ -141,8 +141,8 @@ struct DownloadView: View {
private var emptyActiveDownloadsView: some View { private var emptyActiveDownloadsView: some View {
VStack(spacing: 20) { VStack(spacing: 20) {
Image(systemName: "arrow.down.circle") Image(systemName: "arrow.down.circle")
.font(.system(size: 64, weight: .ultraLight)) .font(.largeTitle)
.foregroundStyle(.tertiary) .foregroundStyle(.secondary)
VStack(spacing: 8) { VStack(spacing: 8) {
Text(NSLocalizedString("No Active Downloads", comment: "")) Text(NSLocalizedString("No Active Downloads", comment: ""))
@ -162,9 +162,9 @@ struct DownloadView: View {
private var emptyDownloadsView: some View { private var emptyDownloadsView: some View {
VStack(spacing: 20) { VStack(spacing: 20) {
Image(systemName: "arrow.down.circle") Image(systemName: "arrow.down.circle")
.font(.system(size: 64, weight: .ultraLight)) .font(.largeTitle)
.foregroundStyle(.tertiary) .foregroundStyle(.secondary)
VStack(spacing: 8) { VStack(spacing: 8) {
Text(NSLocalizedString("No Downloads", comment: "")) Text(NSLocalizedString("No Downloads", comment: ""))

View file

@ -196,6 +196,7 @@ struct MediaInfoView: View {
UserDefaults.standard.set(newValue.lowerBound, forKey: selectedChapterRangeKey) UserDefaults.standard.set(newValue.lowerBound, forKey: selectedChapterRangeKey)
} }
.onDisappear { .onDisappear {
tabBarController.showTabBar()
currentFetchTask?.cancel() currentFetchTask?.cancel()
activeFetchID = nil activeFetchID = nil
} }

View file

@ -0,0 +1,86 @@
//
// WhatsNewView.swift
// Sora
//
// Created by Francesco on 25/06/25.
//
import SwiftUI
import SlideOverCard
struct WhatsNewView: View {
@AppStorage("lastVersionPrompt") private var lastVersionPrompt: String = ""
@Binding var isPresented: Bool
private let currentVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "1.0.0"
private let whatsNewItems = [
WhatsNewItem(title: "Brand new UI", description: "Enjoy this brand new look of Sora", icon: "sparkles"),
WhatsNewItem(title: "TMDB Metadata", description: "Various UI improvements and animations across the app", icon: "bolt.fill"),
WhatsNewItem(title: "Download Support", description: "For both mp4 and HLS with Multi server support", icon: "tray.and.arrow.down.fill")
]
var body: some View {
VStack(alignment: .center, spacing: 25) {
HStack {
Text("What's New in Sora")
.font(.system(size: 28, weight: .bold))
Text("Version \(currentVersion)")
.foregroundColor(.gray)
}
ScrollView {
VStack(spacing: 20) {
ForEach(whatsNewItems) { item in
WhatsNewItemView(item: item)
}
}
.padding(.horizontal)
}
VStack(spacing: 0) {
Button("Continue", action: {
lastVersionPrompt = currentVersion
isPresented = false
}).buttonStyle(SOCActionButton())
Button("Release Notes", action: {
if let url = URL(string: "https://github.com/cranci1/Sora/releases/tag/1.0.0") {
UIApplication.shared.open(url)
}
}).buttonStyle(SOCEmptyButton())
}
}
.frame(height: 480)
}
}
struct WhatsNewItem: Identifiable {
let id = UUID()
let title: String
let description: String
let icon: String
}
struct WhatsNewItemView: View {
let item: WhatsNewItem
var body: some View {
HStack(spacing: 16) {
Image(systemName: item.icon)
.font(.system(size: 24))
.foregroundColor(.blue)
.frame(width: 32)
VStack(alignment: .leading, spacing: 4) {
Text(item.title)
.font(.headline)
Text(item.description)
.font(.subheadline)
.foregroundColor(.gray)
.fixedSize(horizontal: false, vertical: true)
}
Spacer()
}
.padding(.vertical, 8)
}
}

View file

@ -78,6 +78,8 @@
138AA1B82D2D66FD0021F9DF /* EpisodeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138AA1B62D2D66FD0021F9DF /* EpisodeCell.swift */; }; 138AA1B82D2D66FD0021F9DF /* EpisodeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138AA1B62D2D66FD0021F9DF /* EpisodeCell.swift */; };
138AA1B92D2D66FD0021F9DF /* CircularProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138AA1B72D2D66FD0021F9DF /* CircularProgressBar.swift */; }; 138AA1B92D2D66FD0021F9DF /* CircularProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138AA1B72D2D66FD0021F9DF /* CircularProgressBar.swift */; };
138B66A02E0BEA52009BE8D9 /* WebAuthenticationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138B669F2E0BEA52009BE8D9 /* WebAuthenticationManager.swift */; }; 138B66A02E0BEA52009BE8D9 /* WebAuthenticationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138B669F2E0BEA52009BE8D9 /* WebAuthenticationManager.swift */; };
138B66A82E0BF22E009BE8D9 /* SlideOverCard in Frameworks */ = {isa = PBXBuildFile; productRef = 138B66A72E0BF22E009BE8D9 /* SlideOverCard */; };
138B66AC2E0BF560009BE8D9 /* WhatsNewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138B66AB2E0BF560009BE8D9 /* WhatsNewView.swift */; };
138FE1D02DECA00D00936D81 /* TMDB-FetchID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138FE1CF2DECA00D00936D81 /* TMDB-FetchID.swift */; }; 138FE1D02DECA00D00936D81 /* TMDB-FetchID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138FE1CF2DECA00D00936D81 /* TMDB-FetchID.swift */; };
1398FB3F2DE4E161004D3F5F /* SettingsViewAbout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1398FB3E2DE4E161004D3F5F /* SettingsViewAbout.swift */; }; 1398FB3F2DE4E161004D3F5F /* SettingsViewAbout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1398FB3E2DE4E161004D3F5F /* SettingsViewAbout.swift */; };
139935662D468C450065CEFF /* ModuleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 139935652D468C450065CEFF /* ModuleManager.swift */; }; 139935662D468C450065CEFF /* ModuleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 139935652D468C450065CEFF /* ModuleManager.swift */; };
@ -190,6 +192,7 @@
138AA1B62D2D66FD0021F9DF /* EpisodeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpisodeCell.swift; sourceTree = "<group>"; }; 138AA1B62D2D66FD0021F9DF /* EpisodeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpisodeCell.swift; sourceTree = "<group>"; };
138AA1B72D2D66FD0021F9DF /* CircularProgressBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircularProgressBar.swift; sourceTree = "<group>"; }; 138AA1B72D2D66FD0021F9DF /* CircularProgressBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircularProgressBar.swift; sourceTree = "<group>"; };
138B669F2E0BEA52009BE8D9 /* WebAuthenticationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebAuthenticationManager.swift; sourceTree = "<group>"; }; 138B669F2E0BEA52009BE8D9 /* WebAuthenticationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebAuthenticationManager.swift; sourceTree = "<group>"; };
138B66AB2E0BF560009BE8D9 /* WhatsNewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WhatsNewView.swift; path = Sora/Views/WhatsNewView.swift; sourceTree = SOURCE_ROOT; };
138FE1CF2DECA00D00936D81 /* TMDB-FetchID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TMDB-FetchID.swift"; sourceTree = "<group>"; }; 138FE1CF2DECA00D00936D81 /* TMDB-FetchID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TMDB-FetchID.swift"; sourceTree = "<group>"; };
1398FB3E2DE4E161004D3F5F /* SettingsViewAbout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewAbout.swift; sourceTree = "<group>"; }; 1398FB3E2DE4E161004D3F5F /* SettingsViewAbout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewAbout.swift; sourceTree = "<group>"; };
139935652D468C450065CEFF /* ModuleManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleManager.swift; sourceTree = "<group>"; }; 139935652D468C450065CEFF /* ModuleManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleManager.swift; sourceTree = "<group>"; };
@ -242,6 +245,7 @@
13637B902DE0ECD200BDA2FC /* Drops in Frameworks */, 13637B902DE0ECD200BDA2FC /* Drops in Frameworks */,
13530BDF2E0002790048B7DE /* SoraCore in Frameworks */, 13530BDF2E0002790048B7DE /* SoraCore in Frameworks */,
13637B932DE0ECDB00BDA2FC /* MarqueeLabel in Frameworks */, 13637B932DE0ECDB00BDA2FC /* MarqueeLabel in Frameworks */,
138B66A82E0BF22E009BE8D9 /* SlideOverCard in Frameworks */,
13367ECE2DF70698009CB33F /* NukeUI in Frameworks */, 13367ECE2DF70698009CB33F /* NukeUI in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -519,8 +523,8 @@
13DC0C412D2EC9BA00D0F966 /* Info.plist */, 13DC0C412D2EC9BA00D0F966 /* Info.plist */,
133D7C6D2D2BE2500075467E /* SoraApp.swift */, 133D7C6D2D2BE2500075467E /* SoraApp.swift */,
133D7C712D2BE2520075467E /* Assets.xcassets */, 133D7C712D2BE2520075467E /* Assets.xcassets */,
133D7C6F2D2BE2500075467E /* ContentView.swift */,
130C6BF82D53A4C200DC1432 /* Sora.entitlements */, 130C6BF82D53A4C200DC1432 /* Sora.entitlements */,
133D7C6F2D2BE2500075467E /* ContentView.swift */,
133D7C732D2BE2520075467E /* Preview Content */, 133D7C732D2BE2520075467E /* Preview Content */,
); );
path = Sora; path = Sora;
@ -537,9 +541,10 @@
133D7C7B2D2BE2630075467E /* Views */ = { 133D7C7B2D2BE2630075467E /* Views */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
04536F732E04BA5600A11248 /* ReaderView */,
04EAC3992DF9E0DB00BBD483 /* SplashScreenView.swift */, 04EAC3992DF9E0DB00BBD483 /* SplashScreenView.swift */,
138B66AB2E0BF560009BE8D9 /* WhatsNewView.swift */,
72443C7C2DC8036500A61321 /* DownloadView.swift */, 72443C7C2DC8036500A61321 /* DownloadView.swift */,
04536F732E04BA5600A11248 /* ReaderView */,
0402DA122DE7B5EC003BB42C /* SearchView */, 0402DA122DE7B5EC003BB42C /* SearchView */,
133D7C7F2D2BE2630075467E /* MediaInfoView */, 133D7C7F2D2BE2630075467E /* MediaInfoView */,
1399FAD22D3AB34F00E97C31 /* SettingsView */, 1399FAD22D3AB34F00E97C31 /* SettingsView */,
@ -551,8 +556,8 @@
133D7C7F2D2BE2630075467E /* MediaInfoView */ = { 133D7C7F2D2BE2630075467E /* MediaInfoView */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
04536F762E04BA6900A11248 /* ChapterCell */,
1E0435F02DFCB86800FF6808 /* CustomMatching */, 1E0435F02DFCB86800FF6808 /* CustomMatching */,
04536F762E04BA6900A11248 /* ChapterCell */,
138AA1B52D2D66EC0021F9DF /* EpisodeCell */, 138AA1B52D2D66EC0021F9DF /* EpisodeCell */,
133D7C802D2BE2630075467E /* MediaInfoView.swift */, 133D7C802D2BE2630075467E /* MediaInfoView.swift */,
); );
@ -859,6 +864,7 @@
13367ECB2DF70698009CB33F /* Nuke */, 13367ECB2DF70698009CB33F /* Nuke */,
13367ECD2DF70698009CB33F /* NukeUI */, 13367ECD2DF70698009CB33F /* NukeUI */,
13530BDE2E0002790048B7DE /* SoraCore */, 13530BDE2E0002790048B7DE /* SoraCore */,
138B66A72E0BF22E009BE8D9 /* SlideOverCard */,
); );
productName = Sora; productName = Sora;
productReference = 133D7C6A2D2BE2500075467E /* Sulfur.app */; productReference = 133D7C6A2D2BE2500075467E /* Sulfur.app */;
@ -907,6 +913,7 @@
13637B912DE0ECDB00BDA2FC /* XCRemoteSwiftPackageReference "MarqueeLabel" */, 13637B912DE0ECDB00BDA2FC /* XCRemoteSwiftPackageReference "MarqueeLabel" */,
13367ECA2DF70698009CB33F /* XCRemoteSwiftPackageReference "Nuke" */, 13367ECA2DF70698009CB33F /* XCRemoteSwiftPackageReference "Nuke" */,
13530BDD2E0002790048B7DE /* XCRemoteSwiftPackageReference "SoraCore" */, 13530BDD2E0002790048B7DE /* XCRemoteSwiftPackageReference "SoraCore" */,
138B66A62E0BF22E009BE8D9 /* XCRemoteSwiftPackageReference "SlideOverCard" */,
); );
productRefGroup = 133D7C6B2D2BE2500075467E /* Products */; productRefGroup = 133D7C6B2D2BE2500075467E /* Products */;
projectDirPath = ""; projectDirPath = "";
@ -991,6 +998,7 @@
04EAC39A2DF9E0DB00BBD483 /* SplashScreenView.swift in Sources */, 04EAC39A2DF9E0DB00BBD483 /* SplashScreenView.swift in Sources */,
132AF1212D99951700A0140B /* JSController-Streams.swift in Sources */, 132AF1212D99951700A0140B /* JSController-Streams.swift in Sources */,
131845F92D47C62D00CA7A54 /* SettingsViewGeneral.swift in Sources */, 131845F92D47C62D00CA7A54 /* SettingsViewGeneral.swift in Sources */,
138B66AC2E0BF560009BE8D9 /* WhatsNewView.swift in Sources */,
04CD76DB2DE20F2200733536 /* AllWatching.swift in Sources */, 04CD76DB2DE20F2200733536 /* AllWatching.swift in Sources */,
13103E8E2D58E04A000F0673 /* SkeletonCell.swift in Sources */, 13103E8E2D58E04A000F0673 /* SkeletonCell.swift in Sources */,
13D842552D45267500EBBFA6 /* DropManager.swift in Sources */, 13D842552D45267500EBBFA6 /* DropManager.swift in Sources */,
@ -1435,6 +1443,14 @@
kind = branch; kind = branch;
}; };
}; };
138B66A62E0BF22E009BE8D9 /* XCRemoteSwiftPackageReference "SlideOverCard" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/akwasiio/SlideOverCard";
requirement = {
branch = main;
kind = branch;
};
};
/* End XCRemoteSwiftPackageReference section */ /* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */ /* Begin XCSwiftPackageProductDependency section */
@ -1463,6 +1479,11 @@
package = 13637B912DE0ECDB00BDA2FC /* XCRemoteSwiftPackageReference "MarqueeLabel" */; package = 13637B912DE0ECDB00BDA2FC /* XCRemoteSwiftPackageReference "MarqueeLabel" */;
productName = MarqueeLabel; productName = MarqueeLabel;
}; };
138B66A72E0BF22E009BE8D9 /* SlideOverCard */ = {
isa = XCSwiftPackageProductDependency;
package = 138B66A62E0BF22E009BE8D9 /* XCRemoteSwiftPackageReference "SlideOverCard" */;
productName = SlideOverCard;
};
/* End XCSwiftPackageProductDependency section */ /* End XCSwiftPackageProductDependency section */
}; };
rootObject = 133D7C622D2BE2500075467E /* Project object */; rootObject = 133D7C622D2BE2500075467E /* Project object */;

View file

@ -1,5 +1,4 @@
{ {
"originHash" : "07beed18a1a0b5e52eea618e423e9ca1c37c24c4d3d4ec31d68c1664db0f0596",
"pins" : [ "pins" : [
{ {
"identity" : "drops", "identity" : "drops",
@ -28,15 +27,24 @@
"revision" : "c7ba4833b1b38f09e9708858aeaf91babc69f65c" "revision" : "c7ba4833b1b38f09e9708858aeaf91babc69f65c"
} }
}, },
{
"identity" : "slideovercard",
"kind" : "remoteSourceControl",
"location" : "https://github.com/akwasiio/SlideOverCard",
"state" : {
"branch" : "main",
"revision" : "5773fcbbe583ce09a2e0314d6e72494e945c4ab6"
}
},
{ {
"identity" : "soracore", "identity" : "soracore",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/cranci1/SoraCore", "location" : "https://github.com/cranci1/SoraCore",
"state" : { "state" : {
"branch" : "main", "branch" : "main",
"revision" : "957207dded41b1db9fbfdabde81ffb2e72e71b31" "revision" : "543fe1c8c1d421201aeb10e7d2438a91c90c8ac5"
} }
} }
], ],
"version" : 3 "version" : 2
} }