Merge branch 'main' into chapter-support

This commit is contained in:
Ethan Pippin 2022-01-16 19:13:08 -07:00
commit 6b7f3672b2
24 changed files with 60 additions and 6 deletions

View File

@ -85,5 +85,6 @@ final class MainTabCoordinator: TabCoordinatable {
@ViewBuilder @ViewBuilder
func makeSettingsTab(isActive: Bool) -> some View { func makeSettingsTab(isActive: Bool) -> some View {
Image(systemName: "gearshape.fill") Image(systemName: "gearshape.fill")
.accessibilityLabel(L10n.settings)
} }
} }

View File

@ -17,7 +17,7 @@ extension BaseItemDto: PortraitImageStackable {
id ?? "no id" id ?? "no id"
} }
public func imageURLContsructor(maxWidth: Int) -> URL { public func imageURLConstructor(maxWidth: Int) -> URL {
switch self.itemType { switch self.itemType {
case .episode: case .episode:
return getSeriesPrimaryImage(maxWidth: maxWidth) return getSeriesPrimaryImage(maxWidth: maxWidth)

View File

@ -68,7 +68,7 @@ extension BaseItemPerson: PortraitImageStackable {
(id ?? "noid") + title + (subtitle ?? "nodescription") + blurHash + failureInitials (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) self.getImage(baseURL: SessionManager.main.currentLogin.server.currentURI, maxWidth: maxWidth)
} }

View File

@ -356,6 +356,8 @@ internal enum L10n {
internal static var system: String { return L10n.tr("Localizable", "system") } internal static var system: String { return L10n.tr("Localizable", "system") }
/// Tags /// Tags
internal static var tags: String { return L10n.tr("Localizable", "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 /// Try again
internal static var tryAgain: String { return L10n.tr("Localizable", "tryAgain") } internal static var tryAgain: String { return L10n.tr("Localizable", "tryAgain") }
/// TV Shows /// TV Shows

View File

@ -9,7 +9,7 @@
import Foundation import Foundation
public protocol PortraitImageStackable { public protocol PortraitImageStackable {
func imageURLContsructor(maxWidth: Int) -> URL func imageURLConstructor(maxWidth: Int) -> URL
var title: String { get } var title: String { get }
var subtitle: String? { get } var subtitle: String? { get }
var blurHash: String { get } var blurHash: String { get }

View File

@ -44,7 +44,8 @@ final class ConnectToServerViewModel: ViewModel {
return message return message
} }
func connectToServer(uri: String) { func connectToServer(uri: String, redirectCount: Int = 0) {
#if targetEnvironment(simulator) #if targetEnvironment(simulator)
var uri = uri var uri = uri
if uri == "localhost" { if uri == "localhost" {
@ -63,6 +64,27 @@ final class ConnectToServerViewModel: ViewModel {
case .finished: () case .finished: ()
case let .failure(error): case let .failure(error):
switch 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: case is SwiftfinStore.Errors:
let swiftfinError = error as! SwiftfinStore.Errors let swiftfinError = error as! SwiftfinStore.Errors
switch swiftfinError { switch swiftfinError {

View File

@ -37,6 +37,7 @@ struct ImageView: View {
Text(failureInitials) Text(failureInitials)
.font(.largeTitle) .font(.largeTitle)
.foregroundColor(.secondary) .foregroundColor(.secondary)
.accessibilityHidden(true)
} }
} }

View File

@ -34,6 +34,7 @@ struct EpisodeRowCard: View {
} }
} }
.padding(.top) .padding(.top)
.accessibilityIgnoresInvertColors()
VStack(alignment: .leading) { VStack(alignment: .leading) {
Text(episode.getEpisodeLocator() ?? "S-:E-") Text(episode.getEpisodeLocator() ?? "S-:E-")

View File

@ -25,6 +25,7 @@ struct EpisodesRowView<RowManager>: View where RowManager: EpisodesRowManager {
if onlyCurrentSeason { if onlyCurrentSeason {
if let currentSeason = Array(viewModel.seasonsEpisodes.keys).first(where: { $0.id == viewModel.item.id }) { if let currentSeason = Array(viewModel.seasonsEpisodes.keys).first(where: { $0.id == viewModel.item.id }) {
Text(currentSeason.name ?? L10n.noTitle) Text(currentSeason.name ?? L10n.noTitle)
.accessibility(addTraits: [.isHeader])
} }
} else { } else {
Menu { Menu {

View File

@ -21,6 +21,7 @@ struct PillHStackView<ItemType: PillStackable>: View {
.fontWeight(.semibold) .fontWeight(.semibold)
.padding(.top, 3) .padding(.top, 3)
.padding(.leading, 16) .padding(.leading, 16)
.accessibility(addTraits: [.isHeader])
ScrollView(.horizontal, showsIndicators: false) { ScrollView(.horizontal, showsIndicators: false) {
HStack { HStack {

View File

@ -43,11 +43,12 @@ struct PortraitImageHStackView<TopBarView: View, ItemType: PortraitImageStackabl
selectedAction(item) selectedAction(item)
} label: { } label: {
VStack(alignment: horizontalAlignment) { VStack(alignment: horizontalAlignment) {
ImageView(src: item.imageURLContsructor(maxWidth: Int(maxWidth)), ImageView(src: item.imageURLConstructor(maxWidth: Int(maxWidth)),
bh: item.blurHash, bh: item.blurHash,
failureInitials: item.failureInitials) failureInitials: item.failureInitials)
.portraitPoster(width: maxWidth) .portraitPoster(width: maxWidth)
.shadow(radius: 4, y: 2) .shadow(radius: 4, y: 2)
.accessibilityIgnoresInvertColors()
if item.showTitle { if item.showTitle {
Text(item.title) Text(item.title)

View File

@ -35,11 +35,12 @@ struct PortraitItemButton<ItemType: PortraitImageStackable>: View {
selectedAction(item) selectedAction(item)
} label: { } label: {
VStack(alignment: horizontalAlignment) { VStack(alignment: horizontalAlignment) {
ImageView(src: item.imageURLContsructor(maxWidth: Int(maxWidth)), ImageView(src: item.imageURLConstructor(maxWidth: Int(maxWidth)),
bh: item.blurHash, bh: item.blurHash,
failureInitials: item.failureInitials) failureInitials: item.failureInitials)
.portraitPoster(width: maxWidth) .portraitPoster(width: maxWidth)
.shadow(radius: 4, y: 2) .shadow(radius: 4, y: 2)
.accessibilityIgnoresInvertColors()
if item.showTitle { if item.showTitle {
Text(item.title) Text(item.title)

View File

@ -29,6 +29,7 @@ struct ContinueWatchingView: View {
ZStack { ZStack {
ImageView(src: item.getBackdropImage(maxWidth: 320), bh: item.getBackdropImageBlurHash()) ImageView(src: item.getBackdropImage(maxWidth: 320), bh: item.getBackdropImageBlurHash())
.frame(width: 320, height: 180) .frame(width: 320, height: 180)
.accessibilityIgnoresInvertColors()
HStack { HStack {
VStack { VStack {

View File

@ -62,6 +62,7 @@ struct HomeView: View {
.font(.title2) .font(.title2)
.fontWeight(.bold) .fontWeight(.bold)
.padding() .padding()
.accessibility(addTraits: [.isHeader])
} selectedAction: { item in } selectedAction: { item in
homeRouter.route(to: \.item, item) homeRouter.route(to: \.item, item)
} }
@ -73,6 +74,7 @@ struct HomeView: View {
.font(.title2) .font(.title2)
.fontWeight(.bold) .fontWeight(.bold)
.padding() .padding()
.accessibility(addTraits: [.isHeader])
} selectedAction: { item in } selectedAction: { item in
homeRouter.route(to: \.item, item) homeRouter.route(to: \.item, item)
} }
@ -85,6 +87,7 @@ struct HomeView: View {
Text(L10n.latestWithString(library.name ?? "")) Text(L10n.latestWithString(library.name ?? ""))
.font(.title2) .font(.title2)
.fontWeight(.bold) .fontWeight(.bold)
.accessibility(addTraits: [.isHeader])
Spacer() Spacer()
@ -127,6 +130,7 @@ struct HomeView: View {
homeRouter.route(to: \.settings) homeRouter.route(to: \.settings)
} label: { } label: {
Image(systemName: "gearshape.fill") Image(systemName: "gearshape.fill")
.accessibilityLabel(L10n.settings)
} }
} }
} }

View File

@ -55,6 +55,7 @@ struct ItemViewBody: View {
L10n.seasons.text L10n.seasons.text
.fontWeight(.semibold) .fontWeight(.semibold)
.padding() .padding()
.accessibility(addTraits: [.isHeader])
}, selectedAction: { season in }, selectedAction: { season in
itemRouter.route(to: \.item, season) itemRouter.route(to: \.item, season)
}) })
@ -113,6 +114,7 @@ struct ItemViewBody: View {
.fontWeight(.semibold) .fontWeight(.semibold)
.padding(.bottom) .padding(.bottom)
.padding(.horizontal) .padding(.horizontal)
.accessibility(addTraits: [.isHeader])
} selectedAction: { collectionItem in } selectedAction: { collectionItem in
itemRouter.route(to: \.item, collectionItem) itemRouter.route(to: \.item, collectionItem)
} }
@ -128,6 +130,7 @@ struct ItemViewBody: View {
.fontWeight(.semibold) .fontWeight(.semibold)
.padding(.bottom) .padding(.bottom)
.padding(.horizontal) .padding(.horizontal)
.accessibility(addTraits: [.isHeader])
}, },
selectedAction: { person in selectedAction: { person in
itemRouter.route(to: \.library, (viewModel: .init(person: person), title: person.title)) itemRouter.route(to: \.library, (viewModel: .init(person: person), title: person.title))
@ -144,6 +147,7 @@ struct ItemViewBody: View {
.fontWeight(.semibold) .fontWeight(.semibold)
.padding(.bottom) .padding(.bottom)
.padding(.horizontal) .padding(.horizontal)
.accessibility(addTraits: [.isHeader])
}, },
selectedAction: { item in selectedAction: { item in
itemRouter.route(to: \.item, item) itemRouter.route(to: \.item, item)

View File

@ -22,6 +22,7 @@ struct ItemViewDetailsView: View {
L10n.information.text L10n.information.text
.font(.title3) .font(.title3)
.fontWeight(.bold) .fontWeight(.bold)
.accessibility(addTraits: [.isHeader])
ForEach(viewModel.informationItems, id: \.self.title) { informationItem in ForEach(viewModel.informationItems, id: \.self.title) { informationItem in
VStack(alignment: .leading, spacing: 2) { VStack(alignment: .leading, spacing: 2) {
@ -31,6 +32,7 @@ struct ItemViewDetailsView: View {
.font(.subheadline) .font(.subheadline)
.foregroundColor(Color.secondary) .foregroundColor(Color.secondary)
} }
.accessibilityElement(children: .combine)
} }
} }
.padding(.bottom, 20) .padding(.bottom, 20)
@ -40,6 +42,7 @@ struct ItemViewDetailsView: View {
L10n.media.text L10n.media.text
.font(.title3) .font(.title3)
.fontWeight(.bold) .fontWeight(.bold)
.accessibility(addTraits: [.isHeader])
VStack(alignment: .leading, spacing: 2) { VStack(alignment: .leading, spacing: 2) {
L10n.file.text L10n.file.text
@ -48,6 +51,7 @@ struct ItemViewDetailsView: View {
.font(.subheadline) .font(.subheadline)
.foregroundColor(Color.secondary) .foregroundColor(Color.secondary)
} }
.accessibilityElement(children: .combine)
VStack(alignment: .leading, spacing: 2) { VStack(alignment: .leading, spacing: 2) {
L10n.containers.text L10n.containers.text
@ -56,6 +60,7 @@ struct ItemViewDetailsView: View {
.font(.subheadline) .font(.subheadline)
.foregroundColor(Color.secondary) .foregroundColor(Color.secondary)
} }
.accessibilityElement(children: .combine)
ForEach(viewModel.selectedVideoPlayerViewModel?.mediaItems ?? [], id: \.self.title) { mediaItem in ForEach(viewModel.selectedVideoPlayerViewModel?.mediaItems ?? [], id: \.self.title) { mediaItem in
VStack(alignment: .leading, spacing: 2) { VStack(alignment: .leading, spacing: 2) {
@ -65,6 +70,7 @@ struct ItemViewDetailsView: View {
.font(.subheadline) .font(.subheadline)
.foregroundColor(Color.secondary) .foregroundColor(Color.secondary)
} }
.accessibilityElement(children: .combine)
} }
} }
} }

View File

@ -28,6 +28,7 @@ struct ItemLandscapeMainView: View {
bh: viewModel.item.getPrimaryImageBlurHash()) bh: viewModel.item.getPrimaryImageBlurHash())
.frame(width: 130, height: 195) .frame(width: 130, height: 195)
.cornerRadius(10) .cornerRadius(10)
.accessibilityIgnoresInvertColors()
Spacer().frame(height: 15) Spacer().frame(height: 15)
@ -100,6 +101,7 @@ struct ItemLandscapeMainView: View {
.edgesIgnoringSafeArea(.all) .edgesIgnoringSafeArea(.all)
.blur(radius: 8) .blur(radius: 8)
.layoutPriority(-1) .layoutPriority(-1)
.accessibilityIgnoresInvertColors()
// iPadOS is making the view go all the way to the edge. // iPadOS is making the view go all the way to the edge.
// We have to accomodate this here // We have to accomodate this here

View File

@ -25,6 +25,7 @@ struct ItemLandscapeTopBarView: View {
.foregroundColor(.primary) .foregroundColor(.primary)
.padding(.leading, 16) .padding(.leading, 16)
.padding(.bottom, 10) .padding(.bottom, 10)
.accessibility(addTraits: [.isHeader])
// MARK: Details // MARK: Details

View File

@ -26,6 +26,7 @@ struct PortraitHeaderOverlayView: View {
ImageView(src: viewModel.item.portraitHeaderViewURL(maxWidth: 130)) ImageView(src: viewModel.item.portraitHeaderViewURL(maxWidth: 130))
.portraitPoster(width: 130) .portraitPoster(width: 130)
.accessibilityIgnoresInvertColors()
VStack(alignment: .leading, spacing: 1) { VStack(alignment: .leading, spacing: 1) {
Spacer() Spacer()

View File

@ -23,6 +23,7 @@ struct ItemPortraitMainView: View {
bh: viewModel.item.getBackdropImageBlurHash()) bh: viewModel.item.getBackdropImageBlurHash())
.opacity(0.4) .opacity(0.4)
.blur(radius: 2.0) .blur(radius: 2.0)
.accessibilityIgnoresInvertColors()
} }
// MARK: portraitStaticOverlayView // MARK: portraitStaticOverlayView

View File

@ -82,6 +82,7 @@ struct LibraryListView: View {
ZStack { ZStack {
ImageView(src: library.getPrimaryImage(maxWidth: 500), bh: library.getPrimaryImageBlurHash()) ImageView(src: library.getPrimaryImage(maxWidth: 500), bh: library.getPrimaryImageBlurHash())
.opacity(0.4) .opacity(0.4)
.accessibilityIgnoresInvertColors()
HStack { HStack {
Spacer() Spacer()
VStack { VStack {

View File

@ -104,6 +104,7 @@ struct ServerListView: View {
serverListRouter.route(to: \.basicAppSettings) serverListRouter.route(to: \.basicAppSettings)
} label: { } label: {
Image(systemName: "gearshape.fill") Image(systemName: "gearshape.fill")
.accessibilityLabel(L10n.settings)
} }
} }

View File

@ -102,6 +102,7 @@ class VLCPlayerViewController: UIViewController {
setupConstraints() setupConstraints()
view.backgroundColor = .black view.backgroundColor = .black
view.accessibilityIgnoresInvertColors = true
setupMediaPlayer(newViewModel: viewModel) setupMediaPlayer(newViewModel: viewModel)