redo logging
This commit is contained in:
parent
37be6be200
commit
cf812fd9dd
|
@ -59,13 +59,13 @@ final class MainCoordinator: NavigationCoordinatable {
|
|||
|
||||
@objc
|
||||
func didSignIn() {
|
||||
LogManager.shared.log.info("Received `didSignIn` from SwiftfinNotificationCenter.")
|
||||
LogManager.log.info("Received `didSignIn` from SwiftfinNotificationCenter.")
|
||||
root(\.mainTab)
|
||||
}
|
||||
|
||||
@objc
|
||||
func didSignOut() {
|
||||
LogManager.shared.log.info("Received `didSignOut` from SwiftfinNotificationCenter.")
|
||||
LogManager.log.info("Received `didSignOut` from SwiftfinNotificationCenter.")
|
||||
root(\.serverList)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,22 +13,20 @@ struct ErrorMessage: Identifiable {
|
|||
|
||||
let code: Int
|
||||
let title: String
|
||||
let displayMessage: String
|
||||
let logConstructor: LogConstructor
|
||||
let message: String
|
||||
|
||||
// Chosen value such that if an error has this code, don't show the code to the UI
|
||||
// This was chosen because of its unlikelyhood to ever be used
|
||||
static let noShowErrorCode = -69420
|
||||
|
||||
var id: String {
|
||||
"\(code)\(title)\(logConstructor.message)"
|
||||
"\(code)\(title)\(message)"
|
||||
}
|
||||
|
||||
/// If the custom displayMessage is `nil`, it will be set to the given logConstructor's message
|
||||
init(code: Int, title: String, displayMessage: String?, logConstructor: LogConstructor) {
|
||||
init(code: Int, title: String, message: String) {
|
||||
self.code = code
|
||||
self.title = title
|
||||
self.displayMessage = displayMessage ?? logConstructor.message
|
||||
self.logConstructor = logConstructor
|
||||
self.message = message
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
//
|
||||
// Swiftfin is subject to the terms of the Mozilla Public
|
||||
// License, v2.0. If a copy of the MPL was not distributed with this
|
||||
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
|
||||
struct LogConstructor {
|
||||
var message: String
|
||||
let tag: String
|
||||
let level: LogLevel
|
||||
let function: String
|
||||
let file: String
|
||||
let line: UInt
|
||||
}
|
|
@ -9,127 +9,75 @@
|
|||
import Foundation
|
||||
import JellyfinAPI
|
||||
|
||||
/**
|
||||
The implementation of the network errors here are a temporary measure.
|
||||
It is very repetitive, messy, and doesn't fulfill the entire specification of "error reporting".
|
||||
|
||||
Needs to be replaced
|
||||
*/
|
||||
|
||||
enum NetworkError: Error {
|
||||
|
||||
/// For the case that the ErrorResponse object has a code of -1
|
||||
case URLError(response: ErrorResponse, displayMessage: String?, logConstructor: LogConstructor)
|
||||
case URLError(response: ErrorResponse, displayMessage: String?)
|
||||
|
||||
/// For the case that the ErrorRespones object has a code of -2
|
||||
case HTTPURLError(response: ErrorResponse, displayMessage: String?, logConstructor: LogConstructor)
|
||||
case HTTPURLError(response: ErrorResponse, displayMessage: String?)
|
||||
|
||||
/// For the case that the ErrorResponse object has a positive code
|
||||
case JellyfinError(response: ErrorResponse, displayMessage: String?, logConstructor: LogConstructor)
|
||||
case JellyfinError(response: ErrorResponse, displayMessage: String?)
|
||||
|
||||
var errorMessage: ErrorMessage {
|
||||
switch self {
|
||||
case let .URLError(response, displayMessage, logConstructor):
|
||||
return NetworkError.parseURLError(from: response, displayMessage: displayMessage, logConstructor: logConstructor)
|
||||
case let .HTTPURLError(response, displayMessage, logConstructor):
|
||||
return NetworkError.parseHTTPURLError(from: response, displayMessage: displayMessage, logConstructor: logConstructor)
|
||||
case let .JellyfinError(response, displayMessage, logConstructor):
|
||||
return NetworkError.parseJellyfinError(from: response, displayMessage: displayMessage, logConstructor: logConstructor)
|
||||
case let .URLError(response, displayMessage):
|
||||
return NetworkError.parseURLError(from: response, displayMessage: displayMessage)
|
||||
case let .HTTPURLError(response, displayMessage):
|
||||
return NetworkError.parseHTTPURLError(from: response, displayMessage: displayMessage)
|
||||
case let .JellyfinError(response, displayMessage):
|
||||
return NetworkError.parseJellyfinError(from: response, displayMessage: displayMessage)
|
||||
}
|
||||
}
|
||||
|
||||
func logMessage() {
|
||||
let logConstructor = errorMessage.logConstructor
|
||||
let logFunction: (@autoclosure () -> String, String, String, String, UInt) -> Void
|
||||
|
||||
switch logConstructor.level {
|
||||
case .trace:
|
||||
logFunction = LogManager.log.trace
|
||||
case .debug:
|
||||
logFunction = LogManager.log.debug
|
||||
case .information:
|
||||
logFunction = LogManager.log.info
|
||||
case .warning:
|
||||
logFunction = LogManager.log.warning
|
||||
case .error:
|
||||
logFunction = LogManager.log.error
|
||||
case .critical:
|
||||
logFunction = LogManager.log.critical
|
||||
case ._none:
|
||||
logFunction = LogManager.log.debug
|
||||
}
|
||||
|
||||
logFunction(logConstructor.message, logConstructor.tag, logConstructor.function, logConstructor.file, logConstructor.line)
|
||||
}
|
||||
|
||||
private static func parseURLError(from response: ErrorResponse, displayMessage: String?,
|
||||
logConstructor: LogConstructor) -> ErrorMessage
|
||||
{
|
||||
|
||||
private static func parseURLError(from response: ErrorResponse, displayMessage: String?) -> ErrorMessage {
|
||||
let errorMessage: ErrorMessage
|
||||
var logMessage = L10n.unknownError
|
||||
var logConstructor = logConstructor
|
||||
|
||||
switch response {
|
||||
case let .error(_, _, _, err):
|
||||
|
||||
// These codes are currently referenced from:
|
||||
// Code references:
|
||||
// https://developer.apple.com/documentation/foundation/1508628-url_loading_system_error_codes
|
||||
switch err._code {
|
||||
case -1001:
|
||||
logMessage = L10n.networkTimedOut
|
||||
logConstructor.message = logMessage
|
||||
errorMessage = ErrorMessage(code: err._code,
|
||||
title: L10n.error,
|
||||
displayMessage: displayMessage,
|
||||
logConstructor: logConstructor)
|
||||
message: L10n.networkTimedOut)
|
||||
case -1003:
|
||||
errorMessage = ErrorMessage(code: err._code,
|
||||
title: L10n.error,
|
||||
message: L10n.unableToFindHost)
|
||||
case -1004:
|
||||
logMessage = L10n.cannotConnectToHost
|
||||
logConstructor.message = logMessage
|
||||
errorMessage = ErrorMessage(code: err._code,
|
||||
title: L10n.error,
|
||||
displayMessage: displayMessage,
|
||||
logConstructor: logConstructor)
|
||||
message: L10n.cannotConnectToHost)
|
||||
default:
|
||||
logConstructor.message = logMessage
|
||||
errorMessage = ErrorMessage(code: err._code,
|
||||
title: L10n.error,
|
||||
displayMessage: displayMessage,
|
||||
logConstructor: logConstructor)
|
||||
message: L10n.unknownError)
|
||||
}
|
||||
}
|
||||
|
||||
return errorMessage
|
||||
}
|
||||
|
||||
private static func parseHTTPURLError(from response: ErrorResponse, displayMessage: String?,
|
||||
logConstructor: LogConstructor) -> ErrorMessage
|
||||
{
|
||||
|
||||
private static func parseHTTPURLError(from response: ErrorResponse, displayMessage: String?) -> ErrorMessage {
|
||||
let errorMessage: ErrorMessage
|
||||
let logMessage = "An HTTP URL error has occurred"
|
||||
var logConstructor = logConstructor
|
||||
|
||||
// Not implemented as has not run into one of these errors as time of writing
|
||||
switch response {
|
||||
case .error:
|
||||
logConstructor.message = logMessage
|
||||
errorMessage = ErrorMessage(code: 0,
|
||||
title: L10n.error,
|
||||
displayMessage: displayMessage,
|
||||
logConstructor: logConstructor)
|
||||
message: "An HTTP URL error has occurred")
|
||||
}
|
||||
|
||||
return errorMessage
|
||||
}
|
||||
|
||||
private static func parseJellyfinError(from response: ErrorResponse, displayMessage: String?,
|
||||
logConstructor: LogConstructor) -> ErrorMessage
|
||||
{
|
||||
|
||||
private static func parseJellyfinError(from response: ErrorResponse, displayMessage: String?) -> ErrorMessage {
|
||||
let errorMessage: ErrorMessage
|
||||
var logMessage = L10n.unknownError
|
||||
var logConstructor = logConstructor
|
||||
|
||||
switch response {
|
||||
case let .error(code, _, _, _):
|
||||
|
@ -137,18 +85,13 @@ enum NetworkError: Error {
|
|||
// Generic HTTP status codes
|
||||
switch code {
|
||||
case 401:
|
||||
logMessage = L10n.unauthorizedUser
|
||||
logConstructor.message = logMessage
|
||||
errorMessage = ErrorMessage(code: code,
|
||||
title: L10n.unauthorized,
|
||||
displayMessage: displayMessage,
|
||||
logConstructor: logConstructor)
|
||||
message: L10n.unauthorizedUser)
|
||||
default:
|
||||
logConstructor.message = logMessage
|
||||
errorMessage = ErrorMessage(code: code,
|
||||
title: L10n.error,
|
||||
displayMessage: displayMessage,
|
||||
logConstructor: logConstructor)
|
||||
message: L10n.unknownError)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -169,7 +169,7 @@ extension BaseItemDto {
|
|||
|
||||
func createLiveTVVideoPlayerViewModel() -> AnyPublisher<[VideoPlayerViewModel], Error> {
|
||||
|
||||
LogManager.shared.log.debug("Creating liveTV video player view model for item: \(id ?? "")")
|
||||
LogManager.log.debug("Creating liveTV video player view model for item: \(id ?? "")")
|
||||
|
||||
let builder = DeviceProfileBuilder()
|
||||
// TODO: fix bitrate settings
|
||||
|
|
|
@ -390,6 +390,8 @@ internal enum L10n {
|
|||
internal static var tvShows: String { return L10n.tr("Localizable", "tvShows") }
|
||||
/// Unable to connect to server
|
||||
internal static var unableToConnectServer: String { return L10n.tr("Localizable", "unableToConnectServer") }
|
||||
/// Unable to find host
|
||||
internal static var unableToFindHost: String { return L10n.tr("Localizable", "unableToFindHost") }
|
||||
/// Unaired
|
||||
internal static var unaired: String { return L10n.tr("Localizable", "unaired") }
|
||||
/// Unauthorized
|
||||
|
|
|
@ -10,44 +10,41 @@ import Foundation
|
|||
import Puppy
|
||||
|
||||
class LogManager {
|
||||
|
||||
static let log = Puppy()
|
||||
|
||||
static func setup() {
|
||||
|
||||
let logsDirectory = getDocumentsDirectory().appendingPathComponent("logs", isDirectory: true)
|
||||
|
||||
do {
|
||||
try FileManager.default.createDirectory(atPath: logsDirectory.path,
|
||||
withIntermediateDirectories: true,
|
||||
attributes: nil)
|
||||
} catch {
|
||||
// logs directory already created
|
||||
}
|
||||
|
||||
let logFileURL = logsDirectory.appendingPathComponent("swiftfin_log.log")
|
||||
|
||||
let fileRotationLogger = try! FileRotationLogger("org.jellyfin.swiftfin.logger.file-rotation",
|
||||
fileURL: logFileURL)
|
||||
fileRotationLogger.suffixExtension = .numbering
|
||||
fileRotationLogger.maxFileSize = 10 * 1024
|
||||
fileRotationLogger.maxArchivedFilesCount = 5
|
||||
fileRotationLogger.format = LogFormatter()
|
||||
|
||||
let consoleLogger = ConsoleLogger("org.jellyfin.swiftfin.logger.console")
|
||||
consoleLogger.format = LogFormatter()
|
||||
|
||||
log.add(fileRotationLogger, withLevel: .debug)
|
||||
log.add(consoleLogger, withLevel: .debug)
|
||||
}
|
||||
|
||||
private static func getDocumentsDirectory() -> URL {
|
||||
// find all possible documents directories for this user
|
||||
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
|
||||
|
||||
// just send back the first one, which ought to be the only one
|
||||
return paths[0]
|
||||
}
|
||||
static let log = Puppy()
|
||||
|
||||
static func setup() {
|
||||
|
||||
let logsDirectory = getDocumentsDirectory().appendingPathComponent("logs", isDirectory: true)
|
||||
|
||||
do {
|
||||
try FileManager.default.createDirectory(atPath: logsDirectory.path,
|
||||
withIntermediateDirectories: true,
|
||||
attributes: nil)
|
||||
} catch {
|
||||
// logs directory already created
|
||||
}
|
||||
|
||||
let logFileURL = logsDirectory.appendingPathComponent("swiftfin_log.log")
|
||||
|
||||
let fileRotationLogger = try! FileRotationLogger("org.jellyfin.swiftfin.logger.file-rotation",
|
||||
fileURL: logFileURL)
|
||||
fileRotationLogger.format = LogFormatter()
|
||||
|
||||
let consoleLogger = ConsoleLogger("org.jellyfin.swiftfin.logger.console")
|
||||
consoleLogger.format = LogFormatter()
|
||||
|
||||
log.add(fileRotationLogger, withLevel: .debug)
|
||||
log.add(consoleLogger, withLevel: .debug)
|
||||
}
|
||||
|
||||
private static func getDocumentsDirectory() -> URL {
|
||||
// find all possible documents directories for this user
|
||||
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
|
||||
|
||||
// just send back the first one, which ought to be the only one
|
||||
return paths[0]
|
||||
}
|
||||
}
|
||||
|
||||
class LogFormatter: LogFormattable {
|
||||
|
|
|
@ -104,7 +104,7 @@ final class SessionManager {
|
|||
[Where<SwiftfinStore.Models.StoredServer>("id == %@",
|
||||
newServer.id)])
|
||||
{
|
||||
throw SwiftfinStore.Errors.existingServer(existingServer.state)
|
||||
throw SwiftfinStore.Error.existingServer(existingServer.state)
|
||||
}
|
||||
|
||||
return (newServer, transaction)
|
||||
|
@ -210,7 +210,7 @@ final class SessionManager {
|
|||
[Where<SwiftfinStore.Models.StoredUser>("id == %@",
|
||||
newUser.id)])
|
||||
{
|
||||
throw SwiftfinStore.Errors.existingUser(existingUser.state)
|
||||
throw SwiftfinStore.Error.existingUser(existingUser.state)
|
||||
}
|
||||
|
||||
let newAccessToken = transaction.create(Into<SwiftfinStore.Models.StoredAccessToken>())
|
||||
|
|
|
@ -147,9 +147,9 @@ enum SwiftfinStore {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: Errors
|
||||
// MARK: Error
|
||||
|
||||
enum Errors {
|
||||
enum Error {
|
||||
case existingServer(State.Server)
|
||||
case existingUser(State.User)
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ enum SwiftfinStore {
|
|||
|
||||
// MARK: LocalizedError
|
||||
|
||||
extension SwiftfinStore.Errors: LocalizedError {
|
||||
extension SwiftfinStore.Error: LocalizedError {
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
|
|
|
@ -48,7 +48,7 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
|
||||
#if targetEnvironment(simulator)
|
||||
var uri = uri
|
||||
if uri == "localhost" {
|
||||
if uri == "http://localhost" || uri == "localhost" {
|
||||
uri = "http://localhost:8096"
|
||||
}
|
||||
#endif
|
||||
|
@ -71,10 +71,7 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
// 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)
|
||||
self.handleAPIRequestError(displayMessage: L10n.tooManyRedirects, completion: completion)
|
||||
} else {
|
||||
self
|
||||
.connectToServer(uri: newURL.absoluteString
|
||||
|
@ -85,21 +82,17 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
self.handleAPIRequestError(completion: completion)
|
||||
}
|
||||
}
|
||||
case is SwiftfinStore.Errors:
|
||||
let swiftfinError = error as! SwiftfinStore.Errors
|
||||
case is SwiftfinStore.Error:
|
||||
let swiftfinError = error as! SwiftfinStore.Error
|
||||
switch swiftfinError {
|
||||
case let .existingServer(server):
|
||||
self.addServerURIPayload = AddServerURIPayload(server: server, uri: uri)
|
||||
self.backAddServerURIPayload = AddServerURIPayload(server: server, uri: uri)
|
||||
default:
|
||||
self.handleAPIRequestError(displayMessage: L10n.unableToConnectServer, logLevel: .critical,
|
||||
tag: "connectToServer",
|
||||
completion: completion)
|
||||
self.handleAPIRequestError(displayMessage: L10n.unableToConnectServer, completion: completion)
|
||||
}
|
||||
default:
|
||||
self.handleAPIRequestError(displayMessage: L10n.unableToConnectServer, logLevel: .critical,
|
||||
tag: "connectToServer",
|
||||
completion: completion)
|
||||
self.handleAPIRequestError(displayMessage: L10n.unableToConnectServer, completion: completion)
|
||||
}
|
||||
}
|
||||
}, receiveValue: { server in
|
||||
|
@ -128,14 +121,11 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
func addURIToServer(addServerURIPayload: AddServerURIPayload) {
|
||||
SessionManager.main.addURIToServer(server: addServerURIPayload.server, uri: addServerURIPayload.uri)
|
||||
.sink { completion in
|
||||
self.handleAPIRequestError(displayMessage: L10n.unableToConnectServer, logLevel: .critical, tag: "connectToServer",
|
||||
completion: completion)
|
||||
self.handleAPIRequestError(displayMessage: L10n.unableToConnectServer, completion: completion)
|
||||
} receiveValue: { server in
|
||||
SessionManager.main.setServerCurrentURI(server: server, uri: addServerURIPayload.uri)
|
||||
.sink { completion in
|
||||
self.handleAPIRequestError(displayMessage: L10n.unableToConnectServer, logLevel: .critical,
|
||||
tag: "connectToServer",
|
||||
completion: completion)
|
||||
self.handleAPIRequestError(displayMessage: L10n.unableToConnectServer, completion: completion)
|
||||
} receiveValue: { _ in
|
||||
self.router?.dismissCoordinator()
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ final class SeasonItemViewModel: ItemViewModel, EpisodesRowManager {
|
|||
}
|
||||
|
||||
private func requestEpisodes() {
|
||||
LogManager.shared.log
|
||||
LogManager.log
|
||||
.debug("Getting episodes in season \(item.id!) (\(item.name!)) of show \(item.seriesId!) (\(item.seriesName!))")
|
||||
TvShowsAPI.getEpisodes(seriesId: item.seriesId ?? "", userId: SessionManager.main.currentLogin.user.id,
|
||||
fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people],
|
||||
|
@ -55,7 +55,7 @@ final class SeasonItemViewModel: ItemViewModel, EpisodesRowManager {
|
|||
}, receiveValue: { [weak self] response in
|
||||
guard let self = self else { return }
|
||||
self.episodes = response.items ?? []
|
||||
LogManager.shared.log.debug("Retrieved \(String(self.episodes.count)) episodes")
|
||||
LogManager.log.debug("Retrieved \(String(self.episodes.count)) episodes")
|
||||
|
||||
self.setNextUpInSeason()
|
||||
})
|
||||
|
@ -78,7 +78,7 @@ final class SeasonItemViewModel: ItemViewModel, EpisodesRowManager {
|
|||
!episode.unaired && !episode.missing && episode.seasonId ?? "" == self.item.id!
|
||||
}) {
|
||||
self.playButtonItem = nextUpItem
|
||||
LogManager.shared.log.debug("Nextup in season \(self.item.id!) (\(self.item.name!)): \(nextUpItem.id!)")
|
||||
LogManager.log.debug("Nextup in season \(self.item.id!) (\(self.item.name!)): \(nextUpItem.id!)")
|
||||
}
|
||||
|
||||
if self.playButtonItem == nil && !self.episodes.isEmpty {
|
||||
|
|
|
@ -31,13 +31,11 @@ final class UserSignInViewModel: ViewModel {
|
|||
|
||||
func login(username: String, password: String) {
|
||||
LogManager.log.debug("Attempting to login to server at \"\(server.currentURI)\"", tag: "login")
|
||||
LogManager.log.debug("username: \(username), password: \(password)", tag: "login")
|
||||
|
||||
SessionManager.main.loginUser(server: server, username: username, password: password)
|
||||
.trackActivity(loading)
|
||||
.sink { completion in
|
||||
self.handleAPIRequestError(displayMessage: L10n.unableToConnectServer, logLevel: .critical, tag: "login",
|
||||
completion: completion)
|
||||
self.handleAPIRequestError(displayMessage: L10n.unableToConnectServer, completion: completion)
|
||||
} receiveValue: { _ in
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
|
|
@ -585,7 +585,7 @@ extension VideoPlayerViewModel {
|
|||
.sink { completion in
|
||||
self.handleAPIRequestError(completion: completion)
|
||||
} receiveValue: { _ in
|
||||
LogManager.shared.log.debug("Stop report sent for item: \(self.item.id ?? "No ID")")
|
||||
LogManager.log.debug("Stop report sent for item: \(self.item.id ?? "No ID")")
|
||||
Notifications[.didSendStopReport].post(object: self.item.id)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
|
|
@ -25,55 +25,47 @@ class ViewModel: ObservableObject {
|
|||
loading.loading.assign(to: \.isLoading, on: self).store(in: &cancellables)
|
||||
}
|
||||
|
||||
func handleAPIRequestError(displayMessage: String? = nil, logLevel: LogLevel = .error, tag: String = "", function: String = #function,
|
||||
file: String = #file, line: UInt = #line, completion: Subscribers.Completion<Error>)
|
||||
{
|
||||
func handleAPIRequestError(displayMessage: String? = nil, completion: Subscribers.Completion<Error>) {
|
||||
switch completion {
|
||||
case .finished:
|
||||
self.errorMessage = nil
|
||||
case let .failure(error):
|
||||
let logConstructor = LogConstructor(message: "__NOTHING__", tag: tag, level: logLevel, function: function, file: file,
|
||||
line: line)
|
||||
|
||||
switch error {
|
||||
case is ErrorResponse:
|
||||
let networkError: NetworkError
|
||||
let errorResponse = error as! ErrorResponse
|
||||
|
||||
switch errorResponse {
|
||||
case .error(-1, _, _, _):
|
||||
networkError = .URLError(response: errorResponse, displayMessage: displayMessage, logConstructor: logConstructor)
|
||||
networkError = .URLError(response: errorResponse, displayMessage: displayMessage)
|
||||
// Use the errorResponse description for debugging, rather than the user-facing friendly description which may not be implemented
|
||||
LogManager.log
|
||||
.error("Request failed: URL request failed with error \(networkError.errorMessage.code): \(errorResponse.localizedDescription)")
|
||||
case .error(-2, _, _, _):
|
||||
networkError = .HTTPURLError(response: errorResponse, displayMessage: displayMessage, logConstructor: logConstructor)
|
||||
networkError = .HTTPURLError(response: errorResponse, displayMessage: displayMessage)
|
||||
LogManager.log
|
||||
.error("Request failed: HTTP URL request failed with description: \(errorResponse.localizedDescription)")
|
||||
default:
|
||||
networkError = .JellyfinError(response: errorResponse, displayMessage: displayMessage, logConstructor: logConstructor)
|
||||
networkError = .JellyfinError(response: errorResponse, displayMessage: displayMessage)
|
||||
// Able to use user-facing friendly description here since just HTTP status codes
|
||||
LogManager.log
|
||||
.error("Request failed: \(networkError.errorMessage.code) - \(networkError.errorMessage.title): \(networkError.errorMessage.logConstructor.message)\n\(error.localizedDescription)")
|
||||
.error("Request failed: \(networkError.errorMessage.code) - \(networkError.errorMessage.title): \(networkError.errorMessage.message)\n\(error.localizedDescription)")
|
||||
}
|
||||
|
||||
self.errorMessage = networkError.errorMessage
|
||||
|
||||
networkError.logMessage()
|
||||
|
||||
case is SwiftfinStore.Errors:
|
||||
let swiftfinError = error as! SwiftfinStore.Errors
|
||||
case is SwiftfinStore.Error:
|
||||
let swiftfinError = error as! SwiftfinStore.Error
|
||||
let errorMessage = ErrorMessage(code: ErrorMessage.noShowErrorCode,
|
||||
title: swiftfinError.title,
|
||||
displayMessage: swiftfinError.errorDescription ?? "",
|
||||
logConstructor: logConstructor)
|
||||
message: swiftfinError.errorDescription ?? "")
|
||||
self.errorMessage = errorMessage
|
||||
LogManager.log.error("Request failed: \(swiftfinError.errorDescription ?? "")")
|
||||
|
||||
default:
|
||||
let genericErrorMessage = ErrorMessage(code: ErrorMessage.noShowErrorCode,
|
||||
title: "Generic Error",
|
||||
displayMessage: error.localizedDescription,
|
||||
logConstructor: logConstructor)
|
||||
message: error.localizedDescription)
|
||||
self.errorMessage = genericErrorMessage
|
||||
LogManager.log.error("Request failed: Generic error - \(error.localizedDescription)")
|
||||
}
|
||||
|
|
|
@ -28,13 +28,13 @@ struct CinematicNextUpCardView: View {
|
|||
item.getSeriesThumbImage(maxWidth: 350),
|
||||
item.getSeriesBackdropImage(maxWidth: 350),
|
||||
])
|
||||
.frame(width: 350, height: 210)
|
||||
.frame(width: 350, height: 210)
|
||||
} else {
|
||||
ImageView([
|
||||
.init(url: item.getThumbImage(maxWidth: 350)),
|
||||
.init(url: item.getBackdropImage(maxWidth: 350), blurHash: item.getBackdropImageBlurHash()),
|
||||
])
|
||||
.frame(width: 350, height: 210)
|
||||
.frame(width: 350, height: 210)
|
||||
}
|
||||
|
||||
LinearGradient(colors: [.clear, .black],
|
||||
|
|
|
@ -29,13 +29,13 @@ struct CinematicResumeCardView: View {
|
|||
item.getSeriesThumbImage(maxWidth: 350),
|
||||
item.getSeriesBackdropImage(maxWidth: 350),
|
||||
])
|
||||
.frame(width: 350, height: 210)
|
||||
.frame(width: 350, height: 210)
|
||||
} else {
|
||||
ImageView([
|
||||
.init(url: item.getThumbImage(maxWidth: 350)),
|
||||
.init(url: item.getBackdropImage(maxWidth: 350), blurHash: item.getBackdropImageBlurHash()),
|
||||
])
|
||||
.frame(width: 350, height: 210)
|
||||
.frame(width: 350, height: 210)
|
||||
}
|
||||
|
||||
LinearGradient(colors: [.clear, .black],
|
||||
|
|
|
@ -62,18 +62,18 @@ struct tvOSLiveTVOverlay: View {
|
|||
SFSymbolButton(systemName: "chevron.left.circle", action: {
|
||||
viewModel.playerOverlayDelegate?.didSelectPlayPreviousItem()
|
||||
})
|
||||
.frame(maxWidth: 30, maxHeight: 30)
|
||||
.disabled(viewModel.previousItemVideoPlayerViewModel == nil)
|
||||
.foregroundColor(viewModel.nextItemVideoPlayerViewModel == nil ? .gray : .white)
|
||||
.frame(maxWidth: 30, maxHeight: 30)
|
||||
.disabled(viewModel.previousItemVideoPlayerViewModel == nil)
|
||||
.foregroundColor(viewModel.nextItemVideoPlayerViewModel == nil ? .gray : .white)
|
||||
}
|
||||
|
||||
if viewModel.shouldShowPlayNextItem {
|
||||
SFSymbolButton(systemName: "chevron.right.circle", action: {
|
||||
viewModel.playerOverlayDelegate?.didSelectPlayNextItem()
|
||||
})
|
||||
.frame(maxWidth: 30, maxHeight: 30)
|
||||
.disabled(viewModel.nextItemVideoPlayerViewModel == nil)
|
||||
.foregroundColor(viewModel.nextItemVideoPlayerViewModel == nil ? .gray : .white)
|
||||
.frame(maxWidth: 30, maxHeight: 30)
|
||||
.disabled(viewModel.nextItemVideoPlayerViewModel == nil)
|
||||
.foregroundColor(viewModel.nextItemVideoPlayerViewModel == nil ? .gray : .white)
|
||||
}
|
||||
|
||||
if viewModel.shouldShowAutoPlay {
|
||||
|
|
|
@ -62,18 +62,18 @@ struct tvOSVLCOverlay: View {
|
|||
SFSymbolButton(systemName: "chevron.left.circle", action: {
|
||||
viewModel.playerOverlayDelegate?.didSelectPlayPreviousItem()
|
||||
})
|
||||
.frame(maxWidth: 30, maxHeight: 30)
|
||||
.disabled(viewModel.previousItemVideoPlayerViewModel == nil)
|
||||
.foregroundColor(viewModel.nextItemVideoPlayerViewModel == nil ? .gray : .white)
|
||||
.frame(maxWidth: 30, maxHeight: 30)
|
||||
.disabled(viewModel.previousItemVideoPlayerViewModel == nil)
|
||||
.foregroundColor(viewModel.nextItemVideoPlayerViewModel == nil ? .gray : .white)
|
||||
}
|
||||
|
||||
if viewModel.shouldShowPlayNextItem {
|
||||
SFSymbolButton(systemName: "chevron.right.circle", action: {
|
||||
viewModel.playerOverlayDelegate?.didSelectPlayNextItem()
|
||||
})
|
||||
.frame(maxWidth: 30, maxHeight: 30)
|
||||
.disabled(viewModel.nextItemVideoPlayerViewModel == nil)
|
||||
.foregroundColor(viewModel.nextItemVideoPlayerViewModel == nil ? .gray : .white)
|
||||
.frame(maxWidth: 30, maxHeight: 30)
|
||||
.disabled(viewModel.nextItemVideoPlayerViewModel == nil)
|
||||
.foregroundColor(viewModel.nextItemVideoPlayerViewModel == nil ? .gray : .white)
|
||||
}
|
||||
|
||||
if viewModel.shouldShowAutoPlay {
|
||||
|
|
|
@ -313,6 +313,7 @@
|
|||
E10EAA51277BBCC4000269ED /* CGSizeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10EAA4E277BBCC4000269ED /* CGSizeExtensions.swift */; };
|
||||
E10EAA53277BBD17000269ED /* BaseItemDto+VideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10EAA52277BBD17000269ED /* BaseItemDto+VideoPlayerViewModel.swift */; };
|
||||
E10EAA54277BBD17000269ED /* BaseItemDto+VideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10EAA52277BBD17000269ED /* BaseItemDto+VideoPlayerViewModel.swift */; };
|
||||
E1101177281B1E8A006A3584 /* Puppy in Frameworks */ = {isa = PBXBuildFile; productRef = E1101176281B1E8A006A3584 /* Puppy */; };
|
||||
E111DE222790BB46008118A3 /* DetectBottomScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E111DE212790BB46008118A3 /* DetectBottomScrollView.swift */; };
|
||||
E11B1B6C2718CD68006DA3E8 /* JellyfinAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11B1B6B2718CD68006DA3E8 /* JellyfinAPIError.swift */; };
|
||||
E11B1B6D2718CD68006DA3E8 /* JellyfinAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11B1B6B2718CD68006DA3E8 /* JellyfinAPIError.swift */; };
|
||||
|
@ -327,9 +328,6 @@
|
|||
E1267D3E271A1F46003C492E /* PreferenceUIHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1267D3D271A1F46003C492E /* PreferenceUIHostingController.swift */; };
|
||||
E126F741278A656C00A522BF /* ServerStreamType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E126F740278A656C00A522BF /* ServerStreamType.swift */; };
|
||||
E126F742278A656C00A522BF /* ServerStreamType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E126F740278A656C00A522BF /* ServerStreamType.swift */; };
|
||||
E131691726C583BC0074BFEE /* LogConstructor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E131691626C583BC0074BFEE /* LogConstructor.swift */; };
|
||||
E131691826C583BC0074BFEE /* LogConstructor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E131691626C583BC0074BFEE /* LogConstructor.swift */; };
|
||||
E131691926C583BC0074BFEE /* LogConstructor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E131691626C583BC0074BFEE /* LogConstructor.swift */; };
|
||||
E1347DB2279E3C6200BC6161 /* Puppy in Frameworks */ = {isa = PBXBuildFile; productRef = E1347DB1279E3C6200BC6161 /* Puppy */; };
|
||||
E1347DB4279E3C9E00BC6161 /* Puppy in Frameworks */ = {isa = PBXBuildFile; productRef = E1347DB3279E3C9E00BC6161 /* Puppy */; };
|
||||
E1347DB6279E3CA500BC6161 /* Puppy in Frameworks */ = {isa = PBXBuildFile; productRef = E1347DB5279E3CA500BC6161 /* Puppy */; };
|
||||
|
@ -782,7 +780,6 @@
|
|||
E122A9122788EAAD0060FA63 /* MediaStreamExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaStreamExtension.swift; sourceTree = "<group>"; };
|
||||
E1267D3D271A1F46003C492E /* PreferenceUIHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceUIHostingController.swift; sourceTree = "<group>"; };
|
||||
E126F740278A656C00A522BF /* ServerStreamType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerStreamType.swift; sourceTree = "<group>"; };
|
||||
E131691626C583BC0074BFEE /* LogConstructor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogConstructor.swift; sourceTree = "<group>"; };
|
||||
E1384943278036C70024FB48 /* VLCPlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VLCPlayerViewController.swift; sourceTree = "<group>"; };
|
||||
E13AD72D2798BC8D00FDCEE8 /* NativePlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativePlayerViewController.swift; sourceTree = "<group>"; };
|
||||
E13AD72F2798C60F00FDCEE8 /* NativePlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativePlayerViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -894,7 +891,6 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
62666E1727E501CC00EC0ECD /* CFNetwork.framework in Frameworks */,
|
||||
53649AAF269CFAF600A2D8B7 /* Puppy in Frameworks */,
|
||||
E11D83AF278FA998006E9776 /* NukeUI in Frameworks */,
|
||||
62666DFA27E5013700EC0ECD /* TVVLCKit.xcframework in Frameworks */,
|
||||
62666E3227E5021E00EC0ECD /* UIKit.framework in Frameworks */,
|
||||
|
@ -933,6 +929,7 @@
|
|||
62666E3E27E503FA00EC0ECD /* MediaAccessibility.framework in Frameworks */,
|
||||
62666DFF27E5016400EC0ECD /* CFNetwork.framework in Frameworks */,
|
||||
E13DD3D327168E65009D4DAF /* Defaults in Frameworks */,
|
||||
E1101177281B1E8A006A3584 /* Puppy in Frameworks */,
|
||||
E1361DA7278FA7A300BEC523 /* NukeUI in Frameworks */,
|
||||
E1002B682793CFBA00E47059 /* Algorithms in Frameworks */,
|
||||
62666E1127E501B900EC0ECD /* UIKit.framework in Frameworks */,
|
||||
|
@ -1860,7 +1857,6 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
E1FCD09526C47118007C8DCF /* ErrorMessage.swift */,
|
||||
E131691626C583BC0074BFEE /* LogConstructor.swift */,
|
||||
E1FCD08726C35A0D007C8DCF /* NetworkError.swift */,
|
||||
);
|
||||
path = Errors;
|
||||
|
@ -1936,6 +1932,7 @@
|
|||
E1361DA6278FA7A300BEC523 /* NukeUI */,
|
||||
E1002B672793CFBA00E47059 /* Algorithms */,
|
||||
62666E3827E502CE00EC0ECD /* SwizzleSwift */,
|
||||
E1101176281B1E8A006A3584 /* Puppy */,
|
||||
);
|
||||
productName = JellyfinPlayer;
|
||||
productReference = 5377CBF1263B596A003A4E83 /* Swiftfin iOS.app */;
|
||||
|
@ -2029,6 +2026,7 @@
|
|||
E1361DA5278FA7A300BEC523 /* XCRemoteSwiftPackageReference "NukeUI" */,
|
||||
E1002B662793CFBA00E47059 /* XCRemoteSwiftPackageReference "swift-algorithms" */,
|
||||
62666E3727E502CE00EC0ECD /* XCRemoteSwiftPackageReference "SwizzleSwift" */,
|
||||
E1101175281B1E8A006A3584 /* XCRemoteSwiftPackageReference "Puppy" */,
|
||||
);
|
||||
productRefGroup = 5377CBF2263B596A003A4E83 /* Products */;
|
||||
projectDirPath = "";
|
||||
|
@ -2339,7 +2337,6 @@
|
|||
5364F456266CA0DC0026ECBA /* BaseItemPersonExtensions.swift in Sources */,
|
||||
531690FA267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift in Sources */,
|
||||
C4BE0764271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift in Sources */,
|
||||
E131691826C583BC0074BFEE /* LogConstructor.swift in Sources */,
|
||||
E1E5D5512783E67700692DFE /* ExperimentalSettingsView.swift in Sources */,
|
||||
E1A2C160279A7DCA005EC829 /* AboutView.swift in Sources */,
|
||||
C4BE076A271FC164003F4AD1 /* TVLibrariesView.swift in Sources */,
|
||||
|
@ -2416,7 +2413,6 @@
|
|||
C4AE2C3227498D6A00AE13CF /* LiveTVProgramsView.swift in Sources */,
|
||||
62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */,
|
||||
62E632E6267D3F5B0063E547 /* EpisodeItemViewModel.swift in Sources */,
|
||||
E131691726C583BC0074BFEE /* LogConstructor.swift in Sources */,
|
||||
5321753B2671BCFC005491E6 /* SettingsViewModel.swift in Sources */,
|
||||
E14F7D0926DB36F7007C3AE6 /* ItemLandscapeMainView.swift in Sources */,
|
||||
E107BB9327880A8F00354E07 /* CollectionItemViewModel.swift in Sources */,
|
||||
|
@ -2538,7 +2534,6 @@
|
|||
E1D4BF8D2719F3A300A11E64 /* VideoPlayerJumpLength.swift in Sources */,
|
||||
6220D0AF26D5EABE00B8E046 /* ViewExtensions.swift in Sources */,
|
||||
E13DD3D7271693CD009D4DAF /* SwiftfinStoreDefaults.swift in Sources */,
|
||||
E131691926C583BC0074BFEE /* LogConstructor.swift in Sources */,
|
||||
E13DD3CA27164B80009D4DAF /* SwiftfinStore.swift in Sources */,
|
||||
E10EAA51277BBCC4000269ED /* CGSizeExtensions.swift in Sources */,
|
||||
62E1DCC5273CE19800C9AE76 /* URLExtensions.swift in Sources */,
|
||||
|
@ -2884,7 +2879,7 @@
|
|||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 70;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = TY84JMYEFE;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
EXCLUDED_ARCHS = "";
|
||||
|
@ -2920,7 +2915,7 @@
|
|||
CURRENT_PROJECT_VERSION = 70;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = TY84JMYEFE;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
EXCLUDED_ARCHS = "";
|
||||
|
@ -2950,7 +2945,7 @@
|
|||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 70;
|
||||
DEVELOPMENT_TEAM = 4BHXT8RHFR;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
@ -2976,7 +2971,7 @@
|
|||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 70;
|
||||
DEVELOPMENT_TEAM = 4BHXT8RHFR;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
@ -3108,6 +3103,14 @@
|
|||
kind = branch;
|
||||
};
|
||||
};
|
||||
E1101175281B1E8A006A3584 /* XCRemoteSwiftPackageReference "Puppy" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/sushichop/Puppy";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 0.5.0;
|
||||
};
|
||||
};
|
||||
E1267D42271A212C003C492E /* XCRemoteSwiftPackageReference "CombineExt" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/CombineCommunity/CombineExt";
|
||||
|
@ -3237,6 +3240,11 @@
|
|||
package = E10EAA4B277BB716000269ED /* XCRemoteSwiftPackageReference "swiftui-sliders" */;
|
||||
productName = Sliders;
|
||||
};
|
||||
E1101176281B1E8A006A3584 /* Puppy */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E1101175281B1E8A006A3584 /* XCRemoteSwiftPackageReference "Puppy" */;
|
||||
productName = Puppy;
|
||||
};
|
||||
E11D83AE278FA998006E9776 /* NukeUI */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E1361DA5278FA7A300BEC523 /* XCRemoteSwiftPackageReference "NukeUI" */;
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/Flight-School/AnyCodable",
|
||||
"state" : {
|
||||
"revision" : "22f302d4c048aafcda09a4ab5b8c0b03855316fb",
|
||||
"version" : "0.6.3"
|
||||
"revision" : "11423ef0c756e8a1f6b4bb576dab9d97bc016c70",
|
||||
"version" : "0.6.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -50,8 +50,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kaishin/Gifu",
|
||||
"state" : {
|
||||
"revision" : "0ffe24744cc3d82ab9edece53670d0352c6d5507",
|
||||
"version" : "3.3.0"
|
||||
"revision" : "51f2eab32903e336f590c013267cfa4d7f8b06c4",
|
||||
"version" : "3.3.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -68,8 +68,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kean/Nuke.git",
|
||||
"state" : {
|
||||
"revision" : "78fa963b8491fc520791d8c2a509f1b8593d8aae",
|
||||
"version" : "10.7.1"
|
||||
"revision" : "0ea7545b5c918285aacc044dc75048625c8257cc",
|
||||
"version" : "10.8.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -77,8 +77,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kean/NukeUI",
|
||||
"state" : {
|
||||
"revision" : "71398392943f2538fd0f2ebc6f282920f6775b0c",
|
||||
"version" : "0.8.0"
|
||||
"revision" : "17f26c07e6b1d3b9258287f99f528111fcd7b7ad",
|
||||
"version" : "0.8.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -140,8 +140,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/siteline/SwiftUI-Introspect",
|
||||
"state" : {
|
||||
"revision" : "2e09be8af614401bc9f87d40093ec19ce56ccaf2",
|
||||
"version" : "0.1.3"
|
||||
"revision" : "f2616860a41f9d9932da412a8978fec79c06fe24",
|
||||
"version" : "0.1.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -150,7 +150,7 @@
|
|||
"location" : "https://github.com/spacenation/swiftui-sliders",
|
||||
"state" : {
|
||||
"branch" : "master",
|
||||
"revision" : "518bed3bfc7bd522f3c49404a0d1efb98fa1bf2c"
|
||||
"revision" : "538e16b35ad7a066a8f5624da9ecee6327886bf7"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -19,7 +19,7 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
|||
|
||||
// Lazily initialize datastack
|
||||
_ = SwiftfinStore.dataStack
|
||||
LogManager.setup()
|
||||
LogManager.setup()
|
||||
|
||||
let audioSession = AVAudioSession.sharedInstance()
|
||||
do {
|
||||
|
|
|
@ -48,9 +48,9 @@ struct PortraitImageHStackView<TopBarView: View, ItemType: PortraitImageStackabl
|
|||
failureView: {
|
||||
InitialFailureView(item.failureInitials)
|
||||
})
|
||||
.portraitPoster(width: maxWidth)
|
||||
.shadow(radius: 4, y: 2)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
.portraitPoster(width: maxWidth)
|
||||
.shadow(radius: 4, y: 2)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
|
||||
if item.showTitle {
|
||||
Text(item.title)
|
||||
|
|
|
@ -40,9 +40,9 @@ struct PortraitItemButton<ItemType: PortraitImageStackable>: View {
|
|||
failureView: {
|
||||
InitialFailureView(item.failureInitials)
|
||||
})
|
||||
.portraitPoster(width: maxWidth)
|
||||
.shadow(radius: 4, y: 2)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
.portraitPoster(width: maxWidth)
|
||||
.shadow(radius: 4, y: 2)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
|
||||
if item.showTitle {
|
||||
Text(item.title)
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
|
@ -39,6 +35,8 @@
|
|||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
|
@ -65,6 +63,8 @@ network.</string>
|
|||
</dict>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>UILaunchScreen</key>
|
||||
<dict>
|
||||
<key>UIColorName</key>
|
||||
|
|
|
@ -103,7 +103,7 @@ struct ConnectToServerView: View {
|
|||
}
|
||||
.alert(item: $viewModel.errorMessage) { _ in
|
||||
Alert(title: Text(viewModel.alertTitle),
|
||||
message: Text(viewModel.errorMessage?.displayMessage ?? L10n.unknownError),
|
||||
message: Text(viewModel.errorMessage?.message ?? L10n.unknownError),
|
||||
dismissButton: .cancel())
|
||||
}
|
||||
.alert(item: $viewModel.addServerURIPayload) { _ in
|
||||
|
|
|
@ -33,13 +33,13 @@ struct ContinueWatchingView: View {
|
|||
item.getSeriesThumbImage(maxWidth: 320),
|
||||
item.getSeriesBackdropImage(maxWidth: 320),
|
||||
])
|
||||
.frame(width: 320, height: 180)
|
||||
.frame(width: 320, height: 180)
|
||||
} else {
|
||||
ImageView(sources: [
|
||||
item.getThumbImage(maxWidth: 320),
|
||||
item.getBackdropImage(maxWidth: 320),
|
||||
])
|
||||
.frame(width: 320, height: 180)
|
||||
.frame(width: 320, height: 180)
|
||||
}
|
||||
}
|
||||
.accessibilityIgnoresInvertColors()
|
||||
|
|
|
@ -35,7 +35,7 @@ struct HomeView: View {
|
|||
}
|
||||
|
||||
Text("\(errorMessage.code)")
|
||||
Text(errorMessage.displayMessage)
|
||||
Text(errorMessage.message)
|
||||
.frame(minWidth: 50, maxWidth: 240)
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ struct ItemViewBody: View {
|
|||
selectedAction: { genre in
|
||||
itemRouter.route(to: \.library, (viewModel: .init(genre: genre), title: genre.title))
|
||||
})
|
||||
.padding(.bottom)
|
||||
.padding(.bottom)
|
||||
}
|
||||
|
||||
// MARK: Studios
|
||||
|
|
|
@ -50,7 +50,7 @@ struct UserSignInView: View {
|
|||
}
|
||||
.alert(item: $viewModel.errorMessage) { _ in
|
||||
Alert(title: Text(viewModel.alertTitle),
|
||||
message: Text(viewModel.errorMessage?.displayMessage ?? L10n.unknownError),
|
||||
message: Text(viewModel.errorMessage?.message ?? L10n.unknownError),
|
||||
dismissButton: .cancel())
|
||||
}
|
||||
.navigationTitle(L10n.signIn)
|
||||
|
|
|
@ -375,16 +375,16 @@ struct VLCPlayerOverlayView: View {
|
|||
ValueSlider(value: $viewModel.sliderPercentage, onEditingChanged: { editing in
|
||||
viewModel.sliderIsScrubbing = editing
|
||||
})
|
||||
.valueSliderStyle(HorizontalValueSliderStyle(track:
|
||||
HorizontalValueTrack(view:
|
||||
Capsule().foregroundColor(.purple))
|
||||
.background(Capsule().foregroundColor(Color.gray.opacity(0.25)))
|
||||
.frame(height: 4),
|
||||
thumb: Circle().foregroundColor(.purple),
|
||||
thumbSize: CGSize.Circle(radius: viewModel.sliderIsScrubbing ? 20 : 15),
|
||||
thumbInteractiveSize: CGSize.Circle(radius: 40),
|
||||
options: .defaultOptions))
|
||||
.frame(maxHeight: 50)
|
||||
.valueSliderStyle(HorizontalValueSliderStyle(track:
|
||||
HorizontalValueTrack(view:
|
||||
Capsule().foregroundColor(.purple))
|
||||
.background(Capsule().foregroundColor(Color.gray.opacity(0.25)))
|
||||
.frame(height: 4),
|
||||
thumb: Circle().foregroundColor(.purple),
|
||||
thumbSize: CGSize.Circle(radius: viewModel.sliderIsScrubbing ? 20 : 15),
|
||||
thumbInteractiveSize: CGSize.Circle(radius: 40),
|
||||
options: .defaultOptions))
|
||||
.frame(maxHeight: 50)
|
||||
|
||||
Text(viewModel.rightLabelText)
|
||||
.font(.system(size: 18, weight: .semibold, design: .default))
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue