mirror of
https://github.com/cranci1/Sora.git
synced 2026-04-19 23:52:09 +00:00
Who thought this view existed? 😭 (#142)
* Minor changes * more minor changes * MORE MINOR CHANGES * Fixed one singular bug * Update SettingsViewGeneral.swift * fuck conflicts * fuck you and your credits * buh buh * Update SettingsViewAbout.swift * What's that? What's a properly working code without unnecessary issues? * Type shit maybe? * Create ios.yml * smol * type shi shi * end * Fixed system theme bug * (hopefully) fixed sliding items in search + recent searches spam * Fixed open keyboard in media view * Fixed searchviewdata + fixed toggle color being too light * fixed episode slider sensitivity * new recent searches, not fully done but wtv WHO CARES 💯 * Add module screen fix * Delete .github/workflows/ios.yml * UI modifications * Scroll to close keyboard * Text change * Downloadview transition * Search cards text fixed * Reduced header spacing + moved down a to match library * Text change * Small tab bar tweak for search view * added gradient to player buttons * reduced summary size * IBH special 💯 * Fixed different height text? * Removed seperator * Some more fixes start watching button * MediaInfoView modifications * Fixed settingsviewdata * Changed x mark and fixed episode swipe for 4 items * Delete Package.resolved * fix * fix * Update Package.resolved * Removed version lalel type shi * Who thought this view existed? 😭
This commit is contained in:
parent
400079f0da
commit
8b2ee00c90
2 changed files with 295 additions and 75 deletions
|
|
@ -8,6 +8,147 @@
|
|||
import SwiftUI
|
||||
import Drops
|
||||
|
||||
fileprivate struct SettingsSection<Content: View>: View {
|
||||
let title: String
|
||||
let footer: String?
|
||||
let content: Content
|
||||
|
||||
init(title: String, footer: String? = nil, @ViewBuilder content: () -> Content) {
|
||||
self.title = title
|
||||
self.footer = footer
|
||||
self.content = content()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(title.uppercased())
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.gray)
|
||||
.padding(.horizontal, 20)
|
||||
|
||||
VStack(spacing: 0) {
|
||||
content
|
||||
}
|
||||
.background(.ultraThinMaterial)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.strokeBorder(
|
||||
LinearGradient(
|
||||
gradient: Gradient(stops: [
|
||||
.init(color: Color.accentColor.opacity(0.3), location: 0),
|
||||
.init(color: Color.accentColor.opacity(0), location: 1)
|
||||
]),
|
||||
startPoint: .top,
|
||||
endPoint: .bottom
|
||||
),
|
||||
lineWidth: 0.5
|
||||
)
|
||||
)
|
||||
.padding(.horizontal, 20)
|
||||
|
||||
if let footer = footer {
|
||||
Text(footer)
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.gray)
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.top, 4)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct SettingsToggleRow: View {
|
||||
let icon: String
|
||||
let title: String
|
||||
@Binding var isOn: Bool
|
||||
var showDivider: Bool = true
|
||||
|
||||
init(icon: String, title: String, isOn: Binding<Bool>, showDivider: Bool = true) {
|
||||
self.icon = icon
|
||||
self.title = title
|
||||
self._isOn = isOn
|
||||
self.showDivider = showDivider
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
HStack {
|
||||
Image(systemName: icon)
|
||||
.frame(width: 24, height: 24)
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Text(title)
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
|
||||
Toggle("", isOn: $isOn)
|
||||
.labelsHidden()
|
||||
.tint(.accentColor.opacity(0.7))
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
|
||||
if showDivider {
|
||||
Divider()
|
||||
.padding(.horizontal, 16)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct SettingsPickerRow<T: Hashable>: View {
|
||||
let icon: String
|
||||
let title: String
|
||||
let options: [T]
|
||||
let optionToString: (T) -> String
|
||||
@Binding var selection: T
|
||||
var showDivider: Bool = true
|
||||
|
||||
init(icon: String, title: String, options: [T], optionToString: @escaping (T) -> String, selection: Binding<T>, showDivider: Bool = true) {
|
||||
self.icon = icon
|
||||
self.title = title
|
||||
self.options = options
|
||||
self.optionToString = optionToString
|
||||
self._selection = selection
|
||||
self.showDivider = showDivider
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
HStack {
|
||||
Image(systemName: icon)
|
||||
.frame(width: 24, height: 24)
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Text(title)
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
|
||||
Menu {
|
||||
ForEach(options, id: \.self) { option in
|
||||
Button(action: { selection = option }) {
|
||||
Text(optionToString(option))
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Text(optionToString(selection))
|
||||
.foregroundStyle(.gray)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
|
||||
if showDivider {
|
||||
Divider()
|
||||
.padding(.horizontal, 16)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SettingsViewDownloads: View {
|
||||
@EnvironmentObject private var jsController: JSController
|
||||
@AppStorage(DownloadQualityPreference.userDefaultsKey)
|
||||
|
|
@ -20,94 +161,168 @@ struct SettingsViewDownloads: View {
|
|||
@State private var isCalculating: Bool = false
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section(header: Text("Download Settings"), footer: Text("Max concurrent downloads controls how many episodes can download simultaneously. Higher values may use more bandwidth and device resources.")) {
|
||||
Picker("Quality", selection: $downloadQuality) {
|
||||
ForEach(DownloadQualityPreference.allCases, id: \.rawValue) { option in
|
||||
Text(option.rawValue)
|
||||
.tag(option.rawValue)
|
||||
}
|
||||
}
|
||||
.onChange(of: downloadQuality) { newValue in
|
||||
print("Download quality preference changed to: \(newValue)")
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text("Max Concurrent Downloads")
|
||||
Spacer()
|
||||
Stepper("\(maxConcurrentDownloads)", value: $maxConcurrentDownloads, in: 1...10)
|
||||
.onChange(of: maxConcurrentDownloads) { newValue in
|
||||
jsController.updateMaxConcurrentDownloads(newValue)
|
||||
ScrollView {
|
||||
VStack(spacing: 24) {
|
||||
SettingsSection(
|
||||
title: "Download Settings",
|
||||
footer: "Max concurrent downloads controls how many episodes can download simultaneously. Higher values may use more bandwidth and device resources."
|
||||
) {
|
||||
SettingsPickerRow(
|
||||
icon: "4k.tv",
|
||||
title: "Quality",
|
||||
options: DownloadQualityPreference.allCases.map { $0.rawValue },
|
||||
optionToString: { $0 },
|
||||
selection: $downloadQuality
|
||||
)
|
||||
|
||||
VStack(spacing: 0) {
|
||||
HStack {
|
||||
Image(systemName: "arrow.down.circle")
|
||||
.frame(width: 24, height: 24)
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Text("Max Concurrent Downloads")
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
|
||||
Stepper("\(maxConcurrentDownloads)", value: $maxConcurrentDownloads, in: 1...10)
|
||||
.onChange(of: maxConcurrentDownloads) { newValue in
|
||||
jsController.updateMaxConcurrentDownloads(newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Toggle("Allow Cellular Downloads", isOn: $allowCellularDownloads)
|
||||
.tint(.accentColor)
|
||||
}
|
||||
|
||||
Section(header: Text("Quality Information")) {
|
||||
if let preferenceDescription = DownloadQualityPreference(rawValue: downloadQuality)?.description {
|
||||
Text(preferenceDescription)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
Section(header: Text("Storage Management")) {
|
||||
HStack {
|
||||
Text("Storage Used")
|
||||
Spacer()
|
||||
|
||||
if isCalculating {
|
||||
ProgressView()
|
||||
.scaleEffect(0.7)
|
||||
.padding(.trailing, 5)
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
|
||||
Divider()
|
||||
.padding(.horizontal, 16)
|
||||
}
|
||||
|
||||
Text(formatFileSize(totalStorageSize))
|
||||
.foregroundColor(.secondary)
|
||||
SettingsToggleRow(
|
||||
icon: "antenna.radiowaves.left.and.right",
|
||||
title: "Allow Cellular Downloads",
|
||||
isOn: $allowCellularDownloads,
|
||||
showDivider: false
|
||||
)
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text("Files Downloaded")
|
||||
Spacer()
|
||||
Text("\(existingDownloadCount) of \(jsController.savedAssets.count)")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
calculateTotalStorage()
|
||||
}) {
|
||||
HStack {
|
||||
Image(systemName: "arrow.clockwise")
|
||||
Text("Refresh Storage Info")
|
||||
SettingsSection(
|
||||
title: "Quality Information"
|
||||
) {
|
||||
if let preferenceDescription = DownloadQualityPreference(rawValue: downloadQuality)?.description {
|
||||
HStack {
|
||||
Text(preferenceDescription)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
Spacer()
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
}
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
showClearConfirmation = true
|
||||
}) {
|
||||
HStack {
|
||||
Image(systemName: "trash")
|
||||
.foregroundColor(.red)
|
||||
Text("Clear All Downloads")
|
||||
.foregroundColor(.red)
|
||||
SettingsSection(
|
||||
title: "Storage Management"
|
||||
) {
|
||||
VStack(spacing: 0) {
|
||||
HStack {
|
||||
Image(systemName: "externaldrive")
|
||||
.frame(width: 24, height: 24)
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Text("Storage Used")
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
|
||||
if isCalculating {
|
||||
ProgressView()
|
||||
.scaleEffect(0.7)
|
||||
.padding(.trailing, 5)
|
||||
}
|
||||
|
||||
Text(formatFileSize(totalStorageSize))
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
|
||||
Divider()
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
HStack {
|
||||
Image(systemName: "doc.text")
|
||||
.frame(width: 24, height: 24)
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Text("Files Downloaded")
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
|
||||
Text("\(existingDownloadCount) of \(jsController.savedAssets.count)")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
|
||||
Divider()
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
Button(action: {
|
||||
calculateTotalStorage()
|
||||
}) {
|
||||
HStack {
|
||||
Image(systemName: "arrow.clockwise")
|
||||
.frame(width: 24, height: 24)
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Text("Refresh Storage Info")
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
}
|
||||
|
||||
Divider()
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
Button(action: {
|
||||
showClearConfirmation = true
|
||||
}) {
|
||||
HStack {
|
||||
Image(systemName: "trash")
|
||||
.frame(width: 24, height: 24)
|
||||
.foregroundStyle(.red)
|
||||
|
||||
Text("Clear All Downloads")
|
||||
.foregroundStyle(.red)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert("Delete All Downloads", isPresented: $showClearConfirmation) {
|
||||
Button("Cancel", role: .cancel) { }
|
||||
Button("Delete All", role: .destructive) {
|
||||
clearAllDownloads(preservePersistentDownloads: false)
|
||||
}
|
||||
Button("Clear Library Only", role: .destructive) {
|
||||
clearAllDownloads(preservePersistentDownloads: true)
|
||||
}
|
||||
} message: {
|
||||
Text("Are you sure you want to delete all downloaded assets? You can choose to clear only the library while preserving the downloaded files for future use.")
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 20)
|
||||
}
|
||||
.navigationTitle("Downloads")
|
||||
.scrollViewBottomPadding()
|
||||
.alert("Delete All Downloads", isPresented: $showClearConfirmation) {
|
||||
Button("Cancel", role: .cancel) { }
|
||||
Button("Delete All", role: .destructive) {
|
||||
clearAllDownloads(preservePersistentDownloads: false)
|
||||
}
|
||||
Button("Clear Library Only", role: .destructive) {
|
||||
clearAllDownloads(preservePersistentDownloads: true)
|
||||
}
|
||||
} message: {
|
||||
Text("Are you sure you want to delete all downloaded assets? You can choose to clear only the library while preserving the downloaded files for future use.")
|
||||
}
|
||||
.onAppear {
|
||||
calculateTotalStorage()
|
||||
jsController.updateMaxConcurrentDownloads(maxConcurrentDownloads)
|
||||
|
|
|
|||
|
|
@ -82,6 +82,11 @@ struct SettingsView: View {
|
|||
}
|
||||
Divider().padding(.horizontal, 16)
|
||||
|
||||
NavigationLink(destination: SettingsViewDownloads()) {
|
||||
SettingsNavigationRow(icon: "arrow.down.circle", title: "Download Settings")
|
||||
}
|
||||
Divider().padding(.horizontal, 16)
|
||||
|
||||
NavigationLink(destination: SettingsViewTrackers()) {
|
||||
SettingsNavigationRow(icon: "square.stack.3d.up", title: "Trackers")
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue