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:
50/50 2025-05-31 21:17:27 +02:00 committed by GitHub
parent 400079f0da
commit 8b2ee00c90
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 295 additions and 75 deletions

View file

@ -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)

View file

@ -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")
}