jellyflood/Swiftfin/Views/ChannelLibraryView/ChannelLibraryView.swift

101 lines
2.8 KiB
Swift

//
// 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) 2024 Jellyfin & Jellyfin Contributors
//
import CollectionVGrid
import Defaults
import Foundation
import JellyfinAPI
import SwiftUI
// TODO: wide + narrow view toggling
// - after `PosterType` has been refactored and with customizable toggle button
// TODO: sorting by number/filtering
// - should be able to use normal filter view model, but how to add custom filters for data context?
struct ChannelLibraryView: View {
@EnvironmentObject
private var mainRouter: MainCoordinator.Router
@State
private var layout: CollectionVGridLayout
@StateObject
private var viewModel = ChannelLibraryViewModel()
init() {
if UIDevice.isPhone {
layout = .columns(1)
} else {
layout = .minWidth(250)
}
}
private var contentView: some View {
CollectionVGrid(
$viewModel.elements,
layout: layout
) { channel in
WideChannelGridItem(channel: channel)
.onSelect {
guard let mediaSource = channel.channel.mediaSources?.first else { return }
mainRouter.route(
to: \.liveVideoPlayer,
LiveVideoPlayerManager(item: channel.channel, mediaSource: mediaSource)
)
}
}
.onReachedBottomEdge(offset: .offset(300)) {
viewModel.send(.getNextPage)
}
}
private func errorView(with error: some Error) -> some View {
ErrorView(error: error)
.onRetry {
viewModel.send(.refresh)
}
}
var body: some View {
WrappedView {
switch viewModel.state {
case .content:
if viewModel.elements.isEmpty {
L10n.noResults.text
} else {
contentView
}
case let .error(error):
errorView(with: error)
case .initial, .refreshing:
DelayedProgressView()
}
}
.navigationTitle(L10n.channels)
.navigationBarTitleDisplayMode(.inline)
.onFirstAppear {
if viewModel.state == .initial {
viewModel.send(.refresh)
}
}
.afterLastDisappear { interval in
// refresh after 3 hours
if interval >= 10800 {
viewModel.send(.refresh)
}
}
.topBarTrailing {
if viewModel.backgroundStates.contains(.gettingNextPage) {
ProgressView()
}
}
}
}