IBH bug report fixes (Read description) (#197)
Some checks are pending
Build and Release / Build IPA (push) Waiting to run
Build and Release / Build Mac Catalyst (push) Waiting to run

This commit is contained in:
50/50 2025-06-15 21:33:38 +02:00 committed by GitHub
parent 019989df4f
commit 5ef6316036
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 139 additions and 87 deletions

View file

@ -5,9 +5,9 @@
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "1.000", "blue" : "0.000",
"green" : "1.000", "green" : "0.000",
"red" : "1.000" "red" : "0.000"
} }
}, },
"idiom" : "universal" "idiom" : "universal"
@ -23,9 +23,9 @@
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "0.000", "blue" : "1.000",
"green" : "0.000", "green" : "1.000",
"red" : "0.000" "red" : "1.000"
} }
}, },
"idiom" : "universal" "idiom" : "universal"

View file

@ -7,6 +7,7 @@
import Drops import Drops
import UIKit import UIKit
import SwiftUI
class DropManager { class DropManager {
static let shared = DropManager() static let shared = DropManager()
@ -59,7 +60,8 @@ class DropManager {
} }
func info(_ message: String, duration: TimeInterval = 2.0) { func info(_ message: String, duration: TimeInterval = 2.0) {
let icon = UIImage(systemName: "info.circle.fill")?.withTintColor(.blue, renderingMode: .alwaysOriginal) let accentColor = UIColor(Color.accentColor)
let icon = UIImage(systemName: "info.circle.fill")?.withTintColor(accentColor, renderingMode: .alwaysOriginal)
showDrop(title: "Info", subtitle: message, duration: duration, icon: icon) showDrop(title: "Info", subtitle: message, duration: duration, icon: icon)
} }

View file

@ -41,7 +41,7 @@ struct ModuleAdditionSettingsView: View {
} }
.padding(.bottom, 8) .padding(.bottom, 8)
ScrollView { ScrollView(showsIndicators: false) {
VStack(spacing: 24) { VStack(spacing: 24) {
if let metadata = moduleMetadata { if let metadata = moduleMetadata {
VStack(spacing: 0) { VStack(spacing: 0) {

View file

@ -79,7 +79,7 @@ struct DownloadView: View {
if jsController.activeDownloads.isEmpty && jsController.downloadQueue.isEmpty { if jsController.activeDownloads.isEmpty && jsController.downloadQueue.isEmpty {
emptyActiveDownloadsView emptyActiveDownloadsView
} else { } else {
ScrollView { ScrollView(showsIndicators: false) {
VStack(spacing: 20) { VStack(spacing: 20) {
if !jsController.downloadQueue.isEmpty { if !jsController.downloadQueue.isEmpty {
DownloadSectionView( DownloadSectionView(
@ -109,7 +109,7 @@ struct DownloadView: View {
if filteredAndSortedAssets.isEmpty { if filteredAndSortedAssets.isEmpty {
emptyDownloadsView emptyDownloadsView
} else { } else {
ScrollView { ScrollView(showsIndicators: false) {
VStack(spacing: 20) { VStack(spacing: 20) {
DownloadSummaryCard( DownloadSummaryCard(
totalShows: groupedAssets.count, totalShows: groupedAssets.count,
@ -888,7 +888,7 @@ struct EnhancedDownloadGroupCard: View {
.foregroundStyle(.primary) .foregroundStyle(.primary)
HStack(spacing: 16) { HStack(spacing: 16) {
Label("\(group.assetCount)", systemImage: "play.rectangle") Label("\(group.assetCount) \(group.assetCount == 1 ? "Episode" : "Episodes")", systemImage: "play.rectangle")
.font(.subheadline) .font(.subheadline)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
@ -956,7 +956,7 @@ struct EnhancedShowEpisodesView: View {
} }
var body: some View { var body: some View {
ScrollView { ScrollView(showsIndicators: false) {
VStack(spacing: 24) { VStack(spacing: 24) {
VStack(spacing: 20) { VStack(spacing: 20) {
HStack(alignment: .top, spacing: 20) { HStack(alignment: .top, spacing: 20) {
@ -997,7 +997,7 @@ struct EnhancedShowEpisodesView: View {
HStack { HStack {
Image(systemName: "play.rectangle.fill") Image(systemName: "play.rectangle.fill")
.foregroundColor(.accentColor) .foregroundColor(.accentColor)
Text("\(group.assetCount) Episodes") Text("\(group.assetCount) \(group.assetCount == 1 ? "Episode" : "Episodes")")
.font(.headline) .font(.headline)
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }

View file

@ -105,7 +105,7 @@ struct AllWatchingView: View {
.padding(.horizontal) .padding(.horizontal)
.padding(.top) .padding(.top)
ScrollView { ScrollView(showsIndicators: false) {
LazyVStack(spacing: 12) { LazyVStack(spacing: 12) {
ForEach(sortedItems) { item in ForEach(sortedItems) { item in
FullWidthContinueWatchingCell( FullWidthContinueWatchingCell(

View file

@ -16,7 +16,7 @@ struct BookmarkGridView: View {
] ]
var body: some View { var body: some View {
ScrollView { ScrollView(showsIndicators: false) {
LazyVGrid(columns: columns, spacing: 16) { LazyVGrid(columns: columns, spacing: 16) {
ForEach(bookmarks) { bookmark in ForEach(bookmarks) { bookmark in
BookmarkGridItemView( BookmarkGridItemView(

View file

@ -109,7 +109,7 @@ private struct BookmarksDetailGrid: View {
let moduleManager: ModuleManager let moduleManager: ModuleManager
private let columns = [GridItem(.adaptive(minimum: 150))] private let columns = [GridItem(.adaptive(minimum: 150))]
var body: some View { var body: some View {
ScrollView { ScrollView(showsIndicators: false) {
LazyVGrid(columns: columns, spacing: 16) { LazyVGrid(columns: columns, spacing: 16) {
ForEach(bookmarks) { bookmark in ForEach(bookmarks) { bookmark in
BookmarksDetailGridCell(bookmark: bookmark, moduleManager: moduleManager) BookmarksDetailGridCell(bookmark: bookmark, moduleManager: moduleManager)

View file

@ -56,7 +56,7 @@ struct LibraryView: View {
var body: some View { var body: some View {
NavigationView { NavigationView {
ZStack { ZStack {
ScrollView { ScrollView(showsIndicators: false) {
VStack(alignment: .leading, spacing: 20) { VStack(alignment: .leading, spacing: 20) {
Text("Library") Text("Library")
.font(.largeTitle) .font(.largeTitle)

View file

@ -29,7 +29,7 @@ struct AnilistMatchPopupView: View {
var body: some View { var body: some View {
NavigationView { NavigationView {
ScrollView { ScrollView(showsIndicators: false) {
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 4) {
Text("".uppercased()) Text("".uppercased())
.font(.footnote) .font(.footnote)

View file

@ -38,7 +38,7 @@ struct TMDBMatchPopupView: View {
var body: some View { var body: some View {
NavigationView { NavigationView {
ScrollView { ScrollView(showsIndicators: false) {
VStack(spacing: 0) { VStack(spacing: 0) {
if isLoading { if isLoading {
ProgressView() ProgressView()

View file

@ -226,7 +226,7 @@ struct MediaInfoView: View {
@ViewBuilder @ViewBuilder
private var mainScrollView: some View { private var mainScrollView: some View {
ScrollView { ScrollView(showsIndicators: false) {
ZStack(alignment: .top) { ZStack(alignment: .top) {
heroImageSection heroImageSection
contentContainer contentContainer

View file

@ -12,6 +12,10 @@ struct SearchResultsGrid: View {
@AppStorage("mediaColumnsPortrait") private var mediaColumnsPortrait: Int = 2 @AppStorage("mediaColumnsPortrait") private var mediaColumnsPortrait: Int = 2
@AppStorage("mediaColumnsLandscape") private var mediaColumnsLandscape: Int = 4 @AppStorage("mediaColumnsLandscape") private var mediaColumnsLandscape: Int = 4
@Environment(\.verticalSizeClass) var verticalSizeClass @Environment(\.verticalSizeClass) var verticalSizeClass
@EnvironmentObject private var libraryManager: LibraryManager
@EnvironmentObject private var moduleManager: ModuleManager
@State private var showBookmarkToast: Bool = false
@State private var toastMessage: String = ""
let items: [SearchItem] let items: [SearchItem]
let columns: [GridItem] let columns: [GridItem]
@ -28,57 +32,100 @@ struct SearchResultsGrid: View {
} }
var body: some View { var body: some View {
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 12), count: columnsCount), spacing: 12) { ZStack {
ForEach(items) { item in LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 12), count: columnsCount), spacing: 12) {
NavigationLink(destination: MediaInfoView(title: item.title, imageUrl: item.imageUrl, href: item.href, module: selectedModule)) { ForEach(items) { item in
ZStack { NavigationLink(destination: MediaInfoView(title: item.title, imageUrl: item.imageUrl, href: item.href, module: selectedModule)) {
LazyImage(url: URL(string: item.imageUrl)) { state in ZStack {
if let uiImage = state.imageContainer?.image { LazyImage(url: URL(string: item.imageUrl)) { state in
Image(uiImage: uiImage) if let uiImage = state.imageContainer?.image {
.resizable() Image(uiImage: uiImage)
.aspectRatio(0.72, contentMode: .fill) .resizable()
.frame(width: cellWidth, height: cellWidth * 1.5) .aspectRatio(0.72, contentMode: .fill)
.cornerRadius(12) .frame(width: cellWidth, height: cellWidth * 1.5)
.clipped() .cornerRadius(12)
} else { .clipped()
Rectangle() } else {
.fill(.tertiary) Rectangle()
.frame(width: cellWidth, height: cellWidth * 1.5) .fill(.tertiary)
.cornerRadius(12) .frame(width: cellWidth, height: cellWidth * 1.5)
.clipped() .cornerRadius(12)
.clipped()
}
} }
}
VStack {
VStack {
Spacer()
HStack {
Text(item.title)
.lineLimit(2)
.foregroundColor(.white)
.multilineTextAlignment(.leading)
Spacer() Spacer()
} HStack {
.padding(12) Text(item.title)
.background( .lineLimit(2)
LinearGradient( .foregroundColor(.white)
colors: [ .multilineTextAlignment(.leading)
.black.opacity(0.7), Spacer()
.black.opacity(0.0) }
], .padding(12)
startPoint: .bottom, .background(
endPoint: .top LinearGradient(
colors: [
.black.opacity(0.7),
.black.opacity(0.0)
],
startPoint: .bottom,
endPoint: .top
)
.shadow(color: .black, radius: 4, x: 0, y: 2)
) )
.shadow(color: .black, radius: 4, x: 0, y: 2) }
) .frame(width: cellWidth)
} }
.frame(width: cellWidth) .clipShape(RoundedRectangle(cornerRadius: 12))
.padding(4)
} }
.clipShape(RoundedRectangle(cornerRadius: 12)) .id(item.href)
.padding(4) .contextMenu {
}.id(item.href) Button(action: {
let isBookmarked = libraryManager.isBookmarked(href: item.href, moduleName: selectedModule.metadata.sourceName)
libraryManager.toggleBookmark(
title: item.title,
imageUrl: item.imageUrl,
href: item.href,
moduleId: selectedModule.id.uuidString,
moduleName: selectedModule.metadata.sourceName
)
DropManager.shared.showDrop(
title: isBookmarked ? "Removed from Bookmarks" : "Bookmarked!",
subtitle: item.title,
duration: 1.0,
icon: UIImage(systemName: isBookmarked ? "bookmark.slash" : "bookmark.fill")
)
}) {
Label(libraryManager.isBookmarked(href: item.href, moduleName: selectedModule.metadata.sourceName) ? "Remove Bookmark" : "Bookmark",
systemImage: libraryManager.isBookmarked(href: item.href, moduleName: selectedModule.metadata.sourceName) ? "bookmark.slash" : "bookmark")
}
}
}
}
.padding(.top)
.padding()
if showBookmarkToast {
VStack {
Spacer()
Text(toastMessage)
.font(.headline)
.padding()
.background(Color.black.opacity(0.8))
.foregroundColor(.white)
.cornerRadius(12)
.padding(.bottom, 40)
.transition(.move(edge: .bottom).combined(with: .opacity))
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) {
withAnimation { showBookmarkToast = false }
}
}
} }
} }
.padding(.top)
.padding()
} }
} }

View file

@ -93,7 +93,7 @@ struct SearchView: View {
.padding(.horizontal, 20) .padding(.horizontal, 20)
.padding(.top, 20) .padding(.top, 20)
ScrollView { ScrollView(showsIndicators: false) {
SearchContent( SearchContent(
selectedModule: selectedModule, selectedModule: selectedModule,
searchQuery: searchQuery, searchQuery: searchQuery,

View file

@ -60,7 +60,7 @@ fileprivate struct SettingsSection<Content: View>: View {
struct SettingsViewAbout: View { struct SettingsViewAbout: View {
var body: some View { var body: some View {
ScrollView { ScrollView(showsIndicators: false) {
VStack(spacing: 24) { VStack(spacing: 24) {
SettingsSection(title: "App Info", footer: "Sora/Sulfur will always remain free with no ads!") { SettingsSection(title: "App Info", footer: "Sora/Sulfur will always remain free with no ads!") {
HStack(alignment: .center, spacing: 16) { HStack(alignment: .center, spacing: 16) {

View file

@ -149,7 +149,7 @@ struct SettingsViewData: View {
@State private var activeAlert: ActiveAlert = .eraseData @State private var activeAlert: ActiveAlert = .eraseData
var body: some View { var body: some View {
return ScrollView { return ScrollView(showsIndicators: false) {
VStack(spacing: 24) { VStack(spacing: 24) {
SettingsSection( SettingsSection(
title: NSLocalizedString("App Storage", comment: ""), title: NSLocalizedString("App Storage", comment: ""),
@ -191,6 +191,7 @@ struct SettingsViewData: View {
} }
} }
} }
.padding(.vertical, 20)
.scrollViewBottomPadding() .scrollViewBottomPadding()
.navigationTitle(NSLocalizedString("App Data", comment: "")) .navigationTitle(NSLocalizedString("App Data", comment: ""))
.onAppear { .onAppear {

View file

@ -161,7 +161,7 @@ struct SettingsViewDownloads: View {
@State private var isCalculating: Bool = false @State private var isCalculating: Bool = false
var body: some View { var body: some View {
ScrollView { ScrollView(showsIndicators: false) {
VStack(spacing: 24) { VStack(spacing: 24) {
SettingsSection( SettingsSection(
title: String(localized: "Download Settings"), title: String(localized: "Download Settings"),
@ -309,9 +309,9 @@ struct SettingsViewDownloads: View {
} }
} }
.padding(.vertical, 20) .padding(.vertical, 20)
.scrollViewBottomPadding()
.navigationTitle(String(localized: "Downloads"))
} }
.navigationTitle(String(localized: "Downloads"))
.scrollViewBottomPadding()
.alert(String(localized: "Delete All Downloads"), isPresented: $showClearConfirmation) { .alert(String(localized: "Delete All Downloads"), isPresented: $showClearConfirmation) {
Button(String(localized: "Cancel"), role: .cancel) { } Button(String(localized: "Cancel"), role: .cancel) { }
Button(String(localized: "Delete All"), role: .destructive) { Button(String(localized: "Delete All"), role: .destructive) {

View file

@ -173,7 +173,7 @@ struct SettingsViewGeneral: View {
@State private var showRestartAlert = false @State private var showRestartAlert = false
var body: some View { var body: some View {
ScrollView { ScrollView(showsIndicators: false) {
VStack(spacing: 24) { VStack(spacing: 24) {
SettingsSection(title: NSLocalizedString("Interface", comment: "")) { SettingsSection(title: NSLocalizedString("Interface", comment: "")) {
SettingsPickerRow( SettingsPickerRow(
@ -347,8 +347,9 @@ struct SettingsViewGeneral: View {
) )
} }
} }
.navigationTitle("General") .padding(.vertical, 20)
.scrollViewBottomPadding() .scrollViewBottomPadding()
.navigationTitle("General")
} }
.navigationTitle(NSLocalizedString("General", comment: "")) .navigationTitle(NSLocalizedString("General", comment: ""))
.scrollViewBottomPadding() .scrollViewBottomPadding()

View file

@ -74,7 +74,7 @@ struct SettingsViewLogger: View {
} }
var body: some View { var body: some View {
ScrollView { ScrollView(showsIndicators: false) {
VStack(spacing: 24) { VStack(spacing: 24) {
SettingsSection(title: NSLocalizedString("Logs", comment: "")) { SettingsSection(title: NSLocalizedString("Logs", comment: "")) {
if isLoading { if isLoading {

View file

@ -177,7 +177,7 @@ struct SettingsViewLoggerFilter: View {
} }
var body: some View { var body: some View {
ScrollView { ScrollView(showsIndicators: false) {
VStack(spacing: 24) { VStack(spacing: 24) {
SettingsSection(title: NSLocalizedString("Log Types", comment: "")) { SettingsSection(title: NSLocalizedString("Log Types", comment: "")) {
ForEach($viewModel.filters) { $filter in ForEach($viewModel.filters) { $filter in

View file

@ -160,7 +160,7 @@ struct SettingsViewModule: View {
@State private var showLibrary = false @State private var showLibrary = false
var body: some View { var body: some View {
ScrollView { ScrollView(showsIndicators: false) {
VStack(spacing: 24) { VStack(spacing: 24) {
if moduleManager.modules.isEmpty { if moduleManager.modules.isEmpty {
SettingsSection(title: NSLocalizedString("Modules", comment: "")) { SettingsSection(title: NSLocalizedString("Modules", comment: "")) {

View file

@ -212,7 +212,7 @@ struct SettingsViewPlayer: View {
private let qualityOptions = VideoQualityPreference.allCases.map { $0.rawValue } private let qualityOptions = VideoQualityPreference.allCases.map { $0.rawValue }
var body: some View { var body: some View {
ScrollView { ScrollView(showsIndicators: false) {
VStack(spacing: 24) { VStack(spacing: 24) {
SettingsSection( SettingsSection(
title: NSLocalizedString("Media Player", comment: ""), title: NSLocalizedString("Media Player", comment: ""),

View file

@ -115,7 +115,7 @@ struct SettingsViewTrackers: View {
@State private var isTraktLoading: Bool = false @State private var isTraktLoading: Bool = false
var body: some View { var body: some View {
ScrollView { ScrollView(showsIndicators: false) {
VStack(spacing: 24) { VStack(spacing: 24) {
SettingsSection(title: NSLocalizedString("AniList", comment: "")) { SettingsSection(title: NSLocalizedString("AniList", comment: "")) {
VStack(spacing: 0) { VStack(spacing: 0) {

View file

@ -131,10 +131,11 @@ struct SettingsView: View {
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
@StateObject var settings = Settings() @StateObject var settings = Settings()
@EnvironmentObject var moduleManager: ModuleManager @EnvironmentObject var moduleManager: ModuleManager
@State private var isNavigationActive = false
var body: some View { var body: some View {
NavigationView { NavigationView {
ScrollView { ScrollView(showsIndicators: false) {
VStack(spacing: 24) { VStack(spacing: 24) {
Text("Settings") Text("Settings")
.font(.largeTitle) .font(.largeTitle)
@ -150,7 +151,7 @@ struct SettingsView: View {
.foregroundStyle(.gray) .foregroundStyle(.gray)
.padding(.horizontal, 20) .padding(.horizontal, 20)
NavigationLink(destination: SettingsViewModule()) { NavigationLink(destination: SettingsViewModule().navigationBarBackButtonHidden(false)) {
ModulePreviewRow() ModulePreviewRow()
} }
.padding(.horizontal, 20) .padding(.horizontal, 20)
@ -163,22 +164,22 @@ struct SettingsView: View {
.padding(.horizontal, 20) .padding(.horizontal, 20)
VStack(spacing: 0) { VStack(spacing: 0) {
NavigationLink(destination: SettingsViewGeneral()) { NavigationLink(destination: SettingsViewGeneral().navigationBarBackButtonHidden(false)) {
SettingsNavigationRow(icon: "gearshape", titleKey: "General Preferences") SettingsNavigationRow(icon: "gearshape", titleKey: "General Preferences")
} }
Divider().padding(.horizontal, 16) Divider().padding(.horizontal, 16)
NavigationLink(destination: SettingsViewPlayer()) { NavigationLink(destination: SettingsViewPlayer().navigationBarBackButtonHidden(false)) {
SettingsNavigationRow(icon: "play.circle", titleKey: "Video Player") SettingsNavigationRow(icon: "play.circle", titleKey: "Video Player")
} }
Divider().padding(.horizontal, 16) Divider().padding(.horizontal, 16)
NavigationLink(destination: SettingsViewDownloads()) { NavigationLink(destination: SettingsViewDownloads().navigationBarBackButtonHidden(false)) {
SettingsNavigationRow(icon: "arrow.down.circle", titleKey: "Downloads") SettingsNavigationRow(icon: "arrow.down.circle", titleKey: "Downloads")
} }
Divider().padding(.horizontal, 16) Divider().padding(.horizontal, 16)
NavigationLink(destination: SettingsViewTrackers()) { NavigationLink(destination: SettingsViewTrackers().navigationBarBackButtonHidden(false)) {
SettingsNavigationRow(icon: "square.stack.3d.up", titleKey: "Trackers") SettingsNavigationRow(icon: "square.stack.3d.up", titleKey: "Trackers")
} }
} }
@ -208,12 +209,12 @@ struct SettingsView: View {
.padding(.horizontal, 20) .padding(.horizontal, 20)
VStack(spacing: 0) { VStack(spacing: 0) {
NavigationLink(destination: SettingsViewData()) { NavigationLink(destination: SettingsViewData().navigationBarBackButtonHidden(false)) {
SettingsNavigationRow(icon: "folder", titleKey: "Data") SettingsNavigationRow(icon: "folder", titleKey: "Data")
} }
Divider().padding(.horizontal, 16) Divider().padding(.horizontal, 16)
NavigationLink(destination: SettingsViewLogger()) { NavigationLink(destination: SettingsViewLogger().navigationBarBackButtonHidden(false)) {
SettingsNavigationRow(icon: "doc.text", titleKey: "Logs") SettingsNavigationRow(icon: "doc.text", titleKey: "Logs")
} }
} }
@ -243,7 +244,7 @@ struct SettingsView: View {
.padding(.horizontal, 20) .padding(.horizontal, 20)
VStack(spacing: 0) { VStack(spacing: 0) {
NavigationLink(destination: SettingsViewAbout()) { NavigationLink(destination: SettingsViewAbout().navigationBarBackButtonHidden(false)) {
SettingsNavigationRow(icon: "info.circle", titleKey: "About Sora") SettingsNavigationRow(icon: "info.circle", titleKey: "About Sora")
} }
Divider().padding(.horizontal, 16) Divider().padding(.horizontal, 16)