too many things idk

This commit is contained in:
cranci1 2025-03-09 11:32:06 +01:00
parent c7c9e5407c
commit b8d1567efb
10 changed files with 278 additions and 477 deletions

View file

@ -11,10 +11,6 @@ import Kingfisher
struct ContentView: View {
var body: some View {
TabView {
HomeView()
.tabItem {
Label("Home", systemImage: "house")
}
LibraryView()
.tabItem {
Label("Library", systemImage: "books.vertical")

View file

@ -1,353 +0,0 @@
//
// HomeView.swift
// Sora
//
// Created by Francesco on 05/01/25.
//
import SwiftUI
import Kingfisher
struct HomeView: View {
@AppStorage("trackingService") private var tracingService: String = "AniList"
@State private var aniListItems: [AniListItem] = []
@State private var trendingItems: [AniListItem] = []
@State private var continueWatchingItems: [ContinueWatchingItem] = []
@State private var isLoading: Bool = true
private var currentDeviceSeasonAndYear: (season: String, year: Int) {
let currentDate = Date()
let calendar = Calendar.current
let year = calendar.component(.year, from: currentDate)
let month = calendar.component(.month, from: currentDate)
let season: String
switch month {
case 1...3:
season = "Winter"
case 4...6:
season = "Spring"
case 7...9:
season = "Summer"
default:
season = "Fall"
}
return (season, year)
}
private var trendingDateString: String {
let formatter = DateFormatter()
formatter.dateFormat = "EEEE, dd MMMM yyyy"
return formatter.string(from: Date())
}
var body: some View {
NavigationView {
ScrollView {
VStack(alignment: .leading) {
if !continueWatchingItems.isEmpty {
ContinueWatchingSection(items: $continueWatchingItems) { item in
markContinueWatchingItemAsWatched(item: item)
} removeItem: { item in
removeContinueWatchingItem(item: item)
}
}
SeasonalSection(
title: "Seasonal of \(currentDeviceSeasonAndYear.season) \(String(format: "%d", currentDeviceSeasonAndYear.year))",
items: aniListItems,
isLoading: isLoading
)
TrendingSection(
title: "Trending on \(trendingDateString)",
items: trendingItems,
isLoading: isLoading
)
}
.padding(.bottom, 16)
}
.navigationTitle("Home")
.onAppear {
fetchData()
}
.refreshable {
fetchData()
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
private func fetchData() {
isLoading = true
continueWatchingItems = ContinueWatchingManager.shared.fetchItems()
let fetchSeasonal: (@escaping ([AniListItem]?) -> Void) -> Void
let fetchTrending: (@escaping ([AniListItem]?) -> Void) -> Void
if tracingService == "TMDB" {
fetchSeasonal = TMDBSeasonal.fetchTMDBSeasonal
fetchTrending = TMBDTrending.fetchTMDBTrending
} else {
fetchSeasonal = AnilistServiceSeasonalAnime().fetchSeasonalAnime
fetchTrending = AnilistServiceTrendingAnime().fetchTrendingAnime
}
fetchSeasonal { items in
aniListItems = items ?? []
checkLoadingState()
}
fetchTrending { items in
trendingItems = items ?? []
checkLoadingState()
}
}
private func checkLoadingState() {
if !aniListItems.isEmpty && !trendingItems.isEmpty {
isLoading = false
}
}
private func markContinueWatchingItemAsWatched(item: ContinueWatchingItem) {
let key = "lastPlayedTime_\(item.fullUrl)"
let totalKey = "totalTime_\(item.fullUrl)"
UserDefaults.standard.set(99999999.0, forKey: key)
UserDefaults.standard.set(99999999.0, forKey: totalKey)
ContinueWatchingManager.shared.remove(item: item)
continueWatchingItems.removeAll { $0.id == item.id }
}
private func removeContinueWatchingItem(item: ContinueWatchingItem) {
ContinueWatchingManager.shared.remove(item: item)
continueWatchingItems.removeAll { $0.id == item.id }
}
}
struct ContinueWatchingSection: View {
@Binding var items: [ContinueWatchingItem]
var markAsWatched: (ContinueWatchingItem) -> Void
var removeItem: (ContinueWatchingItem) -> Void
var body: some View {
LazyVStack(alignment: .leading) {
SectionHeader(title: "Continue Watching")
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 8) {
ForEach(Array(items.reversed())) { item in
ContinueWatchingCell(item: item) {
markAsWatched(item)
} removeItem: {
removeItem(item)
}
}
}
.padding(.horizontal, 20)
}
.frame(height: 190)
}
}
}
struct ContinueWatchingCell: View {
let item: ContinueWatchingItem
var markAsWatched: () -> Void
var removeItem: () -> Void
var body: some View {
Button(action: {
if UserDefaults.standard.string(forKey: "externalPlayer") == "Sora" {
let customMediaPlayer = CustomMediaPlayerViewController(
module: item.module,
urlString: item.streamUrl,
fullUrl: item.fullUrl,
title: item.mediaTitle,
episodeNumber: item.episodeNumber,
onWatchNext: { },
subtitlesURL: item.subtitles,
episodeImageUrl: item.imageUrl
)
customMediaPlayer.modalPresentationStyle = .fullScreen
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let rootVC = windowScene.windows.first?.rootViewController {
findTopViewController.findViewController(rootVC).present(customMediaPlayer, animated: true, completion: nil)
}
} else {
let videoPlayerViewController = VideoPlayerViewController(module: item.module)
videoPlayerViewController.streamUrl = item.streamUrl
videoPlayerViewController.fullUrl = item.fullUrl
videoPlayerViewController.episodeImageUrl = item.imageUrl
videoPlayerViewController.episodeNumber = item.episodeNumber
videoPlayerViewController.mediaTitle = item.mediaTitle
videoPlayerViewController.subtitles = item.subtitles ?? ""
videoPlayerViewController.modalPresentationStyle = .fullScreen
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let rootVC = windowScene.windows.first?.rootViewController {
findTopViewController.findViewController(rootVC).present(videoPlayerViewController, animated: true, completion: nil)
}
}
}) {
VStack(alignment: .leading) {
ZStack {
KFImage(URL(string: item.imageUrl.isEmpty ? "https://raw.githubusercontent.com/cranci1/Sora/refs/heads/main/assets/banner2.png" : item.imageUrl))
.placeholder {
RoundedRectangle(cornerRadius: 10)
.fill(Color.gray.opacity(0.3))
.frame(width: 240, height: 135)
.shimmering()
}
.setProcessor(RoundCornerImageProcessor(cornerRadius: 10))
.resizable()
.aspectRatio(16/9, contentMode: .fill)
.frame(width: 240, height: 135)
.cornerRadius(10)
.clipped()
.overlay(
KFImage(URL(string: item.module.metadata.iconUrl))
.resizable()
.frame(width: 24, height: 24)
.cornerRadius(4)
.padding(4),
alignment: .topLeading
)
}
.overlay(
ZStack {
Rectangle()
.fill(Color.black.opacity(0.3))
.blur(radius: 3)
.frame(height: 30)
ProgressView(value: item.progress)
.progressViewStyle(LinearProgressViewStyle(tint: .white))
.padding(.horizontal, 8)
.scaleEffect(x: 1, y: 1.5, anchor: .center)
},
alignment: .bottom
)
VStack(alignment: .leading) {
Text("Episode \(item.episodeNumber)")
.font(.caption)
.lineLimit(1)
.foregroundColor(.secondary)
Text(item.mediaTitle)
.font(.caption)
.lineLimit(2)
.foregroundColor(.primary)
.multilineTextAlignment(.leading)
}
}
.frame(width: 240, height: 170)
}
.contextMenu {
Button(action: { markAsWatched() }) {
Label("Mark as Watched", systemImage: "checkmark.circle")
}
Button(role: .destructive, action: { removeItem() }) {
Label("Remove Item", systemImage: "trash")
}
}
}
}
struct SeasonalSection: View {
let title: String
let items: [AniListItem]
let isLoading: Bool
var body: some View {
VStack(alignment: .leading) {
SectionHeader(title: title)
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 8) {
if isLoading {
ForEach(0..<5, id: \.self) { _ in
HomeSkeletonCell()
}
} else {
ForEach(items, id: \.id) { item in
NavigationLink(destination: AniListDetailsView(animeID: item.id)) {
AnimeItemCell(item: item)
}
}
}
}
.padding(.horizontal, 20)
}
}
}
}
struct TrendingSection: View {
let title: String
let items: [AniListItem]
let isLoading: Bool
var body: some View {
VStack(alignment: .leading) {
SectionHeader(title: title)
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 8) {
if isLoading {
ForEach(0..<5, id: \.self) { _ in
HomeSkeletonCell()
}
} else {
ForEach(items, id: \.id) { item in
NavigationLink(destination: AniListDetailsView(animeID: item.id)) {
AnimeItemCell(item: item)
}
}
}
}
.padding(.horizontal, 20)
}
}
}
}
struct SectionHeader: View {
let title: String
var body: some View {
Text(title)
.font(.headline)
.padding(.horizontal, 20)
.padding(.top, 8)
}
}
struct AnimeItemCell: View {
let item: AniListItem
var body: some View {
VStack {
KFImage(URL(string: item.coverImage.large))
.placeholder {
RoundedRectangle(cornerRadius: 10)
.fill(Color.gray.opacity(0.3))
.frame(width: 130, height: 195)
.shimmering()
}
.setProcessor(RoundCornerImageProcessor(cornerRadius: 10))
.resizable()
.scaledToFill()
.frame(width: 130, height: 195)
.cornerRadius(10)
.clipped()
Text(item.title.romaji)
.font(.caption)
.frame(width: 130)
.lineLimit(1)
.multilineTextAlignment(.center)
.foregroundColor(.primary)
}
}
}

View file

@ -13,14 +13,16 @@ struct LibraryItem: Codable, Identifiable {
let imageUrl: String
let href: String
let moduleId: String
let moduleName: String
let dateAdded: Date
init(title: String, imageUrl: String, href: String, moduleId: String) {
init(title: String, imageUrl: String, href: String, moduleId: String, moduleName: String) {
self.id = UUID()
self.title = title
self.imageUrl = imageUrl
self.href = href
self.moduleId = moduleId
self.moduleName = moduleName
self.dateAdded = Date()
}
}
@ -55,15 +57,15 @@ class LibraryManager: ObservableObject {
}
}
func isBookmarked(href: String) -> Bool {
func isBookmarked(href: String, moduleName: String) -> Bool {
bookmarks.contains { $0.href == href }
}
func toggleBookmark(title: String, imageUrl: String, href: String, moduleId: String) {
func toggleBookmark(title: String, imageUrl: String, href: String, moduleId: String, moduleName: String) {
if let index = bookmarks.firstIndex(where: { $0.href == href }) {
bookmarks.remove(at: index)
} else {
let bookmark = LibraryItem(title: title, imageUrl: imageUrl, href: href, moduleId: moduleId)
let bookmark = LibraryItem(title: title, imageUrl: imageUrl, href: href, moduleId: moduleId, moduleName: moduleName)
bookmarks.insert(bookmark, at: 0)
}
saveBookmarks()

View file

@ -12,6 +12,8 @@ struct LibraryView: View {
@EnvironmentObject private var libraryManager: LibraryManager
@EnvironmentObject private var moduleManager: ModuleManager
@State private var continueWatchingItems: [ContinueWatchingItem] = []
private let columns = [
GridItem(.adaptive(minimum: 150), spacing: 16)
]
@ -19,68 +21,252 @@ struct LibraryView: View {
var body: some View {
NavigationView {
ScrollView {
if libraryManager.bookmarks.isEmpty {
VStack(spacing: 8) {
Image(systemName: "magazine")
.font(.largeTitle)
.foregroundColor(.secondary)
Text("No Items saved")
.font(.headline)
Text("You can bookmark items to find them easily here")
.font(.caption)
.foregroundColor(.secondary)
VStack(alignment: .leading, spacing: 32) {
Group {
Text("Continue Watching")
.font(.title2)
.bold()
.padding(.horizontal, 20)
if continueWatchingItems.isEmpty {
Text("No items to continue watching")
.foregroundColor(.secondary)
.padding(.horizontal, 20)
} else {
ContinueWatchingSection(items: $continueWatchingItems,
markAsWatched: { item in
markContinueWatchingItemAsWatched(item: item)
},
removeItem: { item in
removeContinueWatchingItem(item: item)
})
}
}
.padding()
.frame(maxWidth: .infinity)
} else {
LazyVGrid(columns: columns, spacing: 16) {
ForEach(libraryManager.bookmarks) { item in
if let module = moduleManager.modules.first(where: { $0.id.uuidString == item.moduleId }) {
NavigationLink(destination: MediaInfoView(title: item.title, imageUrl: item.imageUrl, href: item.href, module: module)) {
VStack {
ZStack(alignment: .bottomTrailing) {
KFImage(URL(string: item.imageUrl))
.placeholder {
RoundedRectangle(cornerRadius: 10)
.fill(Color.gray.opacity(0.3))
Group {
Text("Bookmarks")
.font(.title2)
.bold()
.padding(.horizontal, 20)
if libraryManager.bookmarks.isEmpty {
VStack(spacing: 8) {
Image(systemName: "magazine")
.font(.largeTitle)
.foregroundColor(.secondary)
Text("No Items saved")
.font(.headline)
Text("Bookmark items for easy access later")
.font(.caption)
.foregroundColor(.secondary)
}
.padding()
.frame(maxWidth: .infinity)
} else {
LazyVGrid(columns: columns, spacing: 16) {
ForEach(libraryManager.bookmarks) { item in
if let module = moduleManager.modules.first(where: { $0.id.uuidString == item.moduleId }) {
NavigationLink(destination: MediaInfoView(title: item.title, imageUrl: item.imageUrl, href: item.href, module: module)) {
VStack {
ZStack(alignment: .bottomTrailing) {
KFImage(URL(string: item.imageUrl))
.placeholder {
RoundedRectangle(cornerRadius: 10)
.fill(Color.gray.opacity(0.3))
.frame(width: 150, height: 225)
.shimmering()
}
.resizable()
.aspectRatio(2/3, contentMode: .fill)
.cornerRadius(10)
.frame(width: 150, height: 225)
.shimmering()
}
.resizable()
.aspectRatio(2/3, contentMode: .fill)
.cornerRadius(10)
.frame(width: 150, height: 225)
KFImage(URL(string: module.metadata.iconUrl))
.placeholder {
Circle()
.fill(Color.gray.opacity(0.3))
KFImage(URL(string: module.metadata.iconUrl))
.placeholder {
Circle()
.fill(Color.gray.opacity(0.3))
.frame(width: 35, height: 35)
.shimmering()
}
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 35, height: 35)
.shimmering()
.clipShape(Circle())
.padding(5)
}
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 35, height: 35)
.clipShape(Circle())
.padding(5)
Text(item.title)
.font(.subheadline)
.foregroundColor(.primary)
.lineLimit(2)
.multilineTextAlignment(.leading)
.padding(.horizontal, 8)
}
}
Text(item.title)
.font(.subheadline)
.foregroundColor(.primary)
.lineLimit(2)
.multilineTextAlignment(.leading)
.padding(.horizontal, 8)
}
}
}
.padding(.horizontal, 20)
}
}
.padding()
}
.padding(.vertical, 20)
}
.navigationTitle("Library")
.onAppear {
fetchContinueWatching()
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
private func fetchContinueWatching() {
continueWatchingItems = ContinueWatchingManager.shared.fetchItems()
}
private func markContinueWatchingItemAsWatched(item: ContinueWatchingItem) {
let key = "lastPlayedTime_\(item.fullUrl)"
let totalKey = "totalTime_\(item.fullUrl)"
UserDefaults.standard.set(99999999.0, forKey: key)
UserDefaults.standard.set(99999999.0, forKey: totalKey)
ContinueWatchingManager.shared.remove(item: item)
continueWatchingItems.removeAll { $0.id == item.id }
}
private func removeContinueWatchingItem(item: ContinueWatchingItem) {
ContinueWatchingManager.shared.remove(item: item)
continueWatchingItems.removeAll { $0.id == item.id }
}
}
// ContinueWatchingSection and ContinueWatchingCell remain unchanged
struct ContinueWatchingSection: View {
@Binding var items: [ContinueWatchingItem]
var markAsWatched: (ContinueWatchingItem) -> Void
var removeItem: (ContinueWatchingItem) -> Void
var body: some View {
VStack(alignment: .leading) {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 8) {
ForEach(Array(items.reversed())) { item in
ContinueWatchingCell(item: item,
markAsWatched: {
markAsWatched(item)
},
removeItem: {
removeItem(item)
})
}
}
.padding(.horizontal, 20)
}
.frame(height: 190)
}
}
}
struct ContinueWatchingCell: View {
let item: ContinueWatchingItem
var markAsWatched: () -> Void
var removeItem: () -> Void
var body: some View {
Button(action: {
if UserDefaults.standard.string(forKey: "externalPlayer") == "Sora" {
let customMediaPlayer = CustomMediaPlayerViewController(
module: item.module,
urlString: item.streamUrl,
fullUrl: item.fullUrl,
title: item.mediaTitle,
episodeNumber: item.episodeNumber,
onWatchNext: { },
subtitlesURL: item.subtitles,
episodeImageUrl: item.imageUrl
)
customMediaPlayer.modalPresentationStyle = .fullScreen
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let rootVC = windowScene.windows.first?.rootViewController {
findTopViewController.findViewController(rootVC).present(customMediaPlayer, animated: true, completion: nil)
}
} else {
let videoPlayerViewController = VideoPlayerViewController(module: item.module)
videoPlayerViewController.streamUrl = item.streamUrl
videoPlayerViewController.fullUrl = item.fullUrl
videoPlayerViewController.episodeImageUrl = item.imageUrl
videoPlayerViewController.episodeNumber = item.episodeNumber
videoPlayerViewController.mediaTitle = item.mediaTitle
videoPlayerViewController.subtitles = item.subtitles ?? ""
videoPlayerViewController.modalPresentationStyle = .fullScreen
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let rootVC = windowScene.windows.first?.rootViewController {
findTopViewController.findViewController(rootVC).present(videoPlayerViewController, animated: true, completion: nil)
}
}
}) {
VStack(alignment: .leading) {
ZStack {
KFImage(URL(string: item.imageUrl.isEmpty ? "https://raw.githubusercontent.com/cranci1/Sora/refs/heads/main/assets/banner2.png" : item.imageUrl))
.placeholder {
RoundedRectangle(cornerRadius: 10)
.fill(Color.gray.opacity(0.3))
.frame(width: 240, height: 135)
.shimmering()
}
.setProcessor(RoundCornerImageProcessor(cornerRadius: 10))
.resizable()
.aspectRatio(16/9, contentMode: .fill)
.frame(width: 240, height: 135)
.cornerRadius(10)
.clipped()
.overlay(
KFImage(URL(string: item.module.metadata.iconUrl))
.resizable()
.frame(width: 24, height: 24)
.cornerRadius(4)
.padding(4),
alignment: .topLeading
)
}
.overlay(
ZStack {
Rectangle()
.fill(Color.black.opacity(0.3))
.blur(radius: 3)
.frame(height: 30)
ProgressView(value: item.progress)
.progressViewStyle(LinearProgressViewStyle(tint: .white))
.padding(.horizontal, 8)
.scaleEffect(x: 1, y: 1.5, anchor: .center)
},
alignment: .bottom
)
VStack(alignment: .leading) {
Text("Episode \(item.episodeNumber)")
.font(.caption)
.lineLimit(1)
.foregroundColor(.secondary)
Text(item.mediaTitle)
.font(.caption)
.lineLimit(2)
.foregroundColor(.primary)
.multilineTextAlignment(.leading)
}
}
.frame(width: 240, height: 170)
}
.contextMenu {
Button(action: { markAsWatched() }) {
Label("Mark as Watched", systemImage: "checkmark.circle")
}
Button(role: .destructive, action: { removeItem() }) {
Label("Remove Item", systemImage: "trash")
}
}
}
}

View file

@ -167,10 +167,11 @@ struct MediaInfoView: View {
title: title,
imageUrl: imageUrl,
href: href,
moduleId: module.id.uuidString
moduleId: module.id.uuidString,
moduleName: module.metadata.sourceName
)
}) {
Image(systemName: libraryManager.isBookmarked(href: href) ? "bookmark.fill" : "bookmark")
Image(systemName: libraryManager.isBookmarked(href: href, moduleName: module.metadata.sourceName) ? "bookmark.fill" : "bookmark")
.resizable()
.frame(width: 20, height: 27)
.foregroundColor(Color.accentColor)

View file

@ -130,7 +130,7 @@ struct SearchView: View {
.navigationBarTitleDisplayMode(.large)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
HStack {
HStack(spacing: 4) {
if let selectedModule = selectedModule {
Text(selectedModule.metadata.sourceName)
.font(.headline)
@ -161,6 +161,8 @@ struct SearchView: View {
.foregroundColor(.secondary)
}
}
.id("moduleMenuHStack")
.fixedSize()
}
}
}

View file

@ -12,6 +12,8 @@ struct SettingsViewGeneral: View {
@AppStorage("refreshModulesOnLaunch") private var refreshModulesOnLaunch: Bool = false
@AppStorage("fetchEpisodeMetadata") private var fetchEpisodeMetadata: Bool = true
@AppStorage("analyticsEnabled") private var analyticsEnabled: Bool = false
@AppStorage("metadataProviders") private var metadataProviders: String = "AniList"
private let metadataProvidersList = ["AniList", "TMDB"]
@EnvironmentObject var settings: Settings
var body: some View {
@ -52,6 +54,20 @@ struct SettingsViewGeneral: View {
}
Toggle("Fetch Episode metadata", isOn: $fetchEpisodeMetadata)
.tint(.accentColor)
HStack {
Text("Metadata Provider")
Spacer()
Menu(metadataProviders) {
ForEach(metadataProvidersList, id: \.self) { provider in
Button(action: {
metadataProviders = provider
}) {
Text(provider)
}
}
}
}
}
Section(header: Text("Modules"), footer: Text("Note that the modules will be replaced only if there is a different version string inside the JSON file.")) {

View file

@ -1,51 +0,0 @@
//
// SettingsViewTrackingServices.swift
// Sulfur
//
// Created by Francesco on 05/03/25.
//
import SwiftUI
import Kingfisher
struct SettingsViewTrackingServices: View {
@AppStorage("trackingService") private var trackingService: String = "AniList"
@EnvironmentObject var settings: Settings
var body: some View {
Form {
Section(header: Text("Tracking Service")) {
HStack {
Text("Service")
Spacer()
Menu {
Button(action: { trackingService = "AniList" }) {
HStack {
KFImage(URL(string: "https://avatars.githubusercontent.com/u/18018524?s=280&v=4"))
.resizable()
.frame(width: 20, height: 20)
Text("AniList")
}
}
Button(action: { trackingService = "TMDB" }) {
HStack {
KFImage(URL(string: "https://pbs.twimg.com/profile_images/1243623122089041920/gVZIvphd_400x400.jpg"))
.resizable()
.frame(width: 20, height: 20)
Text("TMDB")
}
}
} label: {
HStack {
KFImage(URL(string: trackingService == "TMDB" ? "https://pbs.twimg.com/profile_images/1243623122089041920/gVZIvphd_400x400.jpg" : "https://avatars.githubusercontent.com/u/18018524?s=280&v=4"))
.resizable()
.frame(width: 20, height: 20)
Text(trackingService)
}
}
}
}
}
.navigationTitle("Tracking Service")
}
}

View file

@ -21,9 +21,6 @@ struct SettingsView: View {
NavigationLink(destination: SettingsViewModule()) {
Text("Modules")
}
NavigationLink(destination: SettingsViewTrackingServices()) {
Text("Tracking Services")
}
}
Section(header: Text("Info")) {
@ -49,6 +46,19 @@ struct SettingsView: View {
.foregroundColor(.secondary)
}
}
Button(action: {
if let url = URL(string: "https://discord.gg/x7hppDWFDZ") {
UIApplication.shared.open(url)
}
}) {
HStack {
Text("Join the Discord")
.foregroundColor(.primary)
Spacer()
Image(systemName: "safari")
.foregroundColor(.secondary)
}
}
Button(action: {
if let url = URL(string: "https://github.com/cranci1/Sora/issues") {
UIApplication.shared.open(url)
@ -63,12 +73,12 @@ struct SettingsView: View {
}
}
Button(action: {
if let url = URL(string: "https://discord.gg/x7hppDWFDZ") {
if let url = URL(string: "https://github.com/cranci1/Sora/blob/dev/LICENSE") {
UIApplication.shared.open(url)
}
}) {
HStack {
Text("Join the Discord")
Text("License (GPLv3.0)")
.foregroundColor(.primary)
Spacer()
Image(systemName: "safari")
@ -76,7 +86,7 @@ struct SettingsView: View {
}
}
}
Section(footer: Text("Running Sora 0.2.1")) {}
Section(footer: Text("Running Sora 0.2.1 - cranci1")) {}
}
.navigationTitle("Settings")
}

View file

@ -20,13 +20,11 @@
1334FF4F2D786C9E007E289F /* TMDB-Trending.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1334FF4E2D786C9E007E289F /* TMDB-Trending.swift */; };
1334FF522D7871B7007E289F /* TMDBItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1334FF512D7871B7007E289F /* TMDBItem.swift */; };
1334FF542D787217007E289F /* TMDBRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1334FF532D787217007E289F /* TMDBRequest.swift */; };
1334FF562D7872E9007E289F /* SettingsViewTrackingServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1334FF552D7872E9007E289F /* SettingsViewTrackingServices.swift */; };
133D7C6E2D2BE2500075467E /* SoraApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C6D2D2BE2500075467E /* SoraApp.swift */; };
133D7C702D2BE2500075467E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C6F2D2BE2500075467E /* ContentView.swift */; };
133D7C722D2BE2520075467E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 133D7C712D2BE2520075467E /* Assets.xcassets */; };
133D7C752D2BE2520075467E /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 133D7C742D2BE2520075467E /* Preview Assets.xcassets */; };
133D7C8C2D2BE2640075467E /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C7C2D2BE2630075467E /* SearchView.swift */; };
133D7C8D2D2BE2640075467E /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C7D2D2BE2630075467E /* HomeView.swift */; };
133D7C8E2D2BE2640075467E /* LibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C7E2D2BE2630075467E /* LibraryView.swift */; };
133D7C8F2D2BE2640075467E /* MediaInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C802D2BE2630075467E /* MediaInfoView.swift */; };
133D7C902D2BE2640075467E /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C822D2BE2630075467E /* SettingsView.swift */; };
@ -77,14 +75,12 @@
1334FF4E2D786C9E007E289F /* TMDB-Trending.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TMDB-Trending.swift"; sourceTree = "<group>"; };
1334FF512D7871B7007E289F /* TMDBItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TMDBItem.swift; sourceTree = "<group>"; };
1334FF532D787217007E289F /* TMDBRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TMDBRequest.swift; sourceTree = "<group>"; };
1334FF552D7872E9007E289F /* SettingsViewTrackingServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewTrackingServices.swift; sourceTree = "<group>"; };
133D7C6A2D2BE2500075467E /* Sulfur.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sulfur.app; sourceTree = BUILT_PRODUCTS_DIR; };
133D7C6D2D2BE2500075467E /* SoraApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoraApp.swift; sourceTree = "<group>"; };
133D7C6F2D2BE2500075467E /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
133D7C712D2BE2520075467E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
133D7C742D2BE2520075467E /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
133D7C7C2D2BE2630075467E /* SearchView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = "<group>"; };
133D7C7D2D2BE2630075467E /* HomeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
133D7C7E2D2BE2630075467E /* LibraryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LibraryView.swift; sourceTree = "<group>"; };
133D7C802D2BE2630075467E /* MediaInfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaInfoView.swift; sourceTree = "<group>"; };
133D7C822D2BE2630075467E /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
@ -260,7 +256,6 @@
1399FAD22D3AB34F00E97C31 /* SettingsView */,
133F55B92D33B53E00E08EEA /* LibraryView */,
133D7C7C2D2BE2630075467E /* SearchView.swift */,
133D7C7D2D2BE2630075467E /* HomeView.swift */,
);
path = Views;
sourceTree = "<group>";
@ -283,7 +278,6 @@
131845F82D47C62D00CA7A54 /* SettingsViewGeneral.swift */,
135CCBE12D4D1138008B9C0E /* SettingsViewPlayer.swift */,
130C6BF92D53AB1F00DC1432 /* SettingsViewData.swift */,
1334FF552D7872E9007E289F /* SettingsViewTrackingServices.swift */,
);
path = SettingsSubViews;
sourceTree = "<group>";
@ -525,7 +519,6 @@
1E9FF1D32D403E49008AC100 /* SettingsViewLoggerFilter.swift in Sources */,
13EA2BD92D32D98400C1EBD7 /* NormalPlayer.swift in Sources */,
133D7C932D2BE2640075467E /* Modules.swift in Sources */,
1334FF562D7872E9007E289F /* SettingsViewTrackingServices.swift in Sources */,
136F21B92D5B8DD8006409AC /* AniList-MediaInfo.swift in Sources */,
13DB7CC32D7D99C0004371D3 /* SubtitleSettingsManager.swift in Sources */,
133D7C702D2BE2500075467E /* ContentView.swift in Sources */,
@ -539,7 +532,6 @@
13103E892D58A39A000F0673 /* AniListItem.swift in Sources */,
131845F92D47C62D00CA7A54 /* SettingsViewGeneral.swift in Sources */,
13103E8E2D58E04A000F0673 /* SkeletonCell.swift in Sources */,
133D7C8D2D2BE2640075467E /* HomeView.swift in Sources */,
13D842552D45267500EBBFA6 /* DropManager.swift in Sources */,
13103E8B2D58E028000F0673 /* View.swift in Sources */,
1327FBA92D758DEA00FC6689 /* UIDevice+Model.swift in Sources */,