From 14c8aa41010c2a1a0956034f047ec1e09829befd Mon Sep 17 00:00:00 2001 From: Ethan Pippin Date: Sun, 9 Jan 2022 18:22:38 -0700 Subject: [PATCH 1/4] create general poster size --- .../ItemViewModel/ItemViewModel.swift | 15 +++++++------- Shared/Views/PortraitItemSize.swift | 19 ++++++++++++++++++ Swiftfin.xcodeproj/project.pbxproj | 20 ++++++++++++------- .../EpisodesRowView.swift | 0 Swiftfin/Components/PortraitHStackView.swift | 3 +-- .../ItemPortraitHeaderOverlayView.swift | 3 +-- 6 files changed, 41 insertions(+), 19 deletions(-) create mode 100644 Shared/Views/PortraitItemSize.swift rename Swiftfin/{Views/ItemView => Components}/EpisodesRowView.swift (100%) diff --git a/Shared/ViewModels/ItemViewModel/ItemViewModel.swift b/Shared/ViewModels/ItemViewModel/ItemViewModel.swift index 85ead04a..cd06f9da 100644 --- a/Shared/ViewModels/ItemViewModel/ItemViewModel.swift +++ b/Shared/ViewModels/ItemViewModel/ItemViewModel.swift @@ -17,14 +17,9 @@ class ItemViewModel: ViewModel { @Published var item: BaseItemDto @Published var playButtonItem: BaseItemDto? { didSet { - playButtonItem?.createVideoPlayerViewModel() - .sink { completion in - self.handleAPIRequestError(completion: completion) - } receiveValue: { videoPlayerViewModel in - self.itemVideoPlayerViewModel = videoPlayerViewModel - self.mediaItems = videoPlayerViewModel.item.createMediaItems() - } - .store(in: &cancellables) + if let playButtonItem = playButtonItem { + refreshItemVideoPlayerViewModel(for: playButtonItem) + } } } @Published var similarItems: [BaseItemDto] = [] @@ -52,6 +47,10 @@ class ItemViewModel: ViewModel { getSimilarItems() + refreshItemVideoPlayerViewModel(for: item) + } + + func refreshItemVideoPlayerViewModel(for item: BaseItemDto) { item.createVideoPlayerViewModel() .sink { completion in self.handleAPIRequestError(completion: completion) diff --git a/Shared/Views/PortraitItemSize.swift b/Shared/Views/PortraitItemSize.swift new file mode 100644 index 00000000..62cf5241 --- /dev/null +++ b/Shared/Views/PortraitItemSize.swift @@ -0,0 +1,19 @@ +// + /* + * 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 2021 Aiden Vigue & Jellyfin Contributors + */ + +import SwiftUI + +extension View { + + /// Applies Portrait Poster frame with proper corner radius ratio against the width + func portraitPoster(width: CGFloat) -> some View { + self.frame(width: width, height: width * 1.5) + .cornerRadius(width / 24) + } +} diff --git a/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index 428784fe..2779af7c 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -254,6 +254,8 @@ 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 */; }; + E10C0941278B8DAB009DBF93 /* PortraitItemSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10C0940278B8DAB009DBF93 /* PortraitItemSize.swift */; }; + E10C0942278B8DAB009DBF93 /* PortraitItemSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10C0940278B8DAB009DBF93 /* PortraitItemSize.swift */; }; E10D87DA2784E4F100BD264C /* ItemViewDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10D87D92784E4F100BD264C /* ItemViewDetailsView.swift */; }; E10D87DC2784EC5200BD264C /* EpisodesRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10D87DB2784EC5200BD264C /* EpisodesRowView.swift */; }; E10D87DE278510E400BD264C /* PosterSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E10D87DD278510E300BD264C /* PosterSize.swift */; }; @@ -650,6 +652,7 @@ E103A6A8278AB6FF00820EC7 /* CinematicNextUpCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicNextUpCardView.swift; sourceTree = ""; }; E107BB9227880A8F00354E07 /* CollectionItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionItemViewModel.swift; sourceTree = ""; }; E107BB952788104100354E07 /* CinematicCollectionItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicCollectionItemView.swift; sourceTree = ""; }; + E10C0940278B8DAB009DBF93 /* PortraitItemSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortraitItemSize.swift; sourceTree = ""; }; E10D87D92784E4F100BD264C /* ItemViewDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemViewDetailsView.swift; sourceTree = ""; }; E10D87DB2784EC5200BD264C /* EpisodesRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EpisodesRowView.swift; sourceTree = ""; }; E10D87DD278510E300BD264C /* PosterSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PosterSize.swift; sourceTree = ""; }; @@ -1193,6 +1196,7 @@ isa = PBXGroup; children = ( E188460326DEF04800B0C5B7 /* EpisodeCardVStackView.swift */, + E10D87DB2784EC5200BD264C /* EpisodesRowView.swift */, E1AD105B26D9ABDD003E4A08 /* PillHStackView.swift */, E1AD105526D981CE003E4A08 /* PortraitHStackView.swift */, C4BE076D2720FEA8003F4AD1 /* PortraitItemElement.swift */, @@ -1453,7 +1457,6 @@ E14F7D0A26DB3714007C3AE6 /* ItemView */ = { isa = PBXGroup; children = ( - E10D87DB2784EC5200BD264C /* EpisodesRowView.swift */, 535BAE9E2649E569005FA86D /* ItemView.swift */, E18845F726DEA9C900B0C5B7 /* ItemViewBody.swift */, E10D87D92784E4F100BD264C /* ItemViewDetailsView.swift */, @@ -1550,12 +1553,13 @@ E1AD105326D96F5A003E4A08 /* Views */ = { isa = PBXGroup; children = ( - C4E52304272CE68800654268 /* LiveTVChannelItemElement.swift */, - 531690F9267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift */, 531AC8BE26750DE20091C7EB /* ImageView.swift */, 621338B22660A07800A81A2A /* LazyView.swift */, + C4E52304272CE68800654268 /* LiveTVChannelItemElement.swift */, 53E4E648263F725B00F67C6B /* MultiSelectorView.swift */, 6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */, + 531690F9267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift */, + E10C0940278B8DAB009DBF93 /* PortraitItemSize.swift */, 624C21742685CF60007F1390 /* SearchablePickerView.swift */, 53DE4BD1267098F300739748 /* SearchBarView.swift */, ); @@ -2195,6 +2199,7 @@ 6264E88D273850380081A12A /* Strings.swift in Sources */, 536D3D76267BA9BB0004248C /* MainTabViewModel.swift in Sources */, E193D5512719432400900D82 /* ServerDetailViewModel.swift in Sources */, + E10C0942278B8DAB009DBF93 /* PortraitItemSize.swift in Sources */, C4E5081B2703F82A0045C9AB /* LibraryListView.swift in Sources */, E193D53B27193F9200900D82 /* SettingsCoordinator.swift in Sources */, E1E5D5392783A56B00692DFE /* EpisodesRowView.swift in Sources */, @@ -2319,6 +2324,7 @@ E10D87DC2784EC5200BD264C /* EpisodesRowView.swift in Sources */, E1C812BE277A8E5D00918266 /* VLCPlayerViewController.swift in Sources */, E1AA331D2782541500F6439C /* PrimaryButtonView.swift in Sources */, + E10C0941278B8DAB009DBF93 /* PortraitItemSize.swift in Sources */, 62C29EA626D1036A00C1D2E7 /* HomeCoordinator.swift in Sources */, 531AC8BF26750DE20091C7EB /* ImageView.swift in Sources */, C4BE07762725EBEA003F4AD1 /* LiveTVProgramsViewModel.swift in Sources */, @@ -2745,7 +2751,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 66; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = TY84JMYEFE; ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; EXCLUDED_ARCHS = ""; @@ -2782,7 +2788,7 @@ CURRENT_PROJECT_VERSION = 66; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = TY84JMYEFE; ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; EXCLUDED_ARCHS = ""; @@ -2813,7 +2819,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 66; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = TY84JMYEFE; INFOPLIST_FILE = WidgetExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -2840,7 +2846,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 66; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = TY84JMYEFE; INFOPLIST_FILE = WidgetExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Swiftfin/Views/ItemView/EpisodesRowView.swift b/Swiftfin/Components/EpisodesRowView.swift similarity index 100% rename from Swiftfin/Views/ItemView/EpisodesRowView.swift rename to Swiftfin/Components/EpisodesRowView.swift diff --git a/Swiftfin/Components/PortraitHStackView.swift b/Swiftfin/Components/PortraitHStackView.swift index d2c55051..30324336 100644 --- a/Swiftfin/Components/PortraitHStackView.swift +++ b/Swiftfin/Components/PortraitHStackView.swift @@ -46,8 +46,7 @@ struct PortraitImageHStackView Date: Sun, 9 Jan 2022 20:00:41 -0700 Subject: [PATCH 2/4] iOS truncated item overview --- Shared/Coordinators/ItemCoordinator.swift | 5 + .../ItemOverviewCoordinator.swift | 29 +++++ Shared/Extensions/ColorExtension.swift | 1 + Swiftfin.xcodeproj/project.pbxproj | 22 +++- Swiftfin/Components/TruncatedTextView.swift | 106 ++++++++++++++++++ Swiftfin/Views/ItemOverviewView.swift | 35 ++++++ Swiftfin/Views/ItemView/ItemViewBody.swift | 26 ++++- 7 files changed, 214 insertions(+), 10 deletions(-) create mode 100644 Shared/Coordinators/ItemOverviewCoordinator.swift create mode 100644 Swiftfin/Components/TruncatedTextView.swift create mode 100644 Swiftfin/Views/ItemOverviewView.swift diff --git a/Shared/Coordinators/ItemCoordinator.swift b/Shared/Coordinators/ItemCoordinator.swift index 6c9e6c44..f9d13164 100644 --- a/Shared/Coordinators/ItemCoordinator.swift +++ b/Shared/Coordinators/ItemCoordinator.swift @@ -19,6 +19,7 @@ final class ItemCoordinator: NavigationCoordinatable { @Root var start = makeStart @Route(.push) var item = makeItem @Route(.push) var library = makeLibrary + @Route(.modal) var itemOverview = makeItemOverview @Route(.fullScreen) var videoPlayer = makeVideoPlayer let itemDto: BaseItemDto @@ -34,6 +35,10 @@ final class ItemCoordinator: NavigationCoordinatable { func makeItem(item: BaseItemDto) -> ItemCoordinator { ItemCoordinator(item: item) } + + func makeItemOverview(item: BaseItemDto) -> NavigationViewCoordinator { + NavigationViewCoordinator(ItemOverviewCoordinator(item: itemDto)) + } func makeVideoPlayer(viewModel: VideoPlayerViewModel) -> NavigationViewCoordinator { NavigationViewCoordinator(VideoPlayerCoordinator(viewModel: viewModel)) diff --git a/Shared/Coordinators/ItemOverviewCoordinator.swift b/Shared/Coordinators/ItemOverviewCoordinator.swift new file mode 100644 index 00000000..cf80b4f9 --- /dev/null +++ b/Shared/Coordinators/ItemOverviewCoordinator.swift @@ -0,0 +1,29 @@ +// + /* + * 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 2021 Aiden Vigue & Jellyfin Contributors + */ + +import Stinsen +import SwiftUI +import JellyfinAPI + +final class ItemOverviewCoordinator: NavigationCoordinatable { + + let stack = NavigationStack(initial: \ItemOverviewCoordinator.start) + + @Root var start = makeStart + + let item: BaseItemDto + + init(item: BaseItemDto) { + self.item = item + } + + @ViewBuilder func makeStart() -> some View { + ItemOverviewView(item: item) + } +} diff --git a/Shared/Extensions/ColorExtension.swift b/Shared/Extensions/ColorExtension.swift index edc56d4c..b5869500 100644 --- a/Shared/Extensions/ColorExtension.swift +++ b/Shared/Extensions/ColorExtension.swift @@ -20,6 +20,7 @@ extension Color { public static let lightGray = Color(UIColor.lightGray) #else public static let systemFill = Color(UIColor.systemFill) + public static let systemBackground = Color(UIColor.systemBackground) public static let secondarySystemFill = Color(UIColor.secondarySystemBackground) public static let tertiarySystemFill = Color(UIColor.tertiarySystemBackground) #endif diff --git a/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index 2779af7c..d0ced680 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -430,6 +430,9 @@ E1E5D54F2783E67100692DFE /* OverlaySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D54E2783E67100692DFE /* OverlaySettingsView.swift */; }; E1E5D5512783E67700692DFE /* ExperimentalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D5502783E67700692DFE /* ExperimentalSettingsView.swift */; }; E1E5D553278419D900692DFE /* ConfirmCloseOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D552278419D900692DFE /* ConfirmCloseOverlay.swift */; }; + E1EBCB42278BD174009FE6E9 /* TruncatedTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EBCB41278BD174009FE6E9 /* TruncatedTextView.swift */; }; + E1EBCB44278BD1CE009FE6E9 /* ItemOverviewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EBCB43278BD1CE009FE6E9 /* ItemOverviewCoordinator.swift */; }; + E1EBCB46278BD595009FE6E9 /* ItemOverviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EBCB45278BD595009FE6E9 /* ItemOverviewView.swift */; }; E1F0204E26CCCA74001C1C3B /* VideoPlayerJumpLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */; }; E1F0204F26CCCA74001C1C3B /* VideoPlayerJumpLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */; }; E1FA2F7427818A8800B4C270 /* SmallMenuOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FA2F7327818A8800B4C270 /* SmallMenuOverlay.swift */; }; @@ -750,6 +753,9 @@ E1E5D54E2783E67100692DFE /* OverlaySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverlaySettingsView.swift; sourceTree = ""; }; E1E5D5502783E67700692DFE /* ExperimentalSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExperimentalSettingsView.swift; sourceTree = ""; }; E1E5D552278419D900692DFE /* ConfirmCloseOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmCloseOverlay.swift; sourceTree = ""; }; + E1EBCB41278BD174009FE6E9 /* TruncatedTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TruncatedTextView.swift; sourceTree = ""; }; + E1EBCB43278BD1CE009FE6E9 /* ItemOverviewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemOverviewCoordinator.swift; sourceTree = ""; }; + E1EBCB45278BD595009FE6E9 /* ItemOverviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemOverviewView.swift; sourceTree = ""; }; E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerJumpLength.swift; sourceTree = ""; }; E1FA2F7327818A8800B4C270 /* SmallMenuOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmallMenuOverlay.swift; sourceTree = ""; }; E1FCD08726C35A0D007C8DCF /* NetworkError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = ""; }; @@ -1202,6 +1208,7 @@ C4BE076D2720FEA8003F4AD1 /* PortraitItemElement.swift */, 53F866432687A45F00DCD1D7 /* PortraitItemView.swift */, E1AA331C2782541500F6439C /* PrimaryButtonView.swift */, + E1EBCB41278BD174009FE6E9 /* TruncatedTextView.swift */, ); path = Components; sourceTree = ""; @@ -1261,11 +1268,12 @@ 6220D0B926D6092100B8E046 /* FilterCoordinator.swift */, 62C29EA526D1036A00C1D2E7 /* HomeCoordinator.swift */, 6220D0BF26D61C5000B8E046 /* ItemCoordinator.swift */, + E1EBCB43278BD1CE009FE6E9 /* ItemOverviewCoordinator.swift */, 6220D0B326D5ED8000B8E046 /* LibraryCoordinator.swift */, + 62C29EA726D103D500C1D2E7 /* LibraryListCoordinator.swift */, + C4BE07872728448B003F4AD1 /* LiveTVChannelsCoordinator.swift */, C4BE07702725EB06003F4AD1 /* LiveTVProgramsCoordinator.swift */, C4BE07782726EE82003F4AD1 /* LiveTVTabCoordinator.swift */, - C4BE07872728448B003F4AD1 /* LiveTVChannelsCoordinator.swift */, - 62C29EA726D103D500C1D2E7 /* LibraryListCoordinator.swift */, E193D5412719404B00900D82 /* MainCoordinator */, C40CD921271F8CD8000FB198 /* MoviesLibrariesCoordinator.swift */, 6220D0B626D5EE1100B8E046 /* SearchCoordinator.swift */, @@ -1424,18 +1432,19 @@ 5338F74D263B61370014BF09 /* ConnectToServerView.swift */, 5389276D263C25100035E14B /* ContinueWatchingView.swift */, 625CB56E2678C23300530A6E /* HomeView.swift */, + E1EBCB45278BD595009FE6E9 /* ItemOverviewView.swift */, E14F7D0A26DB3714007C3AE6 /* ItemView */, 53FF7F29263CF3F500585C35 /* LatestMediaView.swift */, - C4AE2C2F27498D2300AE13CF /* LiveTVHomeView.swift */, - C4AE2C3127498D6A00AE13CF /* LiveTVProgramsView.swift */, 53E4E646263F6CF100F67C6B /* LibraryFilterView.swift */, 6213388F265F83A900A81A2A /* LibraryListView.swift */, 53EE24E5265060780068F029 /* LibrarySearchView.swift */, 53DF641D263D9C0600A7CD1A /* LibraryView.swift */, + C4AE2C2F27498D2300AE13CF /* LiveTVHomeView.swift */, + C4AE2C3127498D6A00AE13CF /* LiveTVProgramsView.swift */, 5389276F263C25230035E14B /* NextUpView.swift */, - E1E5D54A2783E26100692DFE /* SettingsView */, E173DA4F26D048D600CC4EB7 /* ServerDetailView.swift */, E13DD3E427177D15009D4DAF /* ServerListView.swift */, + E1E5D54A2783E26100692DFE /* SettingsView */, E13DD3FB2717EAE8009D4DAF /* UserListView.swift */, E13DD3F4271793BB009D4DAF /* UserSignInView.swift */, E193D5452719418B00900D82 /* VideoPlayer */, @@ -2240,6 +2249,7 @@ 53FF7F2A263CF3F500585C35 /* LatestMediaView.swift in Sources */, E122A9132788EAAD0060FA63 /* MediaStreamExtension.swift in Sources */, E1C812C5277A90B200918266 /* URLComponentsExtensions.swift in Sources */, + E1EBCB44278BD1CE009FE6E9 /* ItemOverviewCoordinator.swift in Sources */, 62E632EC267D410B0063E547 /* SeriesItemViewModel.swift in Sources */, 625CB5732678C32A00530A6E /* HomeViewModel.swift in Sources */, 62C29EA826D103D500C1D2E7 /* LibraryListCoordinator.swift in Sources */, @@ -2270,6 +2280,7 @@ E19169CE272514760085832A /* HTTPScheme.swift in Sources */, 53192D5D265AA78A008A4215 /* DeviceProfileBuilder.swift in Sources */, C4AE2C3027498D2300AE13CF /* LiveTVHomeView.swift in Sources */, + E1EBCB42278BD174009FE6E9 /* TruncatedTextView.swift in Sources */, 62133890265F83A900A81A2A /* LibraryListView.swift in Sources */, 62C29EA326D1030F00C1D2E7 /* ConnectToServerCoodinator.swift in Sources */, 62E632DA267D2BC40063E547 /* LatestMediaViewModel.swift in Sources */, @@ -2342,6 +2353,7 @@ 62EC352F267666A5000E9F2D /* SessionManager.swift in Sources */, 62E632E3267D3BA60063E547 /* MovieItemViewModel.swift in Sources */, C4BE076F2720FEFF003F4AD1 /* PlainNavigationLinkButton.swift in Sources */, + E1EBCB46278BD595009FE6E9 /* ItemOverviewView.swift in Sources */, 091B5A8A2683142E00D78B61 /* ServerDiscovery.swift in Sources */, 62E632EF267D43320063E547 /* LibraryFilterViewModel.swift in Sources */, 5D64683D277B1649009E09AE /* PreferenceUIHostingSwizzling.swift in Sources */, diff --git a/Swiftfin/Components/TruncatedTextView.swift b/Swiftfin/Components/TruncatedTextView.swift new file mode 100644 index 00000000..a61f49d6 --- /dev/null +++ b/Swiftfin/Components/TruncatedTextView.swift @@ -0,0 +1,106 @@ +// + /* + * 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 2021 Aiden Vigue & Jellyfin Contributors + */ + +import SwiftUI + +struct TruncatedTextView: View { + + @State private var truncated: Bool = false + @State private var shrinkText: String + private var text: String + let font: UIFont + let lineLimit: Int + let seeMoreAction: () -> Void + + private var moreLessText: String { + if !truncated { + return "" + } else { + return "See More" + } + } + + init(_ text: String, + lineLimit: Int, + font: UIFont = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body), + seeMoreAction: @escaping () -> Void) { + self.text = text + self.lineLimit = lineLimit + _shrinkText = State(wrappedValue: text) + self.font = font + self.seeMoreAction = seeMoreAction + } + + var body: some View { + VStack(alignment: .center) { + Group { + Text(shrinkText) + .overlay { + if truncated { + LinearGradient(stops: [.init(color: .systemBackground.opacity(0), location: 0.5), + .init(color: .systemBackground.opacity(0.8), location: 0.7), + .init(color: .systemBackground, location: 1)], + startPoint: .top, + endPoint: .bottom) + } + } + } + .lineLimit(lineLimit) + .background { + // Render the limited text and measure its size + Text(text) + .lineLimit(lineLimit + 2) + .background { + GeometryReader { visibleTextGeometry in + Color.clear + .onAppear { + let size = CGSize(width: visibleTextGeometry.size.width, height: .greatestFiniteMagnitude) + let attributes:[NSAttributedString.Key:Any] = [NSAttributedString.Key.font: font] + var low = 0 + var heigh = shrinkText.count + var mid = heigh + while ((heigh - low) > 1) { + let attributedText = NSAttributedString(string: shrinkText, attributes: attributes) + let boundingRect = attributedText.boundingRect(with: size, options: NSStringDrawingOptions.usesLineFragmentOrigin, context: nil) + if boundingRect.size.height > visibleTextGeometry.size.height { + truncated = true + heigh = mid + mid = (heigh + low)/2 + + } else { + if mid == text.count { + break + } else { + low = mid + mid = (low + heigh)/2 + } + } + shrinkText = String(text.prefix(mid)) + } + + if truncated { + shrinkText = String(shrinkText.prefix(shrinkText.count - 2)) + } + } + } + } + .hidden() + } + .font(Font(font)) + + if truncated { + Button { + seeMoreAction() + } label: { + Text(moreLessText) + } + } + } + } +} diff --git a/Swiftfin/Views/ItemOverviewView.swift b/Swiftfin/Views/ItemOverviewView.swift new file mode 100644 index 00000000..f155ae34 --- /dev/null +++ b/Swiftfin/Views/ItemOverviewView.swift @@ -0,0 +1,35 @@ +// + /* + * 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 2021 Aiden Vigue & Jellyfin Contributors + */ + +import JellyfinAPI +import SwiftUI + +struct ItemOverviewView: View { + + @EnvironmentObject var itemOverviewRouter: ItemOverviewCoordinator.Router + let item: BaseItemDto + + var body: some View { + ScrollView(showsIndicators: false) { + Text(item.overview ?? "") + .font(.footnote) + .padding() + } + .navigationBarTitle("Overview", displayMode: .inline) + .toolbar { + ToolbarItemGroup(placement: .navigationBarLeading) { + Button { + itemOverviewRouter.dismissCoordinator() + } label: { + Image(systemName: "xmark.circle.fill") + } + } + } + } +} diff --git a/Swiftfin/Views/ItemView/ItemViewBody.swift b/Swiftfin/Views/ItemView/ItemViewBody.swift index e3860e3e..548a20ac 100644 --- a/Swiftfin/Views/ItemView/ItemViewBody.swift +++ b/Swiftfin/Views/ItemView/ItemViewBody.swift @@ -13,6 +13,8 @@ import SwiftUI struct ItemViewBody: View { + @Environment(\.horizontalSizeClass) private var hSizeClass + @Environment(\.verticalSizeClass) private var vSizeClass @EnvironmentObject var itemRouter: ItemCoordinator.Router @EnvironmentObject private var viewModel: ItemViewModel @Default(.showCastAndCrew) var showCastAndCrew @@ -20,11 +22,25 @@ struct ItemViewBody: View { var body: some View { VStack(alignment: .leading) { // MARK: Overview - - Text(viewModel.item.overview ?? "") - .font(.footnote) - .padding(.horizontal, 16) - .padding(.vertical, 3) + + if let itemOverview = viewModel.item.overview { + if hSizeClass == .compact && vSizeClass == .regular { + TruncatedTextView(itemOverview, + lineLimit: 5, + font: UIFont.preferredFont(forTextStyle: .footnote)) { + itemRouter.route(to: \.itemOverview, viewModel.item) + } + .padding() + } else { + Text(itemOverview) + .font(.footnote) + .padding() + } + } else { + Text("No overview available") + .font(.footnote) + .padding() + } // MARK: Seasons From e3608e9ca153c50ecdead6ea4d8d0ab32c31cfad Mon Sep 17 00:00:00 2001 From: Ethan Pippin Date: Sun, 9 Jan 2022 21:02:02 -0700 Subject: [PATCH 3/4] ios current episode bordered on row view and see more description --- Shared/Coordinators/ItemOverviewCoordinator.swift | 4 ++++ Swiftfin.xcodeproj/project.pbxproj | 4 ++++ Swiftfin/Components/EpisodesRowView.swift | 7 +++++++ Swiftfin/Views/ItemView/ItemViewBody.swift | 3 ++- 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Shared/Coordinators/ItemOverviewCoordinator.swift b/Shared/Coordinators/ItemOverviewCoordinator.swift index cf80b4f9..870109ec 100644 --- a/Shared/Coordinators/ItemOverviewCoordinator.swift +++ b/Shared/Coordinators/ItemOverviewCoordinator.swift @@ -24,6 +24,10 @@ final class ItemOverviewCoordinator: NavigationCoordinatable { } @ViewBuilder func makeStart() -> some View { + #if os(tvOS) + EmptyView() + #else ItemOverviewView(item: item) + #endif } } diff --git a/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index d0ced680..24b84283 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -433,6 +433,8 @@ E1EBCB42278BD174009FE6E9 /* TruncatedTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EBCB41278BD174009FE6E9 /* TruncatedTextView.swift */; }; E1EBCB44278BD1CE009FE6E9 /* ItemOverviewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EBCB43278BD1CE009FE6E9 /* ItemOverviewCoordinator.swift */; }; E1EBCB46278BD595009FE6E9 /* ItemOverviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EBCB45278BD595009FE6E9 /* ItemOverviewView.swift */; }; + E1EBCB4A278BE443009FE6E9 /* ItemOverviewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EBCB43278BD1CE009FE6E9 /* ItemOverviewCoordinator.swift */; }; + E1EBCB4B278BE5BC009FE6E9 /* ItemOverviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EBCB45278BD595009FE6E9 /* ItemOverviewView.swift */; }; E1F0204E26CCCA74001C1C3B /* VideoPlayerJumpLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */; }; E1F0204F26CCCA74001C1C3B /* VideoPlayerJumpLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */; }; E1FA2F7427818A8800B4C270 /* SmallMenuOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FA2F7327818A8800B4C270 /* SmallMenuOverlay.swift */; }; @@ -2093,6 +2095,7 @@ E1E5D5462783C28100692DFE /* CinematicItemAboutView.swift in Sources */, E1FCD09726C47118007C8DCF /* ErrorMessage.swift in Sources */, E193D53527193F8100900D82 /* ItemCoordinator.swift in Sources */, + E1EBCB4A278BE443009FE6E9 /* ItemOverviewCoordinator.swift in Sources */, 53116A19268B947A003024C9 /* PlainLinkButton.swift in Sources */, C4BE07772725EBEA003F4AD1 /* LiveTVProgramsViewModel.swift in Sources */, 536D3D88267C17350004248C /* PublicUserButton.swift in Sources */, @@ -2127,6 +2130,7 @@ 535870A82669D8AE00D05A09 /* StringExtensions.swift in Sources */, 53A83C33268A309300DF3D92 /* LibraryView.swift in Sources */, 62E632ED267D410B0063E547 /* SeriesItemViewModel.swift in Sources */, + E1EBCB4B278BE5BC009FE6E9 /* ItemOverviewView.swift in Sources */, 5398514526B64DA100101B49 /* SettingsView.swift in Sources */, 62E632F0267D43320063E547 /* LibraryFilterViewModel.swift in Sources */, E193D54B271941D300900D82 /* ServerListView.swift in Sources */, diff --git a/Swiftfin/Components/EpisodesRowView.swift b/Swiftfin/Components/EpisodesRowView.swift index 605a9142..aee0e7c7 100644 --- a/Swiftfin/Components/EpisodesRowView.swift +++ b/Swiftfin/Components/EpisodesRowView.swift @@ -106,6 +106,13 @@ struct EpisodesRowView: View { bh: episode.getBackdropImageBlurHash()) .mask(Rectangle().frame(width: 200, height: 112).cornerRadius(10)) .frame(width: 200, height: 112) + .overlay { + if episode.id == viewModel.episodeItemViewModel.item.id { + RoundedRectangle(cornerRadius: 6) + .stroke(Color.jellyfinPurple, lineWidth: 4) + } + } + .padding(.top) VStack(alignment: .leading) { Text(episode.getEpisodeLocator() ?? "") diff --git a/Swiftfin/Views/ItemView/ItemViewBody.swift b/Swiftfin/Views/ItemView/ItemViewBody.swift index 548a20ac..a7ed4e8d 100644 --- a/Swiftfin/Views/ItemView/ItemViewBody.swift +++ b/Swiftfin/Views/ItemView/ItemViewBody.swift @@ -30,7 +30,8 @@ struct ItemViewBody: View { font: UIFont.preferredFont(forTextStyle: .footnote)) { itemRouter.route(to: \.itemOverview, viewModel.item) } - .padding() + .padding(.horizontal) + .padding(.top) } else { Text(itemOverview) .font(.footnote) From 03aeed967ed50f46732bce116e75a93634d82e5d Mon Sep 17 00:00:00 2001 From: Ethan Pippin Date: Sun, 9 Jan 2022 21:13:35 -0700 Subject: [PATCH 4/4] tvos cleanup --- Shared/Views/PortraitItemSize.swift | 2 +- .../CinematicItemView/CinematicItemAboutView.swift | 7 +++---- Swiftfin.xcodeproj/project.pbxproj | 2 -- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Shared/Views/PortraitItemSize.swift b/Shared/Views/PortraitItemSize.swift index 62cf5241..3af53228 100644 --- a/Shared/Views/PortraitItemSize.swift +++ b/Shared/Views/PortraitItemSize.swift @@ -14,6 +14,6 @@ extension View { /// Applies Portrait Poster frame with proper corner radius ratio against the width func portraitPoster(width: CGFloat) -> some View { self.frame(width: width, height: width * 1.5) - .cornerRadius(width / 24) + .cornerRadius((width * 1.5) / 40) } } diff --git a/Swiftfin tvOS/Views/ItemView/CinematicItemView/CinematicItemAboutView.swift b/Swiftfin tvOS/Views/ItemView/CinematicItemView/CinematicItemAboutView.swift index 2b417eab..89977d74 100644 --- a/Swiftfin tvOS/Views/ItemView/CinematicItemView/CinematicItemAboutView.swift +++ b/Swiftfin tvOS/Views/ItemView/CinematicItemView/CinematicItemAboutView.swift @@ -17,13 +17,12 @@ struct CinematicItemAboutView: View { var body: some View { HStack(alignment: .top, spacing: 10) { ImageView(src: viewModel.item.portraitHeaderViewURL(maxWidth: 257)) - .frame(width: 257, height: 380) - .cornerRadius(10) + .portraitPoster(width: 257) ZStack(alignment: .topLeading) { Color(UIColor.darkGray).opacity(focused ? 0.2 : 0) - .cornerRadius(30) - .frame(height: 380) + .cornerRadius(9.5) + .frame(height: 385.5) VStack(alignment: .leading) { Text("About") diff --git a/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index 24b84283..2a64794f 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -434,7 +434,6 @@ E1EBCB44278BD1CE009FE6E9 /* ItemOverviewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EBCB43278BD1CE009FE6E9 /* ItemOverviewCoordinator.swift */; }; E1EBCB46278BD595009FE6E9 /* ItemOverviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EBCB45278BD595009FE6E9 /* ItemOverviewView.swift */; }; E1EBCB4A278BE443009FE6E9 /* ItemOverviewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EBCB43278BD1CE009FE6E9 /* ItemOverviewCoordinator.swift */; }; - E1EBCB4B278BE5BC009FE6E9 /* ItemOverviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1EBCB45278BD595009FE6E9 /* ItemOverviewView.swift */; }; E1F0204E26CCCA74001C1C3B /* VideoPlayerJumpLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */; }; E1F0204F26CCCA74001C1C3B /* VideoPlayerJumpLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */; }; E1FA2F7427818A8800B4C270 /* SmallMenuOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FA2F7327818A8800B4C270 /* SmallMenuOverlay.swift */; }; @@ -2130,7 +2129,6 @@ 535870A82669D8AE00D05A09 /* StringExtensions.swift in Sources */, 53A83C33268A309300DF3D92 /* LibraryView.swift in Sources */, 62E632ED267D410B0063E547 /* SeriesItemViewModel.swift in Sources */, - E1EBCB4B278BE5BC009FE6E9 /* ItemOverviewView.swift in Sources */, 5398514526B64DA100101B49 /* SettingsView.swift in Sources */, 62E632F0267D43320063E547 /* LibraryFilterViewModel.swift in Sources */, E193D54B271941D300900D82 /* ServerListView.swift in Sources */,