Merge branch 'main' into chapter-support
This commit is contained in:
commit
6b7f3672b2
|
@ -85,5 +85,6 @@ final class MainTabCoordinator: TabCoordinatable {
|
|||
@ViewBuilder
|
||||
func makeSettingsTab(isActive: Bool) -> some View {
|
||||
Image(systemName: "gearshape.fill")
|
||||
.accessibilityLabel(L10n.settings)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ extension BaseItemDto: PortraitImageStackable {
|
|||
id ?? "no id"
|
||||
}
|
||||
|
||||
public func imageURLContsructor(maxWidth: Int) -> URL {
|
||||
public func imageURLConstructor(maxWidth: Int) -> URL {
|
||||
switch self.itemType {
|
||||
case .episode:
|
||||
return getSeriesPrimaryImage(maxWidth: maxWidth)
|
||||
|
|
|
@ -68,7 +68,7 @@ extension BaseItemPerson: PortraitImageStackable {
|
|||
(id ?? "noid") + title + (subtitle ?? "nodescription") + blurHash + failureInitials
|
||||
}
|
||||
|
||||
public func imageURLContsructor(maxWidth: Int) -> URL {
|
||||
public func imageURLConstructor(maxWidth: Int) -> URL {
|
||||
self.getImage(baseURL: SessionManager.main.currentLogin.server.currentURI, maxWidth: maxWidth)
|
||||
}
|
||||
|
||||
|
|
|
@ -356,6 +356,8 @@ internal enum L10n {
|
|||
internal static var system: String { return L10n.tr("Localizable", "system") }
|
||||
/// Tags
|
||||
internal static var tags: String { return L10n.tr("Localizable", "tags") }
|
||||
/// Too Many Redirects
|
||||
internal static var tooManyRedirects: String { return L10n.tr("Localizable", "tooManyRedirects") }
|
||||
/// Try again
|
||||
internal static var tryAgain: String { return L10n.tr("Localizable", "tryAgain") }
|
||||
/// TV Shows
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import Foundation
|
||||
|
||||
public protocol PortraitImageStackable {
|
||||
func imageURLContsructor(maxWidth: Int) -> URL
|
||||
func imageURLConstructor(maxWidth: Int) -> URL
|
||||
var title: String { get }
|
||||
var subtitle: String? { get }
|
||||
var blurHash: String { get }
|
||||
|
|
|
@ -44,7 +44,8 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
return message
|
||||
}
|
||||
|
||||
func connectToServer(uri: String) {
|
||||
func connectToServer(uri: String, redirectCount: Int = 0) {
|
||||
|
||||
#if targetEnvironment(simulator)
|
||||
var uri = uri
|
||||
if uri == "localhost" {
|
||||
|
@ -63,6 +64,27 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
case .finished: ()
|
||||
case let .failure(error):
|
||||
switch error {
|
||||
case is ErrorResponse:
|
||||
let errorResponse = error as! ErrorResponse
|
||||
switch errorResponse {
|
||||
case let .error(_, _, response, _):
|
||||
// a url in the response is the result if a redirect
|
||||
if let newURL = response?.url {
|
||||
if redirectCount > 2 {
|
||||
self.handleAPIRequestError(displayMessage: L10n.tooManyRedirects,
|
||||
logLevel: .critical,
|
||||
tag: "connectToServer",
|
||||
completion: completion)
|
||||
} else {
|
||||
self
|
||||
.connectToServer(uri: newURL.absoluteString
|
||||
.removeRegexMatches(pattern: "/web/index.html"),
|
||||
redirectCount: redirectCount + 1)
|
||||
}
|
||||
} else {
|
||||
self.handleAPIRequestError(completion: completion)
|
||||
}
|
||||
}
|
||||
case is SwiftfinStore.Errors:
|
||||
let swiftfinError = error as! SwiftfinStore.Errors
|
||||
switch swiftfinError {
|
||||
|
|
|
@ -37,6 +37,7 @@ struct ImageView: View {
|
|||
Text(failureInitials)
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(.secondary)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ struct EpisodeRowCard: View {
|
|||
}
|
||||
}
|
||||
.padding(.top)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
Text(episode.getEpisodeLocator() ?? "S-:E-")
|
||||
|
|
|
@ -25,6 +25,7 @@ struct EpisodesRowView<RowManager>: View where RowManager: EpisodesRowManager {
|
|||
if onlyCurrentSeason {
|
||||
if let currentSeason = Array(viewModel.seasonsEpisodes.keys).first(where: { $0.id == viewModel.item.id }) {
|
||||
Text(currentSeason.name ?? L10n.noTitle)
|
||||
.accessibility(addTraits: [.isHeader])
|
||||
}
|
||||
} else {
|
||||
Menu {
|
||||
|
|
|
@ -21,6 +21,7 @@ struct PillHStackView<ItemType: PillStackable>: View {
|
|||
.fontWeight(.semibold)
|
||||
.padding(.top, 3)
|
||||
.padding(.leading, 16)
|
||||
.accessibility(addTraits: [.isHeader])
|
||||
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack {
|
||||
|
|
|
@ -43,11 +43,12 @@ struct PortraitImageHStackView<TopBarView: View, ItemType: PortraitImageStackabl
|
|||
selectedAction(item)
|
||||
} label: {
|
||||
VStack(alignment: horizontalAlignment) {
|
||||
ImageView(src: item.imageURLContsructor(maxWidth: Int(maxWidth)),
|
||||
ImageView(src: item.imageURLConstructor(maxWidth: Int(maxWidth)),
|
||||
bh: item.blurHash,
|
||||
failureInitials: item.failureInitials)
|
||||
.portraitPoster(width: maxWidth)
|
||||
.shadow(radius: 4, y: 2)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
|
||||
if item.showTitle {
|
||||
Text(item.title)
|
||||
|
|
|
@ -35,11 +35,12 @@ struct PortraitItemButton<ItemType: PortraitImageStackable>: View {
|
|||
selectedAction(item)
|
||||
} label: {
|
||||
VStack(alignment: horizontalAlignment) {
|
||||
ImageView(src: item.imageURLContsructor(maxWidth: Int(maxWidth)),
|
||||
ImageView(src: item.imageURLConstructor(maxWidth: Int(maxWidth)),
|
||||
bh: item.blurHash,
|
||||
failureInitials: item.failureInitials)
|
||||
.portraitPoster(width: maxWidth)
|
||||
.shadow(radius: 4, y: 2)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
|
||||
if item.showTitle {
|
||||
Text(item.title)
|
||||
|
|
|
@ -29,6 +29,7 @@ struct ContinueWatchingView: View {
|
|||
ZStack {
|
||||
ImageView(src: item.getBackdropImage(maxWidth: 320), bh: item.getBackdropImageBlurHash())
|
||||
.frame(width: 320, height: 180)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
|
||||
HStack {
|
||||
VStack {
|
||||
|
|
|
@ -62,6 +62,7 @@ struct HomeView: View {
|
|||
.font(.title2)
|
||||
.fontWeight(.bold)
|
||||
.padding()
|
||||
.accessibility(addTraits: [.isHeader])
|
||||
} selectedAction: { item in
|
||||
homeRouter.route(to: \.item, item)
|
||||
}
|
||||
|
@ -73,6 +74,7 @@ struct HomeView: View {
|
|||
.font(.title2)
|
||||
.fontWeight(.bold)
|
||||
.padding()
|
||||
.accessibility(addTraits: [.isHeader])
|
||||
} selectedAction: { item in
|
||||
homeRouter.route(to: \.item, item)
|
||||
}
|
||||
|
@ -85,6 +87,7 @@ struct HomeView: View {
|
|||
Text(L10n.latestWithString(library.name ?? ""))
|
||||
.font(.title2)
|
||||
.fontWeight(.bold)
|
||||
.accessibility(addTraits: [.isHeader])
|
||||
|
||||
Spacer()
|
||||
|
||||
|
@ -127,6 +130,7 @@ struct HomeView: View {
|
|||
homeRouter.route(to: \.settings)
|
||||
} label: {
|
||||
Image(systemName: "gearshape.fill")
|
||||
.accessibilityLabel(L10n.settings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ struct ItemViewBody: View {
|
|||
L10n.seasons.text
|
||||
.fontWeight(.semibold)
|
||||
.padding()
|
||||
.accessibility(addTraits: [.isHeader])
|
||||
}, selectedAction: { season in
|
||||
itemRouter.route(to: \.item, season)
|
||||
})
|
||||
|
@ -113,6 +114,7 @@ struct ItemViewBody: View {
|
|||
.fontWeight(.semibold)
|
||||
.padding(.bottom)
|
||||
.padding(.horizontal)
|
||||
.accessibility(addTraits: [.isHeader])
|
||||
} selectedAction: { collectionItem in
|
||||
itemRouter.route(to: \.item, collectionItem)
|
||||
}
|
||||
|
@ -128,6 +130,7 @@ struct ItemViewBody: View {
|
|||
.fontWeight(.semibold)
|
||||
.padding(.bottom)
|
||||
.padding(.horizontal)
|
||||
.accessibility(addTraits: [.isHeader])
|
||||
},
|
||||
selectedAction: { person in
|
||||
itemRouter.route(to: \.library, (viewModel: .init(person: person), title: person.title))
|
||||
|
@ -144,6 +147,7 @@ struct ItemViewBody: View {
|
|||
.fontWeight(.semibold)
|
||||
.padding(.bottom)
|
||||
.padding(.horizontal)
|
||||
.accessibility(addTraits: [.isHeader])
|
||||
},
|
||||
selectedAction: { item in
|
||||
itemRouter.route(to: \.item, item)
|
||||
|
|
|
@ -22,6 +22,7 @@ struct ItemViewDetailsView: View {
|
|||
L10n.information.text
|
||||
.font(.title3)
|
||||
.fontWeight(.bold)
|
||||
.accessibility(addTraits: [.isHeader])
|
||||
|
||||
ForEach(viewModel.informationItems, id: \.self.title) { informationItem in
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
|
@ -31,6 +32,7 @@ struct ItemViewDetailsView: View {
|
|||
.font(.subheadline)
|
||||
.foregroundColor(Color.secondary)
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
}
|
||||
.padding(.bottom, 20)
|
||||
|
@ -40,6 +42,7 @@ struct ItemViewDetailsView: View {
|
|||
L10n.media.text
|
||||
.font(.title3)
|
||||
.fontWeight(.bold)
|
||||
.accessibility(addTraits: [.isHeader])
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
L10n.file.text
|
||||
|
@ -48,6 +51,7 @@ struct ItemViewDetailsView: View {
|
|||
.font(.subheadline)
|
||||
.foregroundColor(Color.secondary)
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
L10n.containers.text
|
||||
|
@ -56,6 +60,7 @@ struct ItemViewDetailsView: View {
|
|||
.font(.subheadline)
|
||||
.foregroundColor(Color.secondary)
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
|
||||
ForEach(viewModel.selectedVideoPlayerViewModel?.mediaItems ?? [], id: \.self.title) { mediaItem in
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
|
@ -65,6 +70,7 @@ struct ItemViewDetailsView: View {
|
|||
.font(.subheadline)
|
||||
.foregroundColor(Color.secondary)
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ struct ItemLandscapeMainView: View {
|
|||
bh: viewModel.item.getPrimaryImageBlurHash())
|
||||
.frame(width: 130, height: 195)
|
||||
.cornerRadius(10)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
|
||||
Spacer().frame(height: 15)
|
||||
|
||||
|
@ -100,6 +101,7 @@ struct ItemLandscapeMainView: View {
|
|||
.edgesIgnoringSafeArea(.all)
|
||||
.blur(radius: 8)
|
||||
.layoutPriority(-1)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
|
||||
// iPadOS is making the view go all the way to the edge.
|
||||
// We have to accomodate this here
|
||||
|
|
|
@ -25,6 +25,7 @@ struct ItemLandscapeTopBarView: View {
|
|||
.foregroundColor(.primary)
|
||||
.padding(.leading, 16)
|
||||
.padding(.bottom, 10)
|
||||
.accessibility(addTraits: [.isHeader])
|
||||
|
||||
// MARK: Details
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ struct PortraitHeaderOverlayView: View {
|
|||
|
||||
ImageView(src: viewModel.item.portraitHeaderViewURL(maxWidth: 130))
|
||||
.portraitPoster(width: 130)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
|
||||
VStack(alignment: .leading, spacing: 1) {
|
||||
Spacer()
|
||||
|
|
|
@ -23,6 +23,7 @@ struct ItemPortraitMainView: View {
|
|||
bh: viewModel.item.getBackdropImageBlurHash())
|
||||
.opacity(0.4)
|
||||
.blur(radius: 2.0)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
}
|
||||
|
||||
// MARK: portraitStaticOverlayView
|
||||
|
|
|
@ -82,6 +82,7 @@ struct LibraryListView: View {
|
|||
ZStack {
|
||||
ImageView(src: library.getPrimaryImage(maxWidth: 500), bh: library.getPrimaryImageBlurHash())
|
||||
.opacity(0.4)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
HStack {
|
||||
Spacer()
|
||||
VStack {
|
||||
|
|
|
@ -104,6 +104,7 @@ struct ServerListView: View {
|
|||
serverListRouter.route(to: \.basicAppSettings)
|
||||
} label: {
|
||||
Image(systemName: "gearshape.fill")
|
||||
.accessibilityLabel(L10n.settings)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -102,6 +102,7 @@ class VLCPlayerViewController: UIViewController {
|
|||
setupConstraints()
|
||||
|
||||
view.backgroundColor = .black
|
||||
view.accessibilityIgnoresInvertColors = true
|
||||
|
||||
setupMediaPlayer(newViewModel: viewModel)
|
||||
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue