mirror of
https://github.com/cranci1/Sora.git
synced 2026-03-11 17:45:37 +00:00
Separate library settings
This commit is contained in:
parent
f015db7e34
commit
faa7700ff7
4 changed files with 289 additions and 149 deletions
|
|
@ -158,31 +158,13 @@ struct SettingsViewGeneral: View {
|
|||
try! JSONEncoder().encode(["TMDB","AniList"])
|
||||
}()
|
||||
@AppStorage("tmdbImageWidth") private var TMDBimageWidht: String = "original"
|
||||
@AppStorage("mediaColumnsPortrait") private var mediaColumnsPortrait: Int = 2
|
||||
@AppStorage("mediaColumnsLandscape") private var mediaColumnsLandscape: Int = 4
|
||||
@AppStorage("metadataProviders") private var metadataProviders: String = "TMDB"
|
||||
@AppStorage("librarySectionsOrderData") private var librarySectionsOrderData: Data = {
|
||||
try! JSONEncoder().encode(["continueWatching", "continueReading", "collections"])
|
||||
}()
|
||||
@AppStorage("disabledLibrarySectionsData") private var disabledLibrarySectionsData: Data = {
|
||||
try! JSONEncoder().encode([String]())
|
||||
}()
|
||||
|
||||
private var metadataProvidersOrder: [String] {
|
||||
get { (try? JSONDecoder().decode([String].self, from: metadataProvidersOrderData)) ?? ["AniList","TMDB"] }
|
||||
set { metadataProvidersOrderData = try! JSONEncoder().encode(newValue) }
|
||||
}
|
||||
|
||||
private var librarySectionsOrder: [String] {
|
||||
get { (try? JSONDecoder().decode([String].self, from: librarySectionsOrderData)) ?? ["continueWatching", "continueReading", "collections"] }
|
||||
set { librarySectionsOrderData = try! JSONEncoder().encode(newValue) }
|
||||
}
|
||||
|
||||
private var disabledLibrarySections: [String] {
|
||||
get { (try? JSONDecoder().decode([String].self, from: disabledLibrarySectionsData)) ?? [] }
|
||||
set { disabledLibrarySectionsData = try! JSONEncoder().encode(newValue) }
|
||||
}
|
||||
|
||||
private let TMDBimageWidhtList = ["300", "500", "780", "1280", "original"]
|
||||
private let sortOrderOptions = ["Ascending", "Descending"]
|
||||
private let metadataProvidersList = ["TMDB", "AniList"]
|
||||
|
|
@ -348,30 +330,6 @@ struct SettingsViewGeneral: View {
|
|||
.environment(\.editMode, .constant(.active))
|
||||
}
|
||||
|
||||
SettingsSection(
|
||||
title: NSLocalizedString("Media Grid Layout", comment: ""),
|
||||
footer: NSLocalizedString("Adjust the number of media items per row in portrait and landscape modes.", comment: "")
|
||||
) {
|
||||
SettingsPickerRow(
|
||||
icon: "rectangle.portrait",
|
||||
title: NSLocalizedString("Portrait Columns", comment: ""),
|
||||
options: UIDevice.current.userInterfaceIdiom == .pad ? Array(1...5) : Array(1...4),
|
||||
optionToString: { "\($0)" },
|
||||
selection: $mediaColumnsPortrait
|
||||
)
|
||||
|
||||
SettingsPickerRow(
|
||||
icon: "rectangle",
|
||||
title: NSLocalizedString("Landscape Columns", comment: ""),
|
||||
options: UIDevice.current.userInterfaceIdiom == .pad ? Array(2...8) : Array(2...5),
|
||||
optionToString: { "\($0)" },
|
||||
selection: $mediaColumnsLandscape,
|
||||
showDivider: false
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
SettingsSection(
|
||||
title: NSLocalizedString("Advanced", comment: ""),
|
||||
footer: NSLocalizedString("Anonymous data is collected to improve the app. No personal information is collected. This can be disabled at any time.", comment: "")
|
||||
|
|
@ -383,70 +341,6 @@ struct SettingsViewGeneral: View {
|
|||
showDivider: false
|
||||
)
|
||||
}
|
||||
|
||||
SettingsSection(
|
||||
title: NSLocalizedString("Library View", comment: ""),
|
||||
footer: NSLocalizedString("Customize the sections shown in your library. You can reorder sections or disable them completely.", comment: "")
|
||||
) {
|
||||
VStack(spacing: 0) {
|
||||
HStack {
|
||||
Image(systemName: "arrow.up.arrow.down")
|
||||
.frame(width: 24, height: 24)
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Text(NSLocalizedString("Library Sections Order", comment: ""))
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
|
||||
List {
|
||||
ForEach(Array(librarySectionsOrder.enumerated()), id: \.element) { index, section in
|
||||
HStack {
|
||||
Text("\(index + 1)")
|
||||
.frame(width: 24, height: 24)
|
||||
.foregroundStyle(.gray)
|
||||
|
||||
Image(systemName: sectionIcon(for: section))
|
||||
.frame(width: 24, height: 24)
|
||||
|
||||
Text(sectionName(for: section))
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
|
||||
Toggle("", isOn: toggleBinding(for: section))
|
||||
.labelsHidden()
|
||||
.tint(.accentColor.opacity(0.7))
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
.listRowBackground(Color.clear)
|
||||
.listRowSeparator(.visible)
|
||||
.listRowSeparatorTint(.gray.opacity(0.3))
|
||||
.listRowInsets(EdgeInsets())
|
||||
}
|
||||
.onMove { from, to in
|
||||
var arr = librarySectionsOrder
|
||||
arr.move(fromOffsets: from, toOffset: to)
|
||||
librarySectionsOrderData = try! JSONEncoder().encode(arr)
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.frame(height: CGFloat(librarySectionsOrder.count * 70))
|
||||
.background(Color.clear)
|
||||
|
||||
Text(NSLocalizedString("Drag to reorder sections", comment: ""))
|
||||
.font(.caption)
|
||||
.foregroundStyle(.gray)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
.padding(.top, -6)
|
||||
.padding(.bottom, 8)
|
||||
}
|
||||
.environment(\.editMode, .constant(.active))
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 20)
|
||||
}
|
||||
|
|
@ -460,47 +354,4 @@ struct SettingsViewGeneral: View {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func sectionName(for section: String) -> String {
|
||||
switch section {
|
||||
case "continueWatching":
|
||||
return NSLocalizedString("Continue Watching", comment: "")
|
||||
case "continueReading":
|
||||
return NSLocalizedString("Continue Reading", comment: "")
|
||||
case "collections":
|
||||
return NSLocalizedString("Collections", comment: "")
|
||||
default:
|
||||
return section
|
||||
}
|
||||
}
|
||||
|
||||
private func sectionIcon(for section: String) -> String {
|
||||
switch section {
|
||||
case "continueWatching":
|
||||
return "play.fill"
|
||||
case "continueReading":
|
||||
return "book.fill"
|
||||
case "collections":
|
||||
return "folder.fill"
|
||||
default:
|
||||
return "questionmark"
|
||||
}
|
||||
}
|
||||
|
||||
private func toggleBinding(for section: String) -> Binding<Bool> {
|
||||
return Binding(
|
||||
get: { !self.disabledLibrarySections.contains(section) },
|
||||
set: { isEnabled in
|
||||
var sections = self.disabledLibrarySections
|
||||
if isEnabled {
|
||||
sections.removeAll { $0 == section }
|
||||
} else {
|
||||
if !sections.contains(section) {
|
||||
sections.append(section)
|
||||
}
|
||||
}
|
||||
self.disabledLibrarySectionsData = try! JSONEncoder().encode(sections)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,280 @@
|
|||
//
|
||||
// SettingsViewLibrary.swift
|
||||
// Sora
|
||||
//
|
||||
// Created by paul on 05/02/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
HStack {
|
||||
Image(systemName: icon)
|
||||
.frame(width: 24, height: 24)
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Text(title)
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
|
||||
Picker("", selection: $selection) {
|
||||
ForEach(options, id: \.self) { option in
|
||||
Text(optionToString(option)).tag(option)
|
||||
}
|
||||
}
|
||||
.pickerStyle(MenuPickerStyle())
|
||||
.labelsHidden()
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
|
||||
if showDivider {
|
||||
Divider().padding(.horizontal, 16)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SettingsViewLibrary: View {
|
||||
@AppStorage("mediaColumnsPortrait") private var mediaColumnsPortrait: Int = 2
|
||||
@AppStorage("mediaColumnsLandscape") private var mediaColumnsLandscape: Int = 4
|
||||
@AppStorage("librarySectionsOrderData") private var librarySectionsOrderData: Data = {
|
||||
try! JSONEncoder().encode(["continueWatching", "continueReading", "collections"])
|
||||
}()
|
||||
@AppStorage("disabledLibrarySectionsData") private var disabledLibrarySectionsData: Data = {
|
||||
try! JSONEncoder().encode([String]())
|
||||
}()
|
||||
|
||||
private var librarySectionsOrder: [String] {
|
||||
get { (try? JSONDecoder().decode([String].self, from: librarySectionsOrderData)) ?? ["continueWatching", "continueReading", "collections"] }
|
||||
set { librarySectionsOrderData = try! JSONEncoder().encode(newValue) }
|
||||
}
|
||||
|
||||
private var disabledLibrarySections: [String] {
|
||||
get { (try? JSONDecoder().decode([String].self, from: disabledLibrarySectionsData)) ?? [] }
|
||||
set { disabledLibrarySectionsData = try! JSONEncoder().encode(newValue) }
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollView(showsIndicators: false) {
|
||||
VStack(spacing: 24) {
|
||||
SettingsSection(
|
||||
title: NSLocalizedString("Media Grid Layout", comment: ""),
|
||||
footer: NSLocalizedString("Adjust the number of media items per row in portrait and landscape modes.", comment: "")
|
||||
) {
|
||||
SettingsPickerRow(
|
||||
icon: "rectangle.portrait",
|
||||
title: NSLocalizedString("Portrait Columns", comment: ""),
|
||||
options: UIDevice.current.userInterfaceIdiom == .pad ? Array(1...5) : Array(1...4),
|
||||
optionToString: { "\($0)" },
|
||||
selection: $mediaColumnsPortrait
|
||||
)
|
||||
|
||||
SettingsPickerRow(
|
||||
icon: "rectangle",
|
||||
title: NSLocalizedString("Landscape Columns", comment: ""),
|
||||
options: UIDevice.current.userInterfaceIdiom == .pad ? Array(2...8) : Array(2...5),
|
||||
optionToString: { "\($0)" },
|
||||
selection: $mediaColumnsLandscape,
|
||||
showDivider: false
|
||||
)
|
||||
}
|
||||
|
||||
SettingsSection(
|
||||
title: NSLocalizedString("Library View", comment: ""),
|
||||
footer: NSLocalizedString("Customize the sections shown in your library. You can reorder sections or disable them completely.", comment: "")
|
||||
) {
|
||||
VStack(spacing: 0) {
|
||||
HStack {
|
||||
Image(systemName: "arrow.up.arrow.down")
|
||||
.frame(width: 24, height: 24)
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Text(NSLocalizedString("Library Sections Order", comment: ""))
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
|
||||
List {
|
||||
ForEach(Array(librarySectionsOrder.enumerated()), id: \.element) { index, section in
|
||||
HStack {
|
||||
Text("\(index + 1)")
|
||||
.frame(width: 24, height: 24)
|
||||
.foregroundStyle(.gray)
|
||||
|
||||
Image(systemName: sectionIcon(for: section))
|
||||
.frame(width: 24, height: 24)
|
||||
|
||||
Text(sectionName(for: section))
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
|
||||
Toggle("", isOn: toggleBinding(for: section))
|
||||
.labelsHidden()
|
||||
.tint(.accentColor.opacity(0.7))
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
.listRowBackground(Color.clear)
|
||||
.listRowSeparator(.visible)
|
||||
.listRowSeparatorTint(.gray.opacity(0.3))
|
||||
.listRowInsets(EdgeInsets())
|
||||
}
|
||||
.onMove { from, to in
|
||||
var arr = librarySectionsOrder
|
||||
arr.move(fromOffsets: from, toOffset: to)
|
||||
librarySectionsOrderData = try! JSONEncoder().encode(arr)
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.frame(height: CGFloat(librarySectionsOrder.count * 70))
|
||||
.environment(\.editMode, .constant(.active))
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 20)
|
||||
}
|
||||
.navigationTitle(NSLocalizedString("Library", comment: ""))
|
||||
.scrollViewBottomPadding()
|
||||
}
|
||||
|
||||
private func sectionName(for section: String) -> String {
|
||||
switch section {
|
||||
case "continueWatching":
|
||||
return NSLocalizedString("Continue Watching", comment: "")
|
||||
case "continueReading":
|
||||
return NSLocalizedString("Continue Reading", comment: "")
|
||||
case "collections":
|
||||
return NSLocalizedString("Collections", comment: "")
|
||||
default:
|
||||
return section.capitalized
|
||||
}
|
||||
}
|
||||
|
||||
private func sectionIcon(for section: String) -> String {
|
||||
switch section {
|
||||
case "continueWatching":
|
||||
return "play.circle"
|
||||
case "continueReading":
|
||||
return "book"
|
||||
case "collections":
|
||||
return "folder"
|
||||
default:
|
||||
return "questionmark.circle"
|
||||
}
|
||||
}
|
||||
|
||||
private func toggleBinding(for section: String) -> Binding<Bool> {
|
||||
return Binding(
|
||||
get: { !self.disabledLibrarySections.contains(section) },
|
||||
set: { isEnabled in
|
||||
var sections = self.disabledLibrarySections
|
||||
if isEnabled {
|
||||
sections.removeAll { $0 == section }
|
||||
} else {
|
||||
if !sections.contains(section) {
|
||||
sections.append(section)
|
||||
}
|
||||
}
|
||||
self.disabledLibrarySectionsData = try! JSONEncoder().encode(sections)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -171,6 +171,11 @@ struct SettingsView: View {
|
|||
}
|
||||
Divider().padding(.horizontal, 16)
|
||||
|
||||
NavigationLink(destination: SettingsViewLibrary().navigationBarBackButtonHidden(false)) {
|
||||
SettingsNavigationRow(icon: "books.vertical", titleKey: "Library")
|
||||
}
|
||||
Divider().padding(.horizontal, 16)
|
||||
|
||||
NavigationLink(destination: SettingsViewPlayer().navigationBarBackButtonHidden(false)) {
|
||||
SettingsNavigationRow(icon: "play.circle", titleKey: "Video Player")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
0414ECFE2E32D6EF00A7E76A /* Bundle+Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0414ECFC2E32D6EF00A7E76A /* Bundle+Language.swift */; };
|
||||
0414ECFF2E32D6EF00A7E76A /* LocalizationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0414ECFD2E32D6EF00A7E76A /* LocalizationManager.swift */; };
|
||||
0414ED032E32D72400A7E76A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0414ED012E32D72400A7E76A /* Localizable.strings */; };
|
||||
0414ED052E32D90000A7E76A /* SettingsViewLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0414ED042E32D90000A7E76A /* SettingsViewLibrary.swift */; };
|
||||
041E9D722E11D71F0025F150 /* SettingsViewBackup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041E9D712E11D71F0025F150 /* SettingsViewBackup.swift */; };
|
||||
04536F712E04BA3B00A11248 /* JSController-Novel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04536F702E04BA3B00A11248 /* JSController-Novel.swift */; };
|
||||
04536F742E04BA5600A11248 /* ReaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04536F722E04BA5600A11248 /* ReaderView.swift */; };
|
||||
|
|
@ -143,6 +144,7 @@
|
|||
0414ECFC2E32D6EF00A7E76A /* Bundle+Language.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Language.swift"; sourceTree = "<group>"; };
|
||||
0414ECFD2E32D6EF00A7E76A /* LocalizationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationManager.swift; sourceTree = "<group>"; };
|
||||
0414ED002E32D72400A7E76A /* mn-Cyrl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "mn-Cyrl"; path = Localizable.strings; sourceTree = "<group>"; };
|
||||
0414ED042E32D90000A7E76A /* SettingsViewLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewLibrary.swift; sourceTree = "<group>"; };
|
||||
041E9D712E11D71F0025F150 /* SettingsViewBackup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewBackup.swift; sourceTree = "<group>"; };
|
||||
0452339E2E02149C002EA23C /* bos */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bos; path = Localizable.strings; sourceTree = "<group>"; };
|
||||
04536F702E04BA3B00A11248 /* JSController-Novel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSController-Novel.swift"; sourceTree = "<group>"; };
|
||||
|
|
@ -594,6 +596,7 @@
|
|||
133D7C832D2BE2630075467E /* SettingsSubViews */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0414ED042E32D90000A7E76A /* SettingsViewLibrary.swift */,
|
||||
041E9D712E11D71F0025F150 /* SettingsViewBackup.swift */,
|
||||
1E9FF1D22D403E42008AC100 /* SettingsViewLoggerFilter.swift */,
|
||||
1399FAD32D3AB38C00E97C31 /* SettingsViewLogger.swift */,
|
||||
|
|
@ -1015,6 +1018,7 @@
|
|||
047F170E2E0C93F30081B5FB /* ContinueReadingItem.swift in Sources */,
|
||||
047F170F2E0C93F30081B5FB /* ContinueReadingManager.swift in Sources */,
|
||||
13DB46902D900A38008CBC03 /* URL.swift in Sources */,
|
||||
0414ED052E32D90000A7E76A /* SettingsViewLibrary.swift in Sources */,
|
||||
138FE1D02DECA00D00936D81 /* TMDB-FetchID.swift in Sources */,
|
||||
04AD07162E03704700EB74C1 /* BookmarkCell.swift in Sources */,
|
||||
130C6BFA2D53AB1F00DC1432 /* SettingsViewData.swift in Sources */,
|
||||
|
|
|
|||
Loading…
Reference in a new issue