Skip to content

Apply Coordinator Pattern and Add Deep-Links #155

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Oct 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3a090aa
Add Stinsen
PangMo5 Aug 21, 2021
6c5867c
Merge branch 'main' into PangMo5/coordinator-and-deep-link
PangMo5 Aug 21, 2021
4d6ca79
Merge branch 'main' into PangMo5/coordinator-and-deep-link
PangMo5 Aug 21, 2021
0640e70
Add FilterCoordinator
PangMo5 Aug 25, 2021
74a9302
Add ItemCoordinator
PangMo5 Aug 25, 2021
2aab9df
Add VideoPlayerCoordinator
PangMo5 Aug 25, 2021
3ad789f
Merge branch 'main' into PangMo5/coordinator-and-deep-link
PangMo5 Aug 25, 2021
a16d70a
Respond to main branch
PangMo5 Aug 25, 2021
252c7a6
fix tvOS build error
PangMo5 Aug 25, 2021
b544bd6
fix build error
PangMo5 Aug 25, 2021
5d96de3
Temporarily process deep link from HomeView
PangMo5 Aug 25, 2021
3235f80
fix suggested changes
PangMo5 Aug 25, 2021
c757c93
Merge branch 'main' into PangMo5/coordinator-and-deep-link
PangMo5 Sep 2, 2021
1863d97
Merge branch 'main' into PangMo5/coordinator-and-deep-link
PangMo5 Sep 20, 2021
16e3cd6
migrate stinsen v1 to v2
PangMo5 Sep 20, 2021
d10cb35
Refactor
LePips Sep 21, 2021
b07e345
Apply Coordinator pattern to shared view
PangMo5 Sep 21, 2021
b92d66e
Support DeepLink jellyfin://Users/{UserID}/Items/{ItemID}
PangMo5 Sep 21, 2021
c88fabd
Merge branch 'main' into PangMo5/coordinator-and-deep-link
PangMo5 Sep 21, 2021
8175395
remove DeepLinkError
PangMo5 Sep 21, 2021
cd7a58b
fix crash when sign out
PangMo5 Sep 21, 2021
1b7e801
Merge branch 'main' into PangMo5/coordinator-and-deep-link
PangMo5 Sep 23, 2021
fad8f14
fix tvOS build error
PangMo5 Sep 23, 2021
a46ed45
modify LatestMediaViewModel.requestLatestMedia params
PangMo5 Sep 23, 2021
34dbc2a
fix LibraryFilterView's layout
PangMo5 Sep 23, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 118 additions & 8 deletions JellyfinPlayer.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,15 @@
"version": "0.3.1"
}
},
{
"package": "Stinsen",
"repositoryURL": "https://github.com/rundfunk47/stinsen",
"state": {
"branch": null,
"revision": "3d06c7603c70f8af1bd49f8d49f17e98f25b2d6a",
"version": "2.0.2"
}
},
{
"package": "swift-log",
"repositoryURL": "https://github.com/apple/swift-log.git",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
import SwiftUI
import JellyfinAPI

struct CardVStackView: View {
struct EpisodeCardVStackView: View {

let items: [BaseItemDto]
let selectedAction: (BaseItemDto) -> Void

private func buildCardOverlayView(item: BaseItemDto) -> some View {
HStack {
Expand Down Expand Up @@ -45,8 +46,9 @@ struct CardVStackView: View {
var body: some View {
VStack {
ForEach(items, id: \.id) { item in
NavigationLink(destination: ItemNavigationView(item: item)) {

Button {
selectedAction(item)
} label: {
HStack {

// MARK: Image
Expand Down
13 changes: 7 additions & 6 deletions JellyfinPlayer/Components/PillHStackView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ protocol PillStackable {
var title: String { get }
}

struct PillHStackView<NavigationView: View, ItemType: PillStackable>: View {
struct PillHStackView<ItemType: PillStackable>: View {

let title: String
let items: [ItemType]
let navigationView: (ItemType) -> NavigationView
// let navigationView: (ItemType) -> NavigationView
let selectedAction: (ItemType) -> Void

var body: some View {
VStack(alignment: .leading) {
Expand All @@ -30,14 +31,14 @@ struct PillHStackView<NavigationView: View, ItemType: PillStackable>: View {
ScrollView(.horizontal, showsIndicators: false) {
HStack {
ForEach(items, id: \.title) { item in
NavigationLink(destination: LazyView {
navigationView(item)
}) {
Button {
selectedAction(item)
} label: {
ZStack {
Color(UIColor.systemFill)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.cornerRadius(10)

Text(item.title)
.font(.caption)
.fontWeight(.semibold)
Expand Down
62 changes: 30 additions & 32 deletions JellyfinPlayer/Components/PortraitHStackView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@ public protocol PortraitImageStackable {
var failureInitials: String { get }
}

struct PortraitImageHStackView<TopBarView: View, NavigationView: View, ItemType: PortraitImageStackable>: View {
struct PortraitImageHStackView<TopBarView: View, ItemType: PortraitImageStackable>: View {

let items: [ItemType]
let maxWidth: Int
let horizontalAlignment: HorizontalAlignment
let topBarView: () -> TopBarView
let navigationView: (ItemType) -> NavigationView
let selectedAction: (ItemType) -> Void

init(items: [ItemType], maxWidth: Int, horizontalAlignment: HorizontalAlignment = .leading, topBarView: @escaping () -> TopBarView, navigationView: @escaping (ItemType) -> NavigationView) {
init(items: [ItemType], maxWidth: Int, horizontalAlignment: HorizontalAlignment = .leading, topBarView: @escaping () -> TopBarView, selectedAction: @escaping (ItemType) -> Void) {
self.items = items
self.maxWidth = maxWidth
self.horizontalAlignment = horizontalAlignment
self.topBarView = topBarView
self.navigationView = navigationView
self.selectedAction = selectedAction
}

var body: some View {
Expand All @@ -45,38 +45,36 @@ struct PortraitImageHStackView<TopBarView: View, NavigationView: View, ItemType:
Spacer().frame(width: 16)

ForEach(items, id: \.title) { item in
NavigationLink(
destination: LazyView {
navigationView(item)
},
label: {
VStack {
ImageView(src: item.imageURLContsructor(maxWidth: maxWidth),
bh: item.blurHash,
failureInitials: item.failureInitials)
.frame(width: 100, height: CGFloat(maxWidth))
.cornerRadius(10)
.shadow(radius: 4, y: 2)

Text(item.title)
.font(.footnote)
.fontWeight(.regular)
Button {
selectedAction(item)
} label: {
VStack {
ImageView(src: item.imageURLContsructor(maxWidth: maxWidth),
bh: item.blurHash,
failureInitials: item.failureInitials)
.frame(width: 100, height: CGFloat(maxWidth))
.cornerRadius(10)
.shadow(radius: 4, y: 2)

Text(item.title)
.font(.footnote)
.fontWeight(.regular)
.frame(width: 100)
.foregroundColor(.primary)
.multilineTextAlignment(.center)
.lineLimit(2)

if let description = item.description {
Text(description)
.font(.caption)
.fontWeight(.medium)
.frame(width: 100)
.foregroundColor(.primary)
.foregroundColor(.secondary)
.multilineTextAlignment(.center)
.lineLimit(2)

if let description = item.description {
Text(description)
.font(.caption)
.fontWeight(.medium)
.frame(width: 100)
.foregroundColor(.secondary)
.multilineTextAlignment(.center)
.lineLimit(2)
}
}
})
}
}
}
Spacer().frame(width: UIDevice.current.userInterfaceIdiom == .pad ? 16 : 55)
}
Expand Down
152 changes: 73 additions & 79 deletions JellyfinPlayer/Components/PortraitItemView.swift
Original file line number Diff line number Diff line change
@@ -1,91 +1,85 @@
//
/*
* 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
*/
/*
* 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
import JellyfinAPI
import SwiftUI

struct PortraitItemView: View {

var item: BaseItemDto

var body: some View {
NavigationLink(destination: LazyView { ItemNavigationView(item: item) }) {
VStack(alignment: .leading) {
ImageView(src: item.type != "Episode" ? item.getPrimaryImage(maxWidth: 100) : item.getSeriesPrimaryImage(maxWidth: 100), bh: item.type != "Episode" ? item.getPrimaryImageBlurHash() : item.getSeriesPrimaryImageBlurHash())
.frame(width: 100, height: 150)
.cornerRadius(10)
.shadow(radius: 4, y: 2)
.shadow(radius: 4, y: 2)
.overlay(
Rectangle()
.fill(Color(red: 172/255, green: 92/255, blue: 195/255))
.mask(ProgressBar())
.frame(width: CGFloat(item.userData?.playedPercentage ?? 0), height: 7)
.padding(0), alignment: .bottomLeading
)
.overlay(
ZStack {
if item.userData?.isFavorite ?? false {
Image(systemName: "circle.fill")
.foregroundColor(.white)
.opacity(0.6)
Image(systemName: "heart.fill")
.foregroundColor(Color(.systemRed))
.font(.system(size: 10))
}
VStack(alignment: .leading) {
ImageView(src: item.type != "Episode" ? item.getPrimaryImage(maxWidth: 100) : item.getSeriesPrimaryImage(maxWidth: 100),
bh: item.type != "Episode" ? item.getPrimaryImageBlurHash() : item.getSeriesPrimaryImageBlurHash())
.frame(width: 100, height: 150)
.cornerRadius(10)
.shadow(radius: 4, y: 2)
.shadow(radius: 4, y: 2)
.overlay(Rectangle()
.fill(Color(red: 172 / 255, green: 92 / 255, blue: 195 / 255))
.mask(ProgressBar())
.frame(width: CGFloat(item.userData?.playedPercentage ?? 0), height: 7)
.padding(0), alignment: .bottomLeading)
.overlay(ZStack {
if item.userData?.isFavorite ?? false {
Image(systemName: "circle.fill")
.foregroundColor(.white)
.opacity(0.6)
Image(systemName: "heart.fill")
.foregroundColor(Color(.systemRed))
.font(.system(size: 10))
}
}
.padding(.leading, 2)
.padding(.bottom, item.userData?.playedPercentage == nil ? 2 : 9)
.opacity(1), alignment: .bottomLeading)
.overlay(ZStack {
if item.userData?.played ?? false {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.accentColor)
.background(Color(.white))
.clipShape(Circle().scale(0.8))
} else {
if item.userData?.unplayedItemCount != nil {
Capsule()
.fill(Color.accentColor)
.frame(minWidth: 20, minHeight: 20, maxHeight: 20)
Text(String(item.userData!.unplayedItemCount ?? 0))
.foregroundColor(.white)
.font(.caption2)
.padding(2)
}
.padding(.leading, 2)
.padding(.bottom, item.userData?.playedPercentage == nil ? 2 : 9)
.opacity(1), alignment: .bottomLeading)
.overlay(
ZStack {
if item.userData?.played ?? false {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.accentColor)
.background(Color(.white))
.clipShape(Circle().scale(0.8))
} else {
if item.userData?.unplayedItemCount != nil {
Capsule()
.fill(Color.accentColor)
.frame(minWidth: 20, minHeight: 20, maxHeight: 20)
Text(String(item.userData!.unplayedItemCount ?? 0))
.foregroundColor(.white)
.font(.caption2)
.padding(2)
}
}
}.padding(2)
.fixedSize()
.opacity(1), alignment: .topTrailing).opacity(1)
Text(item.seriesName ?? item.name ?? "")
}
}.padding(2)
.fixedSize()
.opacity(1), alignment: .topTrailing).opacity(1)
Text(item.seriesName ?? item.name ?? "")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.primary)
.lineLimit(1)
if item.type == "Movie" || item.type == "Series" {
Text("\(String(item.productionYear ?? 0)) • \(item.officialRating ?? "N/A")")
.foregroundColor(.secondary)
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.primary)
.lineLimit(1)
if item.type == "Movie" || item.type == "Series" {
Text("\(String(item.productionYear ?? 0)) • \(item.officialRating ?? "N/A")")
.foregroundColor(.secondary)
.font(.caption)
.fontWeight(.medium)
} else if item.type == "Season" {
Text("\(item.name ?? "") • \(String(item.productionYear ?? 0))")
.foregroundColor(.secondary)
.font(.caption)
.fontWeight(.medium)
} else {
Text("S\(String(item.parentIndexNumber ?? 0)):E\(String(item.indexNumber ?? 0))")
.foregroundColor(.secondary)
.font(.caption)
.fontWeight(.medium)
}
}.frame(width: 100)
}
.fontWeight(.medium)
} else if item.type == "Season" {
Text("\(item.name ?? "") • \(String(item.productionYear ?? 0))")
.foregroundColor(.secondary)
.font(.caption)
.fontWeight(.medium)
} else {
Text("S\(String(item.parentIndexNumber ?? 0)):E\(String(item.indexNumber ?? 0))")
.foregroundColor(.secondary)
.font(.caption)
.fontWeight(.medium)
}
}.frame(width: 100)
}
}
6 changes: 6 additions & 0 deletions JellyfinPlayer/ConnectToServerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
*/

import SwiftUI
import Stinsen

struct ConnectToServerView: View {
@EnvironmentObject var mainRouter: MainCoordinator.Router
@StateObject var viewModel = ConnectToServerViewModel()
@State var username = ""
@State var password = ""
Expand Down Expand Up @@ -59,6 +61,7 @@ struct ConnectToServerView: View {
if SessionManager.current.doesUserHaveSavedSession(userID: publicUser.id!) {
let user = SessionManager.current.getSavedSession(userID: publicUser.id!)
SessionManager.current.loginWithSavedSession(user: user)
mainRouter.root(\.mainTab)
} else {
username = publicUser.name ?? ""
viewModel.selectedPublicUser = publicUser
Expand Down Expand Up @@ -174,5 +177,8 @@ struct ConnectToServerView: View {
dismissButton: .cancel())
}
.navigationTitle(NSLocalizedString("Connect to Server", comment: ""))
.onAppear {
AppURLHandler.shared.appURLState = .allowedInLogin
}
}
}
Loading