Various Things (#581)

This commit is contained in:
Ethan Pippin 2022-09-12 23:02:06 -06:00 committed by GitHub
parent 83f9c1c81c
commit 5299f5a9ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 94 additions and 118 deletions

View File

@ -17,12 +17,11 @@ final class SearchCoordinator: NavigationCoordinatable {
@Root
var start = makeStart
#if os(tvOS)
@Route(.modal)
var item = makeItem
#else
@Route(.push)
var item = makeItem
var library = makeLibrary
#if !os(tvOS)
@Route(.modal)
var filter = makeFilter
#endif
@ -31,11 +30,19 @@ final class SearchCoordinator: NavigationCoordinatable {
func makeItem(item: BaseItemDto) -> NavigationViewCoordinator<ItemCoordinator> {
NavigationViewCoordinator(ItemCoordinator(item: item))
}
func makeLibrary(parameters: LibraryCoordinator.Parameters) -> NavigationViewCoordinator<LibraryCoordinator> {
NavigationViewCoordinator(LibraryCoordinator(parameters: parameters))
}
#else
func makeItem(item: BaseItemDto) -> ItemCoordinator {
ItemCoordinator(item: item)
}
func makeLibrary(parameters: LibraryCoordinator.Parameters) -> LibraryCoordinator {
LibraryCoordinator(parameters: parameters)
}
func makeFilter(parameters: FilterCoordinator.Parameters) -> NavigationViewCoordinator<FilterCoordinator> {
NavigationViewCoordinator(FilterCoordinator(parameters: parameters))
}

View File

@ -16,4 +16,10 @@ extension UIScreen {
func scale(_ x: CGFloat) -> Int {
Int(nativeScale * x)
}
func maxChildren(width: CGFloat, height: CGFloat) -> Int {
let screenSize = bounds.height * bounds.width
let itemSize = width * height
return Int(screenSize / itemSize)
}
}

View File

@ -36,7 +36,7 @@ final class LibraryViewModel: ViewModel {
filterViewModel.$currentFilters
.sink { newFilters in
self.requestItemsAsync(with: newFilters, replaceCurrentItems: true)
self.requestItems(with: newFilters, replaceCurrentItems: true)
}
.store(in: &cancellables)
}
@ -53,17 +53,17 @@ final class LibraryViewModel: ViewModel {
filterViewModel.$currentFilters
.sink { newFilters in
self.requestItemsAsync(with: newFilters, replaceCurrentItems: true)
self.requestItems(with: newFilters, replaceCurrentItems: true)
}
.store(in: &cancellables)
}
private var pageItemSize: Int {
let height = libraryGridPosterType == .portrait ? libraryGridPosterType.width * 1.5 : libraryGridPosterType.width / 1.77
return UIScreen.itemsFillableOnScreen(width: libraryGridPosterType.width, height: height)
return UIScreen.main.maxChildren(width: libraryGridPosterType.width, height: height)
}
func requestItemsAsync(with filters: ItemFilters, replaceCurrentItems: Bool = false) {
private func requestItems(with filters: ItemFilters, replaceCurrentItems: Bool = false) {
if replaceCurrentItems {
self.items = []
@ -156,18 +156,9 @@ final class LibraryViewModel: ViewModel {
.store(in: &cancellables)
}
func requestNextPageAsync() {
func requestNextPage() {
guard hasNextPage else { return }
currentPage += 1
requestItemsAsync(with: filterViewModel.currentFilters)
}
}
extension UIScreen {
static func itemsFillableOnScreen(width: CGFloat, height: CGFloat) -> Int {
let screenSize = UIScreen.main.bounds.height * UIScreen.main.bounds.width
let itemSize = width * height
return Int(screenSize / itemSize)
requestItems(with: filterViewModel.currentFilters)
}
}

View File

@ -12,18 +12,9 @@ extension ItemView {
struct AboutView: View {
@EnvironmentObject
private var itemRouter: ItemCoordinator.Router
@ObservedObject
var viewModel: ItemViewModel
@State
private var presentOverviewAlert = false
@State
private var presentSubtitlesAlert = false
@State
private var presentAudioAlert = false
var body: some View {
VStack(alignment: .leading) {
@ -33,7 +24,7 @@ extension ItemView {
.padding(.leading, 50)
ScrollView(.horizontal) {
HStack {
HStack(spacing: 30) {
ImageView(
viewModel.item.type == .episode ? viewModel.item.seriesImageSource(.primary, maxWidth: 300) : viewModel.item
.imageSource(.primary, maxWidth: 300)
@ -43,26 +34,20 @@ extension ItemView {
}
.posterStyle(type: .portrait, width: 270)
AboutViewCard(
isShowingAlert: $presentOverviewAlert,
InformationCard(
title: viewModel.item.displayName,
text: viewModel.item.overview ?? L10n.noOverviewAvailable
content: viewModel.item.overview ?? L10n.noOverviewAvailable
)
if let subtitleStreams = viewModel.playButtonItem?.subtitleStreams, !subtitleStreams.isEmpty {
AboutViewCard(
isShowingAlert: $presentSubtitlesAlert,
InformationCard(
title: L10n.subtitles,
text: subtitleStreams.compactMap(\.displayTitle).joined(separator: ", ")
content: subtitleStreams.compactMap(\.displayTitle).joined(separator: ", ")
)
}
if let audioStreams = viewModel.playButtonItem?.audioStreams, !audioStreams.isEmpty {
AboutViewCard(
isShowingAlert: $presentAudioAlert,
title: L10n.audio,
text: audioStreams.compactMap(\.displayTitle).joined(separator: ", ")
)
InformationCard(title: L10n.audio, content: audioStreams.compactMap(\.displayTitle).joined(separator: ", "))
}
}
.padding(.horizontal, 50)
@ -71,39 +56,6 @@ extension ItemView {
}
}
.focusSection()
.alert(viewModel.item.displayName, isPresented: $presentOverviewAlert) {
Button {
presentOverviewAlert = false
} label: {
L10n.close.text
}
} message: {
if let overview = viewModel.item.overview {
overview.text
} else {
L10n.noOverviewAvailable.text
}
}
.alert(L10n.subtitles, isPresented: $presentSubtitlesAlert) {
Button {
presentSubtitlesAlert = false
} label: {
L10n.close.text
}
} message: {
viewModel.item.subtitleStreams.compactMap(\.displayTitle).joined(separator: ", ")
.text
}
.alert(L10n.audio, isPresented: $presentAudioAlert) {
Button {
presentAudioAlert = false
} label: {
L10n.close.text
}
} message: {
viewModel.item.audioStreams.compactMap(\.displayTitle).joined(separator: ", ")
.text
}
}
}
}

View File

@ -10,17 +10,17 @@ import SwiftUI
extension ItemView.AboutView {
struct AboutViewCard: View {
struct InformationCard: View {
@Binding
var isShowingAlert: Bool
@State
private var presentingAlert: Bool = false
let title: String
let text: String
let content: String
var body: some View {
Button {
isShowingAlert = true
presentingAlert = true
} label: {
VStack(alignment: .leading) {
title.text
@ -30,7 +30,7 @@ extension ItemView.AboutView {
Spacer()
TruncatedTextView(text: text, seeMoreAction: {})
TruncatedTextView(text: content, seeMoreAction: {})
.font(.subheadline)
.lineLimit(4)
}
@ -38,6 +38,15 @@ extension ItemView.AboutView {
.frame(width: 700, height: 405)
}
.buttonStyle(.card)
.alert(title, isPresented: $presentingAlert) {
Button {
presentingAlert = false
} label: {
L10n.close.text
}
} message: {
Text(content)
}
}
}
}

View File

@ -29,8 +29,6 @@ struct ItemView: View {
SeriesItemView(viewModel: .init(item: item))
case .boxSet:
CollectionItemView(viewModel: .init(item: item))
case .person:
LibraryView(viewModel: .init(parent: item, type: .person, filters: .init()))
default:
Text(L10n.notImplementedYetWithType(item.type ?? "--"))
}

View File

@ -14,8 +14,6 @@ struct SeriesEpisodesView: View {
@ObservedObject
var viewModel: SeriesItemViewModel
@FocusState
private var isFocused: Bool
@EnvironmentObject
private var parentFocusGuide: FocusGuide
@ -68,7 +66,7 @@ extension SeriesEpisodesView {
focusGuide,
tag: "seasons",
onContentFocus: { focusedSeason = viewModel.selectedSeason },
top: "mediaButtons",
top: "top",
bottom: "episodes"
)
.frame(height: 70)
@ -124,14 +122,28 @@ extension SeriesEpisodesView {
.padding(.bottom, 50)
.padding(.top)
}
.mask {
VStack(spacing: 0) {
Color.white
LinearGradient(
stops: [
.init(color: .white, location: 0),
.init(color: .clear, location: 1),
],
startPoint: .top,
endPoint: .bottom
)
.frame(height: 20)
}
}
.transition(.opacity)
.focusGuide(
focusGuide,
tag: "episodes",
onContentFocus: { focusedEpisodeID = lastFocusedEpisodeID },
top: "seasons",
bottom: "recommended"
top: "seasons"
)
.transition(.opacity)
.introspectScrollView { scrollView in
wrappedScrollView = scrollView
}

View File

@ -14,20 +14,23 @@ extension SeriesItemView {
struct ContentView: View {
@ObservedObject
private var focusGuide = FocusGuide()
@ObservedObject
var viewModel: SeriesItemViewModel
@EnvironmentObject
private var itemRouter: ItemCoordinator.Router
var body: some View {
VStack(spacing: 0) {
ItemView.CinematicHeaderView(viewModel: viewModel)
.focusGuide(focusGuide, tag: "top", bottom: "seasons")
.frame(height: UIScreen.main.bounds.height - 150)
.padding(.bottom, 50)
SeriesEpisodesView(viewModel: viewModel)
.environmentObject(focusGuide)
ItemView.CastAndCrewHStack(people: viewModel.item.people?.filter(\.isDisplayed) ?? [])
ItemView.SimilarItemsHStack(items: viewModel.similarItems)

View File

@ -65,7 +65,7 @@ struct LibraryView: View {
}
.willReachEdge(insets: .init(top: 0, leading: 0, bottom: 600, trailing: 0)) { edge in
if !viewModel.isLoading && edge == .bottom {
viewModel.requestNextPageAsync()
viewModel.requestNextPage()
}
}
.scrollViewOffset($scrollViewOffset)

View File

@ -63,6 +63,14 @@ struct SearchView: View {
.ignoresSafeArea()
}
private func baseItemOnSelect(_ item: BaseItemDto) {
if item.type == .person {
router.route(to: \.library, .init(parent: item, type: .person, filters: .init()))
} else {
router.route(to: \.item, item)
}
}
@ViewBuilder
private func itemsSection(
title: String,
@ -74,7 +82,7 @@ struct SearchView: View {
items: viewModel[keyPath: keyPath]
)
.onSelect { item in
router.route(to: \.item, item)
baseItemOnSelect(item)
}
}

View File

@ -858,10 +858,6 @@
E18E01D8288747230022598C /* PlayButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayButton.swift; sourceTree = "<group>"; };
E18E01D9288747230022598C /* ActionButtonHStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionButtonHStack.swift; sourceTree = "<group>"; };
E18E01F3288747580022598C /* AboutAppView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AboutAppView.swift; sourceTree = "<group>"; };
E18E01F5288747580022598C /* HomeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
E18E01F6288747580022598C /* HomeContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeContentView.swift; sourceTree = "<group>"; };
E18E01F8288747580022598C /* LatestInLibraryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LatestInLibraryView.swift; sourceTree = "<group>"; };
E18E01F9288747580022598C /* HomeErrorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeErrorView.swift; sourceTree = "<group>"; };
E18E01FF288749200022598C /* Divider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Divider.swift; sourceTree = "<group>"; };
E18E0200288749200022598C /* AppIcon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppIcon.swift; sourceTree = "<group>"; };
E18E0201288749200022598C /* AttributeFillView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttributeFillView.swift; sourceTree = "<group>"; };
@ -872,7 +868,6 @@
E1937A3A288E54AD00CB80AA /* BaseItemDto+Images.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseItemDto+Images.swift"; sourceTree = "<group>"; };
E1937A3D288F0D3D00CB80AA /* UIScreenExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScreenExtensions.swift; sourceTree = "<group>"; };
E1937A60288F32DB00CB80AA /* Poster.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Poster.swift; sourceTree = "<group>"; };
E1937A63288F683300CB80AA /* ContinueWatchingCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContinueWatchingCard.swift; sourceTree = "<group>"; };
E193D5422719407E00900D82 /* tvOSMainCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = tvOSMainCoordinator.swift; sourceTree = "<group>"; };
E193D546271941C500900D82 /* UserListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserListView.swift; sourceTree = "<group>"; };
E193D548271941CC00900D82 /* UserSignInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSignInView.swift; sourceTree = "<group>"; };
@ -1103,11 +1098,13 @@
children = (
53913BDA26D323FE00EB3286 /* de.lproj */,
53913BE326D323FE00EB3286 /* el.lproj */,
534D4FE626A7D7CC000A7A48 /* en.lproj */,
53913BE026D323FE00EB3286 /* es.lproj */,
53913BC826D323FE00EB3286 /* fr.lproj */,
53913BE626D323FE00EB3286 /* he.lproj */,
53913BCE26D323FE00EB3286 /* it.lproj */,
53913BEC26D323FE00EB3286 /* kk.lproj */,
534D4FEA26A7D7CC000A7A48 /* ko.lproj */,
53913BCB26D323FE00EB3286 /* ru.lproj */,
53913BE926D323FE00EB3286 /* sk.lproj */,
53913BD726D323FE00EB3286 /* sl.lproj */,
@ -1115,8 +1112,6 @@
53913BDD26D323FE00EB3286 /* ta.lproj */,
53913BD126D323FE00EB3286 /* vi.lproj */,
534D4FED26A7D7CC000A7A48 /* zh-Hans.lproj */,
534D4FE626A7D7CC000A7A48 /* en.lproj */,
534D4FEA26A7D7CC000A7A48 /* ko.lproj */,
);
path = Translations;
sourceTree = "<group>";
@ -1234,13 +1229,12 @@
5377CBE8263B596A003A4E83 = {
isa = PBXGroup;
children = (
534D4FE126A7D7CC000A7A48 /* Translations */,
53D5E3DB264B47EE00BADDC8 /* Frameworks */,
5377CBF3263B596A003A4E83 /* Swiftfin */,
535870612669D21600D05A09 /* Swiftfin tvOS */,
5377CBF2263B596A003A4E83 /* Products */,
535870752669D60C00D05A09 /* Shared */,
E168BD06289A414B001A6922 /* Recovered References */,
534D4FE126A7D7CC000A7A48 /* Translations */,
5377CBF2263B596A003A4E83 /* Products */,
53D5E3DB264B47EE00BADDC8 /* Frameworks */,
);
sourceTree = "<group>";
};
@ -1256,12 +1250,12 @@
5377CBF3263B596A003A4E83 /* Swiftfin */ = {
isa = PBXGroup;
children = (
E1DD1127271E7D15005BE12F /* Objects */,
E13DD3BB27163C3E009D4DAF /* App */,
62ECA01926FA6D6900E8EBB7 /* AppURLHandler */,
5377CBF8263B596B003A4E83 /* Assets.xcassets */,
53F866422687A45400DCD1D7 /* Components */,
5377CC02263B596B003A4E83 /* Info.plist */,
E1DD1127271E7D15005BE12F /* Objects */,
E13D02842788B634000FCB04 /* Swiftfin.entitlements */,
E11CEB85289984F5003E74C7 /* Extensions */,
5377CBFA263B596B003A4E83 /* Preview Content */,
@ -1777,18 +1771,6 @@
path = CollectionItemView;
sourceTree = "<group>";
};
E168BD06289A414B001A6922 /* Recovered References */ = {
isa = PBXGroup;
children = (
E18E01F9288747580022598C /* HomeErrorView.swift */,
E18E01F8288747580022598C /* LatestInLibraryView.swift */,
E18E01F6288747580022598C /* HomeContentView.swift */,
E18E01F5288747580022598C /* HomeView.swift */,
E1937A63288F683300CB80AA /* ContinueWatchingCard.swift */,
);
name = "Recovered References";
sourceTree = "<group>";
};
E168BD07289A4162001A6922 /* HomeView */ = {
isa = PBXGroup;
children = (

View File

@ -69,7 +69,7 @@ struct LibraryView: View {
}
.willReachEdge(insets: .init(top: 0, leading: 0, bottom: 200, trailing: 0)) { edge in
if !viewModel.isLoading && edge == .bottom {
viewModel.requestNextPageAsync()
viewModel.requestNextPage()
}
}
.configure { configuration in
@ -96,7 +96,7 @@ struct LibraryView: View {
}
.willReachEdge(insets: .init(top: 0, leading: 0, bottom: 200, trailing: 0)) { edge in
if !viewModel.isLoading && edge == .bottom {
viewModel.requestNextPageAsync()
viewModel.requestNextPage()
}
}
.configure { configuration in

View File

@ -66,6 +66,14 @@ struct SearchView: View {
}
}
private func baseItemOnSelect(_ item: BaseItemDto) {
if item.type == .person {
router.route(to: \.library, .init(parent: item, type: .person, filters: .init()))
} else {
router.route(to: \.item, item)
}
}
@ViewBuilder
private func itemsSection(
title: String,
@ -78,7 +86,7 @@ struct SearchView: View {
items: viewModel[keyPath: keyPath]
)
.onSelect { item in
router.route(to: \.item, item)
baseItemOnSelect(item)
}
}