Merge remote-tracking branch 'origin/main' into main

This commit is contained in:
Aiden Vigue 2021-06-22 00:15:50 -04:00
commit 1f0cefba0d
No known key found for this signature in database
GPG Key ID: B9A09843AB079D5B
8 changed files with 135 additions and 126 deletions

View File

@ -12,74 +12,72 @@ import JellyfinAPI
struct ProgressBar: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
let tl = CGPoint(x: rect.minX, y: rect.minY)
let tr = CGPoint(x: rect.maxX, y: rect.minY)
let br = CGPoint(x: rect.maxX, y: rect.maxY)
let bls = CGPoint(x: rect.minX + 10, y: rect.maxY)
let blc = CGPoint(x: rect.minX + 10, y: rect.maxY - 10)
path.move(to: tl)
path.addLine(to: tr)
path.addLine(to: br)
path.addLine(to: bls)
path.addRelativeArc(center: blc, radius: 10,
startAngle: Angle.degrees(90), delta: Angle.degrees(90))
startAngle: Angle.degrees(90), delta: Angle.degrees(90))
return path
}
}
struct ContinueWatchingView: View {
var items: [BaseItemDto]
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
if items.count > 0 {
LazyHStack {
Spacer().frame(width: 14)
ForEach(items, id: \.id) { item in
NavigationLink(destination: ItemView(item: item)) {
VStack(alignment: .leading) {
Spacer().frame(height: 10)
ImageView(src: item.getBackdropImage(maxWidth: 320), bh: item.getBackdropImageBlurHash())
.frame(width: 320, height: 180)
.cornerRadius(10)
.overlay(
Group {
if item.type == "Episode" {
Text("\(item.name ?? "")")
.font(.caption)
.padding(6)
.foregroundColor(.white)
}
}.background(Color.black)
LazyHStack {
Spacer().frame(width: 14)
ForEach(items, id: \.id) { item in
NavigationLink(destination: ItemView(item: item)) {
VStack(alignment: .leading) {
Spacer().frame(height: 10)
ImageView(src: item.getBackdropImage(maxWidth: 320), bh: item.getBackdropImageBlurHash())
.frame(width: 320, height: 180)
.cornerRadius(10)
.overlay(
Group {
if item.type == "Episode" {
Text("\(item.name ?? "")")
.font(.caption)
.padding(6)
.foregroundColor(.white)
}
}.background(Color.black)
.opacity(0.8)
.cornerRadius(10.0)
.padding(6), alignment: .topTrailing
)
.overlay(
Rectangle()
.fill(Color(red: 172/255, green: 92/255, blue: 195/255))
.mask(ProgressBar())
.frame(width: CGFloat((item.userData?.playedPercentage ?? 0) * 3.2), height: 7)
.padding(0), alignment: .bottomLeading
)
Text(item.seriesName ?? item.name ?? "")
.font(.callout)
.fontWeight(.semibold)
.foregroundColor(.primary)
.lineLimit(1)
.frame(width: 320, alignment: .leading)
Spacer().frame(height: 5)
}
)
.overlay(
Rectangle()
.fill(Color(red: 172/255, green: 92/255, blue: 195/255))
.mask(ProgressBar())
.frame(width: CGFloat((item.userData?.playedPercentage ?? 0) * 3.2), height: 7)
.padding(0), alignment: .bottomLeading
)
Text(item.seriesName ?? item.name ?? "")
.font(.callout)
.fontWeight(.semibold)
.foregroundColor(.primary)
.lineLimit(1)
.frame(width: 320, alignment: .leading)
Spacer().frame(height: 5)
}
Spacer().frame(width: 16)
}
Spacer().frame(width: 2)
}.frame(height: 215)
Spacer().frame(width: 16)
}
Spacer().frame(width: 2)
}.frame(height: 215)
.padding(.bottom, 10)
}
}
}
}

View File

@ -52,6 +52,7 @@ struct EpisodeItemView: View {
.stroke(Color.secondary, lineWidth: 1))
}
}
.padding(.top, 1)
}
.padding(.bottom, UIDevice.current.userInterfaceIdiom == .pad ? 98 : 30)
}
@ -89,10 +90,10 @@ struct EpisodeItemView: View {
viewModel.updateWatchState()
} label: {
if viewModel.isWatched {
Image(systemName: "checkmark.rectangle.fill").foregroundColor(Color.primary)
Image(systemName: "checkmark.circle.fill").foregroundColor(Color.primary)
.font(.system(size: 20))
} else {
Image(systemName: "xmark.rectangle").foregroundColor(Color.primary)
Image(systemName: "checkmark.circle").foregroundColor(Color.primary)
.font(.system(size: 20))
}
}
@ -254,6 +255,8 @@ struct EpisodeItemView: View {
Spacer()
}.frame(maxWidth: .infinity, alignment: .leading)
.offset(x: 14)
.padding(.top, 1)
}.frame(maxWidth: .infinity, alignment: .leading)
Spacer()
HStack {
@ -273,10 +276,10 @@ struct EpisodeItemView: View {
viewModel.updateWatchState()
} label: {
if viewModel.isWatched {
Image(systemName: "checkmark.rectangle.fill").foregroundColor(Color.primary)
Image(systemName: "checkmark.circle.fill").foregroundColor(Color.primary)
.font(.system(size: 20))
} else {
Image(systemName: "xmark.rectangle").foregroundColor(Color.primary)
Image(systemName: "checkmark.circle").foregroundColor(Color.primary)
.font(.system(size: 20))
}
}

View File

@ -15,16 +15,17 @@ struct HomeView: View {
@Environment(\.horizontalSizeClass) var hSizeClass
@Environment(\.verticalSizeClass) var vSizeClass
@State var showingSettings = false
var body: some View {
@ViewBuilder
var innerBody: some View {
if(viewModel.isLoading) {
ProgressView()
} else {
ScrollView {
LazyVStack(alignment: .leading) {
Spacer().frame(height: hSizeClass == .compact && vSizeClass == .regular ? 0 : 16)
if !viewModel.resumeItems.isEmpty {
ContinueWatchingView(items: viewModel.resumeItems)
.padding(.top, hSizeClass == .compact && vSizeClass == .regular ? 0 : 16)
}
if !viewModel.nextUpItems.isEmpty {
NextUpView(items: viewModel.nextUpItems)
@ -52,10 +53,15 @@ struct HomeView: View {
}.padding(EdgeInsets(top: 4, leading: 0, bottom: 0, trailing: 0))
}
}
Spacer().frame(height: UIDevice.current.userInterfaceIdiom == .phone ? 20 : 30)
}
.padding(.top, hSizeClass == .compact && vSizeClass == .regular ? 0 : 16)
.padding(.bottom, UIDevice.current.userInterfaceIdiom == .phone ? 20 : 30)
}
}
}
var body: some View {
innerBody
.navigationTitle(MainTabView.Tab.home.localized)
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
@ -69,6 +75,5 @@ struct HomeView: View {
.fullScreenCover(isPresented: $showingSettings) {
SettingsView(viewModel: SettingsViewModel(), close: $showingSettings)
}
}
}
}

View File

@ -8,38 +8,36 @@
import SwiftUI
struct LatestMediaView: View {
@StateObject var viewModel: LatestMediaViewModel
@ObservedObject var viewModel: LatestMediaViewModel
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
ZStack {
LazyHStack {
Spacer().frame(width: 16)
ForEach(viewModel.items, id: \.id) { item in
if item.type == "Series" || item.type == "Movie" {
NavigationLink(destination: ItemView(item: item)) {
VStack(alignment: .leading) {
Spacer().frame(height: 10)
ImageView(src: item.getPrimaryImage(maxWidth: 100), bh: item.getPrimaryImageBlurHash())
.frame(width: 100, height: 150)
.cornerRadius(10)
Spacer().frame(height: 5)
Text(item.seriesName ?? item.name ?? "")
LazyHStack {
Spacer().frame(width: 16)
ForEach(viewModel.items, id: \.id) { item in
if item.type == "Series" || item.type == "Movie" {
NavigationLink(destination: ItemView(item: item)) {
VStack(alignment: .leading) {
Spacer().frame(height: 10)
ImageView(src: item.getPrimaryImage(maxWidth: 100), bh: item.getPrimaryImageBlurHash())
.frame(width: 100, height: 150)
.cornerRadius(10)
Spacer().frame(height: 5)
Text(item.seriesName ?? item.name ?? "")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.primary)
.lineLimit(1)
if item.productionYear != nil {
Text(String(item.productionYear ?? 0))
.foregroundColor(.secondary)
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.primary)
.lineLimit(1)
if item.productionYear != nil {
Text(String(item.productionYear ?? 0))
.foregroundColor(.secondary)
.font(.caption)
.fontWeight(.medium)
} else {
Text(item.type!)
}
}.frame(width: 100)
Spacer().frame(width: 15)
}
.fontWeight(.medium)
} else {
Text(item.type!)
}
}.frame(width: 100)
Spacer().frame(width: 15)
}
}
}

View File

@ -58,6 +58,7 @@ struct MovieItemView: View {
.stroke(Color.secondary, lineWidth: 1))
}
}
.padding(.top, 1)
}
.padding(.bottom, UIDevice.current.userInterfaceIdiom == .pad ? 98 : 30)
}
@ -95,10 +96,10 @@ struct MovieItemView: View {
viewModel.updateWatchState()
} label: {
if viewModel.isWatched {
Image(systemName: "checkmark.rectangle.fill").foregroundColor(Color.primary)
Image(systemName: "checkmark.circle.fill").foregroundColor(Color.primary)
.font(.system(size: 20))
} else {
Image(systemName: "xmark.rectangle").foregroundColor(Color.primary)
Image(systemName: "checkmark.circle").foregroundColor(Color.primary)
.font(.system(size: 20))
}
}
@ -270,6 +271,7 @@ struct MovieItemView: View {
Spacer()
}.frame(maxWidth: .infinity, alignment: .leading)
.offset(x: 14)
.padding(.top, 1)
}.frame(maxWidth: .infinity, alignment: .leading)
Spacer()
HStack {
@ -289,10 +291,10 @@ struct MovieItemView: View {
viewModel.updateWatchState()
} label: {
if viewModel.isWatched {
Image(systemName: "checkmark.rectangle.fill").foregroundColor(Color.primary)
Image(systemName: "checkmark.circle.fill").foregroundColor(Color.primary)
.font(.system(size: 20))
} else {
Image(systemName: "xmark.rectangle").foregroundColor(Color.primary)
Image(systemName: "checkmark.circle").foregroundColor(Color.primary)
.font(.system(size: 20))
}
}

View File

@ -10,44 +10,42 @@ import Combine
import JellyfinAPI
struct NextUpView: View {
var items: [BaseItemDto]
var body: some View {
VStack(alignment: .leading) {
if items.count != 0 {
Text("Next Up")
.font(.title2)
.fontWeight(.bold)
.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack {
Spacer().frame(width: 16)
ForEach(items, id: \.id) { item in
NavigationLink(destination: ItemView(item: item)) {
VStack(alignment: .leading) {
ImageView(src: item.getSeriesPrimaryImage(maxWidth: 100), bh: item.getSeriesPrimaryImageBlurHash())
.frame(width: 100, height: 150)
.cornerRadius(10)
Spacer().frame(height: 5)
Text(item.seriesName!)
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.primary)
.lineLimit(1)
Text("S\(item.parentIndexNumber ?? 0):E\(item.indexNumber ?? 0)")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.secondary)
.lineLimit(1)
}.frame(width: 100)
Spacer().frame(width: 16)
}
Text("Next Up")
.font(.title2)
.fontWeight(.bold)
.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack {
Spacer().frame(width: 16)
ForEach(items, id: \.id) { item in
NavigationLink(destination: ItemView(item: item)) {
VStack(alignment: .leading) {
ImageView(src: item.getSeriesPrimaryImage(maxWidth: 100), bh: item.getSeriesPrimaryImageBlurHash())
.frame(width: 100, height: 150)
.cornerRadius(10)
Spacer().frame(height: 5)
Text(item.seriesName!)
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.primary)
.lineLimit(1)
Text("S\(item.parentIndexNumber ?? 0):E\(item.indexNumber ?? 0)")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.secondary)
.lineLimit(1)
}.frame(width: 100)
Spacer().frame(width: 16)
}
}
}
.frame(height: 200)
}
.frame(height: 200)
}
.padding(EdgeInsets(top: 4, leading: 0, bottom: 0, trailing: 0))
}

View File

@ -104,14 +104,16 @@ extension BaseItemDto {
// MARK: Calculations
func getItemRuntime() -> String {
let seconds = (self.runTimeTicks ?? 0) / 10_000_000
let hours = (seconds / 3600)
let minutes = ((seconds - (hours * 3600)) / 60)
if hours != 0 {
return "\(hours):\(String(minutes).leftPad(toWidth: 2, withString: "0"))"
} else {
return "\(String(minutes))m"
}
let timeHMSFormatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .brief
formatter.allowedUnits = [.hour, .minute]
return formatter
}()
let text = timeHMSFormatter.string(from: Double(self.runTimeTicks! / 10_000_000)) ?? ""
return text
}
func getItemProgressString() -> String {

View File

@ -34,6 +34,7 @@ final class ConnectToServerViewModel: ViewModel {
func getPublicUsers() {
if ServerEnvironment.current.server != nil {
UserAPI.getPublicUsers()
.trackActivity(loading)
.sink(receiveCompletion: { completion in
self.handleAPIRequestCompletion(completion: completion)
}, receiveValue: { response in
@ -56,6 +57,7 @@ final class ConnectToServerViewModel: ViewModel {
func connectToServer() {
ServerEnvironment.current.create(with: uriSubject.value)
.trackActivity(loading)
.sink(receiveCompletion: { result in
switch result {
case let .failure(error):
@ -71,6 +73,7 @@ final class ConnectToServerViewModel: ViewModel {
func login() {
SessionManager.current.login(username: usernameSubject.value, password: passwordSubject.value)
.trackActivity(loading)
.sink(receiveCompletion: { completion in
switch completion {
case .finished: