mirror of
https://github.com/cranci1/Sora.git
synced 2026-03-11 17:45:37 +00:00
* add contributor link, add hide empty sections toggle, cleanup warnings, tests * fix darkmode label color * use primary and secondary colors ( for consistency with rest of codebase ) * add basic profile views / ui * add current profile view to important places ( navigationbar leading ) * reorder contributors row, update url * merge upstream into fork * add new icons, cleanup, tests. * close app icon sheet automatically on completion * add profilestore ( persistence, enviromentobject ), finalize profile settings view, cleanup, tests * add profilestore ( persistence, enviromentobject ), finalize profile settings view, cleanup, tests * add dismiss keyboard extension, dismiss keyboard on tap outside ( profile settings view ) * fix icon transparency issue, add profile data to icloud sync * remove weird empty view ( search ) shadow, fix dismiss keyboard, align system appearance to other rows ( style ), cleanup, tests * fancy profile switch manu ( navigationbar ) * add explore view ( basic library and search view copy ) * fix uikit alerts not using the correct accentColor * apply custom accentColor to stepper components * style consistency ( icons, colors ), change duplicate section title ( "Info" ), hide more empty sections conditionally, cleanup * fix missing section headers * fix copy paste error ^^' * add empty explore view placeholder, add new shimmer effect ( configurable via settings ), cleanup * convert ContinueWatchingManager() singleton to dependency injected enviroment object to match similar manager structures * fix spelling, inject profile into library and continueWatching Managers, fix iCloudSync premature execution, remove profile from explore view ( wont be needed ) * add update profile function to library and continuewatching managers ( to reload the media items ), change media fetching style of continuewatchingmanager to better match librarymanager, update libraryview to use the new continuewatchingmanager fetch style * fix state desync on insertion / removal / profile change * switched from filtering by profile ids to seperated data storage via user default suites with different ids. * update todo markers * fix bookmarks not getting overwritten on empty userdefaults load * add the profile button back to the explore view ( you might wanna change profile quickly ), add todos * moved some views into folders, renamed contentview to rootview, moved bookmark button into navigationbar, used randomUseragents everywhere, add tvos target, add tvos images and basic tabview, started work on settings view design * add new shimmer type, swap two settings rows, add detailed instructions to some todos * Squashed commit of the following: commit5d076e0cf7Author: cranci <100066266+cranci1@users.noreply.github.com> Date: Tue Apr 22 15:03:25 2025 +0200 Aniskip logic and basic buttons (#96) (#97) * Aniskip logic and basic buttons * good fuckin enough for now * im callin good enough * bug fix * its something * hallelujah * Update SearchView.swift * made subs go up the progress bar if it is showing --------- Co-authored-by: ibro <54913038+xibrox@users.noreply.github.com> Co-authored-by: Seiike <122684677+Seeike@users.noreply.github.com> commit0ad4659d2cAuthor: Seiike <122684677+Seeike@users.noreply.github.com> Date: Sun Apr 20 19:50:15 2025 +0200 hello 👋 (#95) * bug fix dimming * improved the fetchEpisodeMetadata logic commit83cf7b0e9fMerge:d28a55a68e8196Author: cranci <100066266+cranci1@users.noreply.github.com> Date: Sun Apr 20 08:53:08 2025 +0200 Implementation of loading modal and dim mode (#93) --------- Co-authored-by: Dominic Drees <dominic.drees@atino.de> Co-authored-by: Francesco <100066266+cranci1@users.noreply.github.com>
215 lines
9 KiB
Swift
215 lines
9 KiB
Swift
//
|
|
// SettingsViewModule.swift
|
|
// Sora
|
|
//
|
|
// Created by Francesco on 05/01/25.
|
|
//
|
|
|
|
import SwiftUI
|
|
import Kingfisher
|
|
|
|
struct SettingsViewModule: View {
|
|
@EnvironmentObject var moduleManager: ModuleManager
|
|
@EnvironmentObject var settings: Settings
|
|
|
|
@AppStorage("selectedModuleId") private var selectedModuleId: String?
|
|
@AppStorage("hideEmptySections") private var hideEmptySections: Bool?
|
|
|
|
@State private var errorMessage: String?
|
|
@State private var isLoading = false
|
|
@State private var isRefreshing = false
|
|
@State private var moduleUrl: String = ""
|
|
@State private var refreshTask: Task<Void, Never>?
|
|
|
|
var body: some View {
|
|
VStack {
|
|
Form {
|
|
if !(hideEmptySections ?? false) && moduleManager.modules.isEmpty {
|
|
VStack(spacing: 8) {
|
|
Image(systemName: "plus.app")
|
|
.font(.largeTitle)
|
|
.foregroundColor(.secondary)
|
|
Text("No Modules")
|
|
.font(.headline)
|
|
Text("Click the plus button to add a module!")
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
.frame(maxWidth: .infinity)
|
|
}
|
|
.padding()
|
|
.frame(maxWidth: .infinity)
|
|
}
|
|
else {
|
|
ForEach(moduleManager.modules) { module in
|
|
HStack {
|
|
KFImage(URL(string: module.metadata.iconUrl))
|
|
.resizable()
|
|
.frame(width: 50, height: 50)
|
|
.clipShape(Circle())
|
|
.padding(.trailing, 10)
|
|
|
|
VStack(alignment: .leading) {
|
|
HStack(alignment: .bottom, spacing: 4) {
|
|
Text(module.metadata.sourceName)
|
|
.font(.headline)
|
|
.foregroundColor(.primary)
|
|
Text("v\(module.metadata.version)")
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
Text("Author: \(module.metadata.author.name)")
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
Text("Language: \(module.metadata.language)")
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
|
|
Spacer()
|
|
|
|
if module.id.uuidString == selectedModuleId {
|
|
Image(systemName: "checkmark")
|
|
.foregroundColor(.accentColor)
|
|
.frame(width: 25, height: 25)
|
|
}
|
|
}
|
|
.contentShape(Rectangle())
|
|
.onTapGesture {
|
|
selectedModuleId = module.id.uuidString
|
|
}
|
|
.contextMenu {
|
|
Button(action: {
|
|
UIPasteboard.general.string = module.metadataUrl
|
|
DropManager.shared.showDrop(title: "Copied to Clipboard", subtitle: "", duration: 1.0, icon: UIImage(systemName: "doc.on.clipboard.fill"))
|
|
}) {
|
|
Label("Copy URL", systemImage: "doc.on.doc")
|
|
}
|
|
Button(role: .destructive) {
|
|
if selectedModuleId != module.id.uuidString {
|
|
moduleManager.deleteModule(module)
|
|
DropManager.shared.showDrop(title: "Module Removed", subtitle: "", duration: 1.0, icon: UIImage(systemName: "trash"))
|
|
}
|
|
} label: {
|
|
Label("Delete", systemImage: "trash")
|
|
}
|
|
.disabled(selectedModuleId == module.id.uuidString)
|
|
}
|
|
.swipeActions {
|
|
if selectedModuleId != module.id.uuidString {
|
|
Button(role: .destructive) {
|
|
moduleManager.deleteModule(module)
|
|
DropManager.shared.showDrop(title: "Module Removed", subtitle: "", duration: 1.0, icon: UIImage(systemName: "trash"))
|
|
} label: {
|
|
Label("Delete", systemImage: "trash")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("Modules")
|
|
.navigationBarItems(trailing: Button(action: {
|
|
showAddModuleAlert()
|
|
}) {
|
|
Image(systemName: "plus")
|
|
.resizable()
|
|
.padding(5)
|
|
})
|
|
.refreshable {
|
|
isRefreshing = true
|
|
refreshTask?.cancel()
|
|
refreshTask = Task {
|
|
await moduleManager.refreshModules()
|
|
isRefreshing = false
|
|
}
|
|
}
|
|
}
|
|
.onAppear {
|
|
refreshTask = Task {
|
|
await moduleManager.refreshModules()
|
|
}
|
|
}
|
|
.onDisappear {
|
|
refreshTask?.cancel()
|
|
}
|
|
.alert("Error", isPresented: Binding(
|
|
get: { errorMessage != nil },
|
|
set: { if !$0 { errorMessage = nil } }
|
|
)) {
|
|
Button("OK") {
|
|
errorMessage = nil
|
|
}
|
|
} message: {
|
|
Text(errorMessage ?? "Unknown error")
|
|
}
|
|
}
|
|
|
|
func showAddModuleAlert() {
|
|
let pasteboardString = UIPasteboard.general.string ?? ""
|
|
|
|
if !pasteboardString.isEmpty {
|
|
let clipboardAlert = UIAlertController(
|
|
title: "Clipboard Detected",
|
|
message: "We found some text in your clipboard. Would you like to use it as the module URL?",
|
|
preferredStyle: .alert
|
|
)
|
|
|
|
clipboardAlert.addAction(UIAlertAction(title: "Use Clipboard", style: .default, handler: { _ in
|
|
self.displayModuleView(url: pasteboardString)
|
|
}))
|
|
|
|
clipboardAlert.addAction(UIAlertAction(title: "Enter Manually", style: .cancel, handler: { _ in
|
|
self.showManualUrlAlert()
|
|
}))
|
|
|
|
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
|
let rootViewController = windowScene.windows.first?.rootViewController {
|
|
windowScene.windows.first?.tintColor = UIColor(settings.accentColor)
|
|
rootViewController.present(clipboardAlert, animated: true, completion: nil)
|
|
}
|
|
|
|
} else {
|
|
showManualUrlAlert()
|
|
}
|
|
}
|
|
|
|
func showManualUrlAlert() {
|
|
let alert = UIAlertController(
|
|
title: "Add Module",
|
|
message: "Enter the URL of the module file",
|
|
preferredStyle: .alert
|
|
)
|
|
|
|
alert.addTextField { textField in
|
|
textField.placeholder = "https://real.url/module.json"
|
|
}
|
|
|
|
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
|
|
alert.addAction(UIAlertAction(title: "Add", style: .default, handler: { _ in
|
|
if let url = alert.textFields?.first?.text, !url.isEmpty {
|
|
self.displayModuleView(url: url)
|
|
}
|
|
}))
|
|
|
|
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
|
let rootViewController = windowScene.windows.first?.rootViewController {
|
|
windowScene.windows.first?.tintColor = UIColor(settings.accentColor)
|
|
rootViewController.present(alert, animated: true, completion: nil)
|
|
}
|
|
}
|
|
|
|
func displayModuleView(url: String) {
|
|
DispatchQueue.main.async {
|
|
let addModuleView = ModuleAdditionSettingsView(moduleUrl: url)
|
|
.environmentObject(self.moduleManager)
|
|
let hostingController = UIHostingController(rootView: addModuleView)
|
|
|
|
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
|
let window = windowScene.windows.first {
|
|
window.tintColor = UIColor(settings.accentColor)
|
|
window.rootViewController?.present(hostingController, animated: true, completion: nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|