Merge pull request #393 from LePips/improve-image-views
This commit is contained in:
commit
1ab99a6f2f
|
@ -45,12 +45,10 @@ final class MainCoordinator: NavigationCoordinatable {
|
|||
barAppearance.tintColor = UIColor(Color.jellyfinPurple)
|
||||
|
||||
// Notification setup for state
|
||||
let nc = SwiftfinNotificationCenter.main
|
||||
nc.addObserver(self, selector: #selector(didLogIn), name: SwiftfinNotificationCenter.Keys.didSignIn, object: nil)
|
||||
nc.addObserver(self, selector: #selector(didLogOut), name: SwiftfinNotificationCenter.Keys.didSignOut, object: nil)
|
||||
nc.addObserver(self, selector: #selector(processDeepLink), name: SwiftfinNotificationCenter.Keys.processDeepLink, object: nil)
|
||||
nc.addObserver(self, selector: #selector(didChangeServerCurrentURI),
|
||||
name: SwiftfinNotificationCenter.Keys.didChangeServerCurrentURI, object: nil)
|
||||
Notifications[.didSignIn].subscribe(self, selector: #selector(didSignIn))
|
||||
Notifications[.didSignOut].subscribe(self, selector: #selector(didSignOut))
|
||||
Notifications[.processDeepLink].subscribe(self, selector: #selector(processDeepLink(_:)))
|
||||
Notifications[.didChangeServerCurrentURI].subscribe(self, selector: #selector(didChangeServerCurrentURI(_:)))
|
||||
|
||||
Defaults.publisher(.appAppearance)
|
||||
.sink { _ in
|
||||
|
@ -60,13 +58,13 @@ final class MainCoordinator: NavigationCoordinatable {
|
|||
}
|
||||
|
||||
@objc
|
||||
func didLogIn() {
|
||||
func didSignIn() {
|
||||
LogManager.shared.log.info("Received `didSignIn` from SwiftfinNotificationCenter.")
|
||||
root(\.mainTab)
|
||||
}
|
||||
|
||||
@objc
|
||||
func didLogOut() {
|
||||
func didSignOut() {
|
||||
LogManager.shared.log.info("Received `didSignOut` from SwiftfinNotificationCenter.")
|
||||
root(\.serverList)
|
||||
}
|
||||
|
|
|
@ -40,19 +40,18 @@ final class MainCoordinator: NavigationCoordinatable {
|
|||
DataLoader.sharedUrlCache.diskCapacity = 1000 * 1024 * 1024 // 1000MB disk
|
||||
|
||||
// Notification setup for state
|
||||
let nc = SwiftfinNotificationCenter.main
|
||||
nc.addObserver(self, selector: #selector(didLogIn), name: SwiftfinNotificationCenter.Keys.didSignIn, object: nil)
|
||||
nc.addObserver(self, selector: #selector(didLogOut), name: SwiftfinNotificationCenter.Keys.didSignOut, object: nil)
|
||||
Notifications[.didSignIn].subscribe(self, selector: #selector(didSignIn))
|
||||
Notifications[.didSignOut].subscribe(self, selector: #selector(didSignOut))
|
||||
}
|
||||
|
||||
@objc
|
||||
func didLogIn() {
|
||||
func didSignIn() {
|
||||
LogManager.shared.log.info("Received `didSignIn` from NSNotificationCenter.")
|
||||
root(\.mainTab)
|
||||
}
|
||||
|
||||
@objc
|
||||
func didLogOut() {
|
||||
func didSignOut() {
|
||||
LogManager.shared.log.info("Received `didSignOut` from NSNotificationCenter.")
|
||||
root(\.serverList)
|
||||
}
|
||||
|
|
|
@ -240,7 +240,7 @@ final class SessionManager {
|
|||
Defaults[.lastServerUserID] = user.id
|
||||
|
||||
currentLogin = (server: currentServer.state, user: currentUser.state)
|
||||
SwiftfinNotificationCenter.main.post(name: SwiftfinNotificationCenter.Keys.didSignIn, object: nil)
|
||||
Notifications[.didSignIn].post()
|
||||
})
|
||||
.map { _, user, _ in
|
||||
user.state
|
||||
|
@ -255,7 +255,7 @@ final class SessionManager {
|
|||
Defaults[.lastServerUserID] = user.id
|
||||
setAuthHeader(with: user.accessToken)
|
||||
currentLogin = (server: server, user: user)
|
||||
SwiftfinNotificationCenter.main.post(name: SwiftfinNotificationCenter.Keys.didSignIn, object: nil)
|
||||
Notifications[.didSignIn].post()
|
||||
}
|
||||
|
||||
// MARK: logout
|
||||
|
@ -265,7 +265,7 @@ final class SessionManager {
|
|||
JellyfinAPI.basePath = ""
|
||||
setAuthHeader(with: "")
|
||||
Defaults[.lastServerUserID] = nil
|
||||
SwiftfinNotificationCenter.main.post(name: SwiftfinNotificationCenter.Keys.didSignOut, object: nil)
|
||||
Notifications[.didSignOut].post()
|
||||
}
|
||||
|
||||
// MARK: purge
|
||||
|
@ -278,7 +278,7 @@ final class SessionManager {
|
|||
delete(server: server)
|
||||
}
|
||||
|
||||
SwiftfinNotificationCenter.main.post(name: SwiftfinNotificationCenter.Keys.didPurge, object: nil)
|
||||
Notifications[.didPurge].post()
|
||||
}
|
||||
|
||||
// MARK: delete user
|
||||
|
|
|
@ -8,20 +8,63 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
enum SwiftfinNotificationCenter {
|
||||
class SwiftfinNotification {
|
||||
|
||||
private let notificationName: Notification.Name
|
||||
|
||||
fileprivate init(_ notificationName: Notification.Name) {
|
||||
self.notificationName = notificationName
|
||||
}
|
||||
|
||||
func post(object: Any? = nil) {
|
||||
Notifications.main.post(name: notificationName, object: object)
|
||||
}
|
||||
|
||||
func subscribe(_ observer: Any, selector: Selector) {
|
||||
Notifications.main.addObserver(observer, selector: selector, name: notificationName, object: nil)
|
||||
}
|
||||
|
||||
func unsubscribe(_ observer: Any) {
|
||||
Notifications.main.removeObserver(self, name: notificationName, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
enum Notifications {
|
||||
|
||||
static let main: NotificationCenter = {
|
||||
NotificationCenter()
|
||||
}()
|
||||
|
||||
enum Keys {
|
||||
static let didSignIn = Notification.Name("didSignIn")
|
||||
static let didSignOut = Notification.Name("didSignOut")
|
||||
static let processDeepLink = Notification.Name("processDeepLink")
|
||||
static let didPurge = Notification.Name("didPurge")
|
||||
static let didChangeServerCurrentURI = Notification.Name("didChangeCurrentLoginURI")
|
||||
final class Key {
|
||||
public typealias NotificationKey = Notifications.Key
|
||||
|
||||
// Send with an item id to check if current item for item views
|
||||
static let didSendStopReport = Notification.Name("didSendStopReport")
|
||||
public let key: String
|
||||
public let underlyingNotification: SwiftfinNotification
|
||||
|
||||
public init(_ key: String) {
|
||||
self.key = key
|
||||
self.underlyingNotification = SwiftfinNotification(Notification.Name(key))
|
||||
}
|
||||
}
|
||||
|
||||
static subscript(key: Key) -> SwiftfinNotification {
|
||||
key.underlyingNotification
|
||||
}
|
||||
|
||||
static func unsubscribe(_ observer: Any) {
|
||||
main.removeObserver(observer)
|
||||
}
|
||||
}
|
||||
|
||||
extension Notifications.Key {
|
||||
|
||||
static let didSignIn = NotificationKey("didSignIn")
|
||||
static let didSignOut = NotificationKey("didSignOut")
|
||||
static let processDeepLink = NotificationKey("processDeepLink")
|
||||
static let didPurge = NotificationKey("didPurge")
|
||||
static let didChangeServerCurrentURI = NotificationKey("didChangeCurrentLoginURI")
|
||||
static let toggleOfflineMode = NotificationKey("toggleOfflineMode")
|
||||
static let didDeleteOfflineItem = NotificationKey("didDeleteOfflineItem")
|
||||
static let didAddDownload = NotificationKey("didAddDownload")
|
||||
static let didSendStopReport = NotificationKey("didSendStopReport")
|
||||
}
|
||||
|
|
|
@ -34,9 +34,8 @@ final class HomeViewModel: ViewModel {
|
|||
// Nov. 6, 2021
|
||||
// This is a workaround since Stinsen doesn't have the ability to rebuild a root at the time of writing.
|
||||
// See ServerDetailViewModel.swift for feature request issue
|
||||
let nc = SwiftfinNotificationCenter.main
|
||||
nc.addObserver(self, selector: #selector(didSignIn), name: SwiftfinNotificationCenter.Keys.didSignIn, object: nil)
|
||||
nc.addObserver(self, selector: #selector(didSignOut), name: SwiftfinNotificationCenter.Keys.didSignOut, object: nil)
|
||||
Notifications[.didSignIn].subscribe(self, selector: #selector(didSignIn))
|
||||
Notifications[.didSignOut].subscribe(self, selector: #selector(didSignOut))
|
||||
}
|
||||
|
||||
@objc
|
||||
|
|
|
@ -55,10 +55,7 @@ class ItemViewModel: ViewModel {
|
|||
|
||||
getSimilarItems()
|
||||
|
||||
SwiftfinNotificationCenter.main.addObserver(self,
|
||||
selector: #selector(receivedStopReport(_:)),
|
||||
name: SwiftfinNotificationCenter.Keys.didSendStopReport,
|
||||
object: nil)
|
||||
Notifications[.didSendStopReport].subscribe(self, selector: #selector(receivedStopReport(_:)))
|
||||
|
||||
refreshItemVideoPlayerViewModel(for: item)
|
||||
}
|
||||
|
@ -72,7 +69,7 @@ class ItemViewModel: ViewModel {
|
|||
} else {
|
||||
// Remove if necessary. Note that this cannot be in deinit as
|
||||
// holding as an observer won't allow the object to be deinit-ed
|
||||
SwiftfinNotificationCenter.main.removeObserver(self)
|
||||
Notifications.unsubscribe(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,8 +25,7 @@ class ServerDetailViewModel: ViewModel {
|
|||
} receiveValue: { newServerState in
|
||||
self.server = newServerState
|
||||
|
||||
let nc = SwiftfinNotificationCenter.main
|
||||
nc.post(name: SwiftfinNotificationCenter.Keys.didChangeServerCurrentURI, object: newServerState)
|
||||
Notifications[.didChangeServerCurrentURI].post(object: newServerState)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
|
|
@ -20,8 +20,7 @@ class ServerListViewModel: ObservableObject {
|
|||
// This is a workaround since Stinsen doesn't have the ability to rebuild a root at the time of writing.
|
||||
// Feature request issue: https://github.com/rundfunk47/stinsen/issues/33
|
||||
// Go to each MainCoordinator and implement the rebuild of the root when receiving the notification
|
||||
let nc = SwiftfinNotificationCenter.main
|
||||
nc.addObserver(self, selector: #selector(didPurge), name: SwiftfinNotificationCenter.Keys.didPurge, object: nil)
|
||||
Notifications[.didPurge].subscribe(self, selector: #selector(didPurge))
|
||||
}
|
||||
|
||||
func fetchServers() {
|
||||
|
|
|
@ -21,9 +21,7 @@ class UserListViewModel: ViewModel {
|
|||
|
||||
super.init()
|
||||
|
||||
let nc = SwiftfinNotificationCenter.main
|
||||
nc.addObserver(self, selector: #selector(didChangeCurrentLoginURI), name: SwiftfinNotificationCenter.Keys.didChangeServerCurrentURI,
|
||||
object: nil)
|
||||
Notifications[.didChangeServerCurrentURI].subscribe(self, selector: #selector(didChangeCurrentLoginURI(_:)))
|
||||
}
|
||||
|
||||
@objc
|
||||
|
|
|
@ -585,8 +585,7 @@ extension VideoPlayerViewModel {
|
|||
self.handleAPIRequestError(completion: completion)
|
||||
} receiveValue: { _ in
|
||||
LogManager.shared.log.debug("Stop report sent for item: \(self.item.id ?? "No ID")")
|
||||
SwiftfinNotificationCenter.main.post(name: SwiftfinNotificationCenter.Keys.didSendStopReport,
|
||||
object: self.item.id)
|
||||
Notifications[.didSendStopReport].post(object: self.item.id)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// 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 SwiftUI
|
||||
import UIKit
|
||||
|
||||
struct BlurHashView: UIViewRepresentable {
|
||||
|
||||
let blurHash: String
|
||||
|
||||
func makeUIView(context: Context) -> UIBlurHashView {
|
||||
UIBlurHashView(blurHash)
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: UIBlurHashView, context: Context) {}
|
||||
}
|
||||
|
||||
class UIBlurHashView: UIView {
|
||||
|
||||
private let imageView: UIImageView
|
||||
|
||||
init(_ blurHash: String) {
|
||||
let imageView = UIImageView()
|
||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.imageView = imageView
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
computeBlurHashImageAsync(blurHash: blurHash) { [weak self] blurImage in
|
||||
guard let self = self else { return }
|
||||
DispatchQueue.main.async {
|
||||
self.imageView.image = blurImage
|
||||
self.imageView.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
addSubview(imageView)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
imageView.topAnchor.constraint(equalTo: topAnchor),
|
||||
imageView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
imageView.leftAnchor.constraint(equalTo: leftAnchor),
|
||||
imageView.rightAnchor.constraint(equalTo: rightAnchor),
|
||||
])
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func computeBlurHashImageAsync(blurHash: String, _ completion: @escaping (UIImage?) -> Void) {
|
||||
DispatchQueue.global(qos: .utility).async {
|
||||
let image = UIImage(blurHash: blurHash, size: .Circle(radius: 12))
|
||||
completion(image)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,58 +6,68 @@
|
|||
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Nuke
|
||||
import NukeUI
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
// TODO: update multiple sources so that multiple blurhashes can be taken, clean up
|
||||
// TODO: Fix 100+ inits
|
||||
|
||||
struct ImageView: View {
|
||||
struct ImageViewSource {
|
||||
let url: URL?
|
||||
let blurHash: String?
|
||||
|
||||
init(url: URL? = nil, blurHash: String? = nil) {
|
||||
self.url = url
|
||||
self.blurHash = blurHash
|
||||
}
|
||||
}
|
||||
|
||||
struct DefaultFailureView: View {
|
||||
|
||||
var body: some View {
|
||||
Color.secondary
|
||||
}
|
||||
}
|
||||
|
||||
struct ImageView<FailureView: View>: View {
|
||||
|
||||
@State
|
||||
private var sources: [URL]
|
||||
private var currentURL: URL? { sources.first }
|
||||
private var sources: [ImageViewSource]
|
||||
private var currentURL: URL? { sources.first?.url }
|
||||
private var currentBlurHash: String? { sources.first?.blurHash }
|
||||
private var failureView: FailureView
|
||||
|
||||
private let blurhash: String
|
||||
private let failureInitials: String
|
||||
|
||||
init(src: URL, bh: String = "001fC^", failureInitials: String = "") {
|
||||
self.sources = [src]
|
||||
self.blurhash = bh
|
||||
self.failureInitials = failureInitials
|
||||
init(_ source: URL?, blurHash: String? = nil, @ViewBuilder failureView: () -> FailureView) {
|
||||
let imageViewSource = ImageViewSource(url: source, blurHash: blurHash)
|
||||
_sources = State(initialValue: [imageViewSource])
|
||||
self.failureView = failureView()
|
||||
}
|
||||
|
||||
init(sources: [URL], bh: String = "001fC^", failureInitials: String = "") {
|
||||
assert(!sources.isEmpty, "Must supply at least one source")
|
||||
|
||||
self.sources = sources
|
||||
self.blurhash = bh
|
||||
self.failureInitials = failureInitials
|
||||
init(_ source: ImageViewSource, @ViewBuilder failureView: () -> FailureView) {
|
||||
_sources = State(initialValue: [source])
|
||||
self.failureView = failureView()
|
||||
}
|
||||
|
||||
init(_ sources: [ImageViewSource], @ViewBuilder failureView: () -> FailureView) {
|
||||
_sources = State(initialValue: sources)
|
||||
self.failureView = failureView()
|
||||
}
|
||||
|
||||
// TODO: fix placeholder hash view
|
||||
@ViewBuilder
|
||||
private var placeholderView: some View {
|
||||
Image(uiImage: UIImage(blurHash: blurhash, size: CGSize(width: 12, height: 12)) ??
|
||||
UIImage(blurHash: "001fC^", size: CGSize(width: 12, height: 12))!)
|
||||
.resizable()
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func failureImage() -> some View {
|
||||
ZStack {
|
||||
Rectangle()
|
||||
.foregroundColor(Color(UIColor.darkGray))
|
||||
|
||||
Text(failureInitials)
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(.secondary)
|
||||
.accessibilityHidden(true)
|
||||
if let currentBlurHash = currentBlurHash {
|
||||
BlurHashView(blurHash: currentBlurHash)
|
||||
.id(currentBlurHash)
|
||||
} else {
|
||||
Color.secondary
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if let u = currentURL {
|
||||
LazyImage(source: u) { state in
|
||||
|
||||
if let currentURL = currentURL {
|
||||
LazyImage(source: currentURL) { state in
|
||||
if let image = state.image {
|
||||
image
|
||||
} else if state.error != nil {
|
||||
|
@ -67,9 +77,29 @@ struct ImageView: View {
|
|||
}
|
||||
}
|
||||
.pipeline(ImagePipeline(configuration: .withDataCache))
|
||||
.id(u)
|
||||
.id(currentURL)
|
||||
} else {
|
||||
failureImage()
|
||||
failureView
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ImageView where FailureView == DefaultFailureView {
|
||||
init(_ source: URL?, blurHash: String? = nil) {
|
||||
let imageViewSource = ImageViewSource(url: source, blurHash: blurHash)
|
||||
self.init(imageViewSource, failureView: { DefaultFailureView() })
|
||||
}
|
||||
|
||||
init(_ source: ImageViewSource) {
|
||||
self.init(source, failureView: { DefaultFailureView() })
|
||||
}
|
||||
|
||||
init(_ sources: [ImageViewSource]) {
|
||||
self.init(sources, failureView: { DefaultFailureView() })
|
||||
}
|
||||
|
||||
init(sources: [URL]) {
|
||||
let imageViewSources = sources.compactMap { ImageViewSource(url: $0, blurHash: nil) }
|
||||
self.init(imageViewSources, failureView: { DefaultFailureView() })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// 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 SwiftUI
|
||||
|
||||
struct InitialFailureView: View {
|
||||
|
||||
let initials: String
|
||||
|
||||
init(_ initials: String) {
|
||||
self.initials = initials
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Rectangle()
|
||||
.foregroundColor(Color(UIColor.darkGray))
|
||||
|
||||
Text(initials)
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(.secondary)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ struct LiveTVChannelItemElement: View {
|
|||
.font(.footnote)
|
||||
.frame(alignment: .trailing)
|
||||
}.frame(alignment: .top)
|
||||
ImageView(src: channel.getPrimaryImage(maxWidth: 125))
|
||||
ImageView(channel.getPrimaryImage(maxWidth: 125))
|
||||
.frame(width: 125, alignment: .center)
|
||||
.offset(x: 0, y: -32)
|
||||
Text(channel.name ?? "?")
|
||||
|
|
|
@ -21,8 +21,8 @@ struct EpisodeRowCard: View {
|
|||
Button {
|
||||
itemRouter.route(to: \.item, episode)
|
||||
} label: {
|
||||
ImageView(src: episode.getBackdropImage(maxWidth: 550),
|
||||
bh: episode.getBackdropImageBlurHash())
|
||||
ImageView(episode.getBackdropImage(maxWidth: 550),
|
||||
blurHash: episode.getBackdropImageBlurHash())
|
||||
.mask(Rectangle().frame(width: 550, height: 308))
|
||||
.frame(width: 550, height: 308)
|
||||
}
|
||||
|
|
|
@ -30,11 +30,10 @@ struct CinematicNextUpCardView: View {
|
|||
])
|
||||
.frame(width: 350, height: 210)
|
||||
} else {
|
||||
ImageView(sources: [
|
||||
item.getThumbImage(maxWidth: 350),
|
||||
item.getBackdropImage(maxWidth: 350),
|
||||
],
|
||||
bh: item.getBackdropImageBlurHash())
|
||||
ImageView([
|
||||
.init(url: item.getThumbImage(maxWidth: 350)),
|
||||
.init(url: item.getBackdropImage(maxWidth: 350), blurHash: item.getBackdropImageBlurHash()),
|
||||
])
|
||||
.frame(width: 350, height: 210)
|
||||
}
|
||||
|
||||
|
|
|
@ -31,11 +31,10 @@ struct CinematicResumeCardView: View {
|
|||
])
|
||||
.frame(width: 350, height: 210)
|
||||
} else {
|
||||
ImageView(sources: [
|
||||
item.getThumbImage(maxWidth: 350),
|
||||
item.getBackdropImage(maxWidth: 350),
|
||||
],
|
||||
bh: item.getBackdropImageBlurHash())
|
||||
ImageView([
|
||||
.init(url: item.getThumbImage(maxWidth: 350)),
|
||||
.init(url: item.getBackdropImage(maxWidth: 350), blurHash: item.getBackdropImageBlurHash()),
|
||||
])
|
||||
.frame(width: 350, height: 210)
|
||||
}
|
||||
|
||||
|
|
|
@ -46,9 +46,9 @@ struct LandscapeItemElement: View {
|
|||
|
||||
var body: some View {
|
||||
VStack {
|
||||
ImageView(src: item.type == "Episode" && !(inSeasonView ?? false) ? item.getSeriesBackdropImage(maxWidth: 445) : item
|
||||
ImageView(item.type == "Episode" && !(inSeasonView ?? false) ? item.getSeriesBackdropImage(maxWidth: 445) : item
|
||||
.getBackdropImage(maxWidth: 445),
|
||||
bh: item.type == "Episode" ? item.getSeriesBackdropImageBlurHash() : item.getBackdropImageBlurHash())
|
||||
blurHash: item.type == "Episode" ? item.getSeriesBackdropImageBlurHash() : item.getBackdropImageBlurHash())
|
||||
.frame(width: 445, height: 250)
|
||||
.cornerRadius(10)
|
||||
.ignoresSafeArea()
|
||||
|
|
|
@ -21,8 +21,8 @@ struct PortraitItemElement: View {
|
|||
|
||||
var body: some View {
|
||||
VStack {
|
||||
ImageView(src: item.type == "Episode" ? item.getSeriesPrimaryImage(maxWidth: 200) : item.getPrimaryImage(maxWidth: 200),
|
||||
bh: item.type == "Episode" ? item.getSeriesPrimaryImageBlurHash() : item.getPrimaryImageBlurHash())
|
||||
ImageView(item.type == "Episode" ? item.getSeriesPrimaryImage(maxWidth: 200) : item.getPrimaryImage(maxWidth: 200),
|
||||
blurHash: item.type == "Episode" ? item.getSeriesPrimaryImageBlurHash() : item.getPrimaryImageBlurHash())
|
||||
.frame(width: 200, height: 300)
|
||||
.cornerRadius(10)
|
||||
.shadow(radius: focused ? 10.0 : 0)
|
||||
|
|
|
@ -45,7 +45,7 @@ struct PortraitItemsRowView: View {
|
|||
Button {
|
||||
selectedAction(item)
|
||||
} label: {
|
||||
ImageView(src: item.portraitHeaderViewURL(maxWidth: 257))
|
||||
ImageView(item.portraitHeaderViewURL(maxWidth: 257))
|
||||
.frame(width: 257, height: 380)
|
||||
}
|
||||
.frame(height: 380)
|
||||
|
|
|
@ -20,7 +20,7 @@ struct PublicUserButton: View {
|
|||
var body: some View {
|
||||
VStack {
|
||||
if publicUser.primaryImageTag != nil {
|
||||
ImageView(src: URL(string: "\(SessionManager.main.currentLogin.server.currentURI)/Users/\(publicUser.id ?? "")/Images/Primary?width=500&quality=80&tag=\(publicUser.primaryImageTag!)")!)
|
||||
ImageView(URL(string: "\(SessionManager.main.currentLogin.server.currentURI)/Users/\(publicUser.id ?? "")/Images/Primary?width=500&quality=80&tag=\(publicUser.primaryImageTag!)")!)
|
||||
.frame(width: 250, height: 250)
|
||||
.cornerRadius(125.0)
|
||||
} else {
|
||||
|
|
|
@ -23,10 +23,10 @@ struct ContinueWatchingCard: View {
|
|||
ZStack(alignment: .bottom) {
|
||||
|
||||
if item.itemType == .episode {
|
||||
ImageView(src: item.getSeriesBackdropImage(maxWidth: 500))
|
||||
ImageView(item.getSeriesBackdropImage(maxWidth: 500))
|
||||
.frame(width: 500, height: 281.25)
|
||||
} else {
|
||||
ImageView(src: item.getBackdropImage(maxWidth: 500))
|
||||
ImageView(item.getBackdropImage(maxWidth: 500))
|
||||
.frame(width: 500, height: 281.25)
|
||||
}
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@ struct CinematicCollectionItemView: View {
|
|||
var body: some View {
|
||||
ZStack {
|
||||
|
||||
ImageView(src: viewModel.item.getBackdropImage(maxWidth: 1920),
|
||||
bh: viewModel.item.getBackdropImageBlurHash())
|
||||
ImageView(viewModel.item.getBackdropImage(maxWidth: 1920),
|
||||
blurHash: viewModel.item.getBackdropImageBlurHash())
|
||||
.ignoresSafeArea()
|
||||
|
||||
ScrollView {
|
||||
|
|
|
@ -32,8 +32,8 @@ struct CinematicEpisodeItemView: View {
|
|||
var body: some View {
|
||||
ZStack {
|
||||
|
||||
ImageView(src: viewModel.item.getBackdropImage(maxWidth: 1920),
|
||||
bh: viewModel.item.getBackdropImageBlurHash())
|
||||
ImageView(viewModel.item.getBackdropImage(maxWidth: 1920),
|
||||
blurHash: viewModel.item.getBackdropImageBlurHash())
|
||||
.frame(height: UIScreen.main.bounds.height - 10)
|
||||
.ignoresSafeArea()
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ struct CinematicItemAboutView: View {
|
|||
|
||||
var body: some View {
|
||||
HStack(alignment: .top, spacing: 10) {
|
||||
ImageView(src: viewModel.item.portraitHeaderViewURL(maxWidth: 257))
|
||||
ImageView(viewModel.item.portraitHeaderViewURL(maxWidth: 257))
|
||||
.portraitPoster(width: 257)
|
||||
|
||||
ZStack(alignment: .topLeading) {
|
||||
|
|
|
@ -24,8 +24,8 @@ struct CinematicMovieItemView: View {
|
|||
var body: some View {
|
||||
ZStack {
|
||||
|
||||
ImageView(src: viewModel.item.getBackdropImage(maxWidth: 1920),
|
||||
bh: viewModel.item.getBackdropImageBlurHash())
|
||||
ImageView(viewModel.item.getBackdropImage(maxWidth: 1920),
|
||||
blurHash: viewModel.item.getBackdropImageBlurHash())
|
||||
.ignoresSafeArea()
|
||||
|
||||
ScrollView {
|
||||
|
|
|
@ -23,7 +23,7 @@ struct CinematicSeasonItemView: View {
|
|||
var body: some View {
|
||||
ZStack {
|
||||
|
||||
ImageView(src: viewModel.item.getBackdropImage(maxWidth: 1920), bh: viewModel.item.getBackdropImageBlurHash())
|
||||
ImageView(viewModel.item.getBackdropImage(maxWidth: 1920), blurHash: viewModel.item.getBackdropImageBlurHash())
|
||||
.ignoresSafeArea()
|
||||
|
||||
ScrollView {
|
||||
|
|
|
@ -23,7 +23,7 @@ struct CinematicSeriesItemView: View {
|
|||
var body: some View {
|
||||
ZStack {
|
||||
|
||||
ImageView(src: viewModel.item.getBackdropImage(maxWidth: 1920), bh: viewModel.item.getBackdropImageBlurHash())
|
||||
ImageView(viewModel.item.getBackdropImage(maxWidth: 1920), blurHash: viewModel.item.getBackdropImageBlurHash())
|
||||
.ignoresSafeArea()
|
||||
|
||||
ScrollView {
|
||||
|
|
|
@ -45,7 +45,7 @@ struct EpisodeItemView: View {
|
|||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
ImageView(src: viewModel.item.getBackdropImage(maxWidth: 1920), bh: viewModel.item.getBackdropImageBlurHash())
|
||||
ImageView(viewModel.item.getBackdropImage(maxWidth: 1920), blurHash: viewModel.item.getBackdropImageBlurHash())
|
||||
.opacity(0.4)
|
||||
.ignoresSafeArea()
|
||||
LazyVStack(alignment: .leading) {
|
||||
|
|
|
@ -50,7 +50,7 @@ struct MovieItemView: View {
|
|||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
ImageView(src: viewModel.item.getBackdropImage(maxWidth: 1920), bh: viewModel.item.getBackdropImageBlurHash())
|
||||
ImageView(viewModel.item.getBackdropImage(maxWidth: 1920), blurHash: viewModel.item.getBackdropImageBlurHash())
|
||||
.opacity(0.4)
|
||||
.ignoresSafeArea()
|
||||
ScrollView {
|
||||
|
|
|
@ -25,7 +25,7 @@ struct SeasonItemView: View {
|
|||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
ImageView(src: viewModel.item.getSeriesBackdropImage(maxWidth: 1920), bh: viewModel.item.getSeriesBackdropImageBlurHash())
|
||||
ImageView(viewModel.item.getSeriesBackdropImage(maxWidth: 1920), blurHash: viewModel.item.getSeriesBackdropImageBlurHash())
|
||||
.opacity(0.4)
|
||||
.ignoresSafeArea()
|
||||
ScrollView {
|
||||
|
|
|
@ -53,7 +53,7 @@ struct SeriesItemView: View {
|
|||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
ImageView(src: viewModel.item.getBackdropImage(maxWidth: 1920), bh: viewModel.item.getBackdropImageBlurHash())
|
||||
ImageView(viewModel.item.getBackdropImage(maxWidth: 1920), blurHash: viewModel.item.getBackdropImageBlurHash())
|
||||
.opacity(0.4)
|
||||
.ignoresSafeArea()
|
||||
ScrollView {
|
||||
|
|
|
@ -34,7 +34,7 @@ struct LatestMediaView: View {
|
|||
Button {
|
||||
homeRouter.route(to: \.modalItem, item)
|
||||
} label: {
|
||||
ImageView(src: item.portraitHeaderViewURL(maxWidth: 257))
|
||||
ImageView(item.portraitHeaderViewURL(maxWidth: 257))
|
||||
.frame(width: 257, height: 380)
|
||||
}
|
||||
.frame(height: 380)
|
||||
|
|
|
@ -21,10 +21,10 @@ struct NextUpCard: View {
|
|||
homeRouter.route(to: \.modalItem, item)
|
||||
} label: {
|
||||
if item.itemType == .episode {
|
||||
ImageView(src: item.getSeriesBackdropImage(maxWidth: 500))
|
||||
ImageView(item.getSeriesBackdropImage(maxWidth: 500))
|
||||
.frame(width: 500, height: 281.25)
|
||||
} else {
|
||||
ImageView(src: item.getBackdropImage(maxWidth: 500))
|
||||
ImageView(item.getBackdropImage(maxWidth: 500))
|
||||
.frame(width: 500, height: 281.25)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -700,7 +700,7 @@ extension LiveTVPlayerViewController: VLCMediaPlayerDelegate {
|
|||
|
||||
// MARK: mediaPlayerStateChanged
|
||||
|
||||
func mediaPlayerStateChanged(_ aNotification: Notification!) {
|
||||
func mediaPlayerStateChanged(_ aNotification: Notification) {
|
||||
|
||||
// Don't show buffering if paused, usually here while scrubbing
|
||||
if vlcMediaPlayer.state == .buffering && viewModel.playerState == .paused {
|
||||
|
@ -720,7 +720,7 @@ extension LiveTVPlayerViewController: VLCMediaPlayerDelegate {
|
|||
|
||||
// MARK: mediaPlayerTimeChanged
|
||||
|
||||
func mediaPlayerTimeChanged(_ aNotification: Notification!) {
|
||||
func mediaPlayerTimeChanged(_ aNotification: Notification) {
|
||||
|
||||
if !viewModel.sliderIsScrubbing {
|
||||
viewModel.sliderPercentage = Double(vlcMediaPlayer.position)
|
||||
|
|
|
@ -329,7 +329,7 @@ struct SmallMediaStreamSelectionView: View {
|
|||
Button {
|
||||
viewModel.playerOverlayDelegate?.didSelectChapter(viewModel.chapters[chapterIndex])
|
||||
} label: {
|
||||
ImageView(src: chapterImages[chapterIndex])
|
||||
ImageView(chapterImages[chapterIndex])
|
||||
.cornerRadius(10)
|
||||
.frame(width: 350, height: 210)
|
||||
}
|
||||
|
|
|
@ -700,7 +700,7 @@ extension VLCPlayerViewController: VLCMediaPlayerDelegate {
|
|||
|
||||
// MARK: mediaPlayerStateChanged
|
||||
|
||||
func mediaPlayerStateChanged(_ aNotification: Notification!) {
|
||||
func mediaPlayerStateChanged(_ aNotification: Notification) {
|
||||
|
||||
// Don't show buffering if paused, usually here while scrubbing
|
||||
if vlcMediaPlayer.state == .buffering && viewModel.playerState == .paused {
|
||||
|
@ -720,7 +720,7 @@ extension VLCPlayerViewController: VLCMediaPlayerDelegate {
|
|||
|
||||
// MARK: mediaPlayerTimeChanged
|
||||
|
||||
func mediaPlayerTimeChanged(_ aNotification: Notification!) {
|
||||
func mediaPlayerTimeChanged(_ aNotification: Notification) {
|
||||
|
||||
if !viewModel.sliderIsScrubbing {
|
||||
viewModel.sliderPercentage = Double(vlcMediaPlayer.position)
|
||||
|
|
|
@ -292,6 +292,10 @@
|
|||
E103A6A5278A82E500820EC7 /* HomeCinematicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E103A6A4278A82E500820EC7 /* HomeCinematicView.swift */; };
|
||||
E103A6A7278AB6D700820EC7 /* CinematicResumeCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E103A6A6278AB6D700820EC7 /* CinematicResumeCardView.swift */; };
|
||||
E103A6A9278AB6FF00820EC7 /* CinematicNextUpCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E103A6A8278AB6FF00820EC7 /* CinematicNextUpCardView.swift */; };
|
||||
E1047E2027E584AF00CB0D4A /* BlurHashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1047E1F27E584AF00CB0D4A /* BlurHashView.swift */; };
|
||||
E1047E2127E584AF00CB0D4A /* BlurHashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1047E1F27E584AF00CB0D4A /* BlurHashView.swift */; };
|
||||
E1047E2327E5880000CB0D4A /* InitialFailureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1047E2227E5880000CB0D4A /* InitialFailureView.swift */; };
|
||||
E1047E2427E5880000CB0D4A /* InitialFailureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1047E2227E5880000CB0D4A /* InitialFailureView.swift */; };
|
||||
E107BB9327880A8F00354E07 /* CollectionItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E107BB9227880A8F00354E07 /* CollectionItemViewModel.swift */; };
|
||||
E107BB9427880A8F00354E07 /* CollectionItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E107BB9227880A8F00354E07 /* CollectionItemViewModel.swift */; };
|
||||
E107BB972788104100354E07 /* CinematicCollectionItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E107BB952788104100354E07 /* CinematicCollectionItemView.swift */; };
|
||||
|
@ -760,6 +764,8 @@
|
|||
E103A6A4278A82E500820EC7 /* HomeCinematicView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCinematicView.swift; sourceTree = "<group>"; };
|
||||
E103A6A6278AB6D700820EC7 /* CinematicResumeCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicResumeCardView.swift; sourceTree = "<group>"; };
|
||||
E103A6A8278AB6FF00820EC7 /* CinematicNextUpCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicNextUpCardView.swift; sourceTree = "<group>"; };
|
||||
E1047E1F27E584AF00CB0D4A /* BlurHashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashView.swift; sourceTree = "<group>"; };
|
||||
E1047E2227E5880000CB0D4A /* InitialFailureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialFailureView.swift; sourceTree = "<group>"; };
|
||||
E107BB9227880A8F00354E07 /* CollectionItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionItemViewModel.swift; sourceTree = "<group>"; };
|
||||
E107BB952788104100354E07 /* CinematicCollectionItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicCollectionItemView.swift; sourceTree = "<group>"; };
|
||||
E10C0940278B8DAB009DBF93 /* PortraitItemSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortraitItemSize.swift; sourceTree = "<group>"; };
|
||||
|
@ -1748,7 +1754,9 @@
|
|||
E1AD105326D96F5A003E4A08 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E1047E1F27E584AF00CB0D4A /* BlurHashView.swift */,
|
||||
531AC8BE26750DE20091C7EB /* ImageView.swift */,
|
||||
E1047E2227E5880000CB0D4A /* InitialFailureView.swift */,
|
||||
621338B22660A07800A81A2A /* LazyView.swift */,
|
||||
C4E52304272CE68800654268 /* LiveTVChannelItemElement.swift */,
|
||||
53E4E648263F725B00F67C6B /* MultiSelectorView.swift */,
|
||||
|
@ -2243,6 +2251,7 @@
|
|||
C4BE078C272844AF003F4AD1 /* LiveTVChannelsView.swift in Sources */,
|
||||
E1D4BF852719D25A00A11E64 /* TrackLanguage.swift in Sources */,
|
||||
53272532268BF09D0035FBF1 /* MediaViewActionButton.swift in Sources */,
|
||||
E1047E2127E584AF00CB0D4A /* BlurHashView.swift in Sources */,
|
||||
531690F0267ABF72005D8AB9 /* NextUpView.swift in Sources */,
|
||||
E193D53427193F7F00900D82 /* HomeCoordinator.swift in Sources */,
|
||||
E193D5502719430400900D82 /* ServerDetailView.swift in Sources */,
|
||||
|
@ -2258,6 +2267,7 @@
|
|||
62E632DD267D2E130063E547 /* LibrarySearchViewModel.swift in Sources */,
|
||||
536D3D81267BDFC60004248C /* PortraitItemElement.swift in Sources */,
|
||||
5D1603FD278A40DB00D22B99 /* SubtitleSize.swift in Sources */,
|
||||
E1047E2427E5880000CB0D4A /* InitialFailureView.swift in Sources */,
|
||||
E1AA33232782648000F6439C /* OverlaySliderColor.swift in Sources */,
|
||||
E103A6A7278AB6D700820EC7 /* CinematicResumeCardView.swift in Sources */,
|
||||
62E1DCC4273CE19800C9AE76 /* URLExtensions.swift in Sources */,
|
||||
|
@ -2390,9 +2400,11 @@
|
|||
E1EBCB42278BD174009FE6E9 /* TruncatedTextView.swift in Sources */,
|
||||
62133890265F83A900A81A2A /* LibraryListView.swift in Sources */,
|
||||
62C29EA326D1030F00C1D2E7 /* ConnectToServerCoodinator.swift in Sources */,
|
||||
E1047E2027E584AF00CB0D4A /* BlurHashView.swift in Sources */,
|
||||
62E632DA267D2BC40063E547 /* LatestMediaViewModel.swift in Sources */,
|
||||
E1AD105C26D9ABDD003E4A08 /* PillHStackView.swift in Sources */,
|
||||
625CB56F2678C23300530A6E /* HomeView.swift in Sources */,
|
||||
E1047E2327E5880000CB0D4A /* InitialFailureView.swift in Sources */,
|
||||
E1CEFBF527914C7700F60429 /* CustomizeViewsSettings.swift in Sources */,
|
||||
E1C812BC277A8E5D00918266 /* PlaybackSpeed.swift in Sources */,
|
||||
E1E5D5492783CDD700692DFE /* OverlaySettingsView.swift in Sources */,
|
||||
|
|
|
@ -50,8 +50,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kaishin/Gifu",
|
||||
"state" : {
|
||||
"revision" : "51f2eab32903e336f590c013267cfa4d7f8b06c4",
|
||||
"version" : "3.3.1"
|
||||
"revision" : "0ffe24744cc3d82ab9edece53670d0352c6d5507",
|
||||
"version" : "3.3.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -66,7 +66,7 @@
|
|||
{
|
||||
"identity" : "nuke",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kean/Nuke",
|
||||
"location" : "https://github.com/kean/Nuke.git",
|
||||
"state" : {
|
||||
"revision" : "78fa963b8491fc520791d8c2a509f1b8593d8aae",
|
||||
"version" : "10.7.1"
|
||||
|
@ -140,8 +140,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/siteline/SwiftUI-Introspect",
|
||||
"state" : {
|
||||
"revision" : "f2616860a41f9d9932da412a8978fec79c06fe24",
|
||||
"version" : "0.1.4"
|
||||
"revision" : "2e09be8af614401bc9f87d40093ec19ce56ccaf2",
|
||||
"version" : "0.1.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -81,7 +81,7 @@ extension AppURLHandler {
|
|||
// It would be nice if the ItemViewModel could be initialized to id later.
|
||||
getItem(userID: userID, itemID: itemID) { item in
|
||||
guard let item = item else { return }
|
||||
SwiftfinNotificationCenter.main.post(name: SwiftfinNotificationCenter.Keys.processDeepLink, object: DeepLink.item(item))
|
||||
Notifications[.processDeepLink].post(object: DeepLink.item(item))
|
||||
}
|
||||
|
||||
return true
|
||||
|
|
|
@ -23,8 +23,8 @@ struct EpisodeRowCard: View {
|
|||
HStack(alignment: .top) {
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
ImageView(src: episode.getBackdropImage(maxWidth: 200),
|
||||
bh: episode.getBackdropImageBlurHash())
|
||||
ImageView(episode.getBackdropImage(maxWidth: 200),
|
||||
blurHash: episode.getBackdropImageBlurHash())
|
||||
.mask(Rectangle().frame(width: 200, height: 112).cornerRadius(10))
|
||||
.frame(width: 200, height: 112)
|
||||
.overlay {
|
||||
|
|
|
@ -43,12 +43,14 @@ struct PortraitImageHStackView<TopBarView: View, ItemType: PortraitImageStackabl
|
|||
selectedAction(item)
|
||||
} label: {
|
||||
VStack(alignment: horizontalAlignment) {
|
||||
ImageView(src: item.imageURLConstructor(maxWidth: Int(maxWidth)),
|
||||
bh: item.blurHash,
|
||||
failureInitials: item.failureInitials)
|
||||
.portraitPoster(width: maxWidth)
|
||||
.shadow(radius: 4, y: 2)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
ImageView(item.imageURLConstructor(maxWidth: Int(maxWidth)),
|
||||
blurHash: item.blurHash,
|
||||
failureView: {
|
||||
InitialFailureView(item.failureInitials)
|
||||
})
|
||||
.portraitPoster(width: maxWidth)
|
||||
.shadow(radius: 4, y: 2)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
|
||||
if item.showTitle {
|
||||
Text(item.title)
|
||||
|
|
|
@ -35,12 +35,14 @@ struct PortraitItemButton<ItemType: PortraitImageStackable>: View {
|
|||
selectedAction(item)
|
||||
} label: {
|
||||
VStack(alignment: horizontalAlignment) {
|
||||
ImageView(src: item.imageURLConstructor(maxWidth: Int(maxWidth)),
|
||||
bh: item.blurHash,
|
||||
failureInitials: item.failureInitials)
|
||||
.portraitPoster(width: maxWidth)
|
||||
.shadow(radius: 4, y: 2)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
ImageView(item.imageURLConstructor(maxWidth: Int(maxWidth)),
|
||||
blurHash: item.blurHash,
|
||||
failureView: {
|
||||
InitialFailureView(item.failureInitials)
|
||||
})
|
||||
.portraitPoster(width: maxWidth)
|
||||
.shadow(radius: 4, y: 2)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
|
||||
if item.showTitle {
|
||||
Text(item.title)
|
||||
|
|
|
@ -24,8 +24,8 @@ struct ItemLandscapeMainView: View {
|
|||
// MARK: Sidebar Image
|
||||
|
||||
VStack {
|
||||
ImageView(src: viewModel.item.portraitHeaderViewURL(maxWidth: 130),
|
||||
bh: viewModel.item.getPrimaryImageBlurHash())
|
||||
ImageView(viewModel.item.portraitHeaderViewURL(maxWidth: 130),
|
||||
blurHash: viewModel.item.getPrimaryImageBlurHash())
|
||||
.frame(width: 130, height: 195)
|
||||
.cornerRadius(10)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
|
@ -95,8 +95,8 @@ struct ItemLandscapeMainView: View {
|
|||
ZStack {
|
||||
// MARK: Backdrop
|
||||
|
||||
ImageView(src: viewModel.item.getBackdropImage(maxWidth: 200),
|
||||
bh: viewModel.item.getBackdropImageBlurHash())
|
||||
ImageView(viewModel.item.getBackdropImage(maxWidth: 200),
|
||||
blurHash: viewModel.item.getBackdropImageBlurHash())
|
||||
.opacity(0.3)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.blur(radius: 8)
|
||||
|
|
|
@ -24,8 +24,8 @@ struct PortraitHeaderOverlayView: View {
|
|||
|
||||
// MARK: Portrait Image
|
||||
|
||||
ImageView(src: viewModel.item.portraitHeaderViewURL(maxWidth: 130),
|
||||
bh: viewModel.item.getPrimaryImageBlurHash())
|
||||
ImageView(viewModel.item.portraitHeaderViewURL(maxWidth: 130),
|
||||
blurHash: viewModel.item.getPrimaryImageBlurHash())
|
||||
.portraitPoster(width: 130)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@ struct ItemPortraitMainView: View {
|
|||
// MARK: portraitHeaderView
|
||||
|
||||
var portraitHeaderView: some View {
|
||||
ImageView(src: viewModel.item.getBackdropImage(maxWidth: Int(UIScreen.main.bounds.width)),
|
||||
bh: viewModel.item.getBackdropImageBlurHash())
|
||||
ImageView(viewModel.item.getBackdropImage(maxWidth: Int(UIScreen.main.bounds.width)),
|
||||
blurHash: viewModel.item.getBackdropImageBlurHash())
|
||||
.opacity(0.4)
|
||||
.blur(radius: 2.0)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
|
|
|
@ -54,7 +54,7 @@ struct LibraryListView: View {
|
|||
title: library.name ?? ""))
|
||||
} label: {
|
||||
ZStack {
|
||||
ImageView(src: library.getPrimaryImage(maxWidth: 500), bh: library.getPrimaryImageBlurHash())
|
||||
ImageView(library.getPrimaryImage(maxWidth: 500), blurHash: library.getPrimaryImageBlurHash())
|
||||
.opacity(0.4)
|
||||
.accessibilityIgnoresInvertColors()
|
||||
HStack {
|
||||
|
|
|
@ -48,7 +48,7 @@ struct VLCPlayerChapterOverlayView: View {
|
|||
Button {
|
||||
viewModel.playerOverlayDelegate?.didSelectChapter(viewModel.chapters[chapterIndex])
|
||||
} label: {
|
||||
ImageView(src: chapterImages[chapterIndex])
|
||||
ImageView(chapterImages[chapterIndex])
|
||||
.cornerRadius(10)
|
||||
.frame(width: 150, height: 100)
|
||||
.overlay {
|
||||
|
|
|
@ -755,7 +755,7 @@ extension VLCPlayerViewController {
|
|||
extension VLCPlayerViewController: VLCMediaPlayerDelegate {
|
||||
// MARK: mediaPlayerStateChanged
|
||||
|
||||
func mediaPlayerStateChanged(_ aNotification: Notification!) {
|
||||
func mediaPlayerStateChanged(_ aNotification: Notification) {
|
||||
// Don't show buffering if paused, usually here while scrubbing
|
||||
if vlcMediaPlayer.state == .buffering, viewModel.playerState == .paused {
|
||||
return
|
||||
|
@ -774,7 +774,7 @@ extension VLCPlayerViewController: VLCMediaPlayerDelegate {
|
|||
|
||||
// MARK: mediaPlayerTimeChanged
|
||||
|
||||
func mediaPlayerTimeChanged(_ aNotification: Notification!) {
|
||||
func mediaPlayerTimeChanged(_ aNotification: Notification) {
|
||||
if !viewModel.sliderIsScrubbing {
|
||||
viewModel.sliderPercentage = Double(vlcMediaPlayer.position)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue