add watched state to item image views.

This commit is contained in:
Aiden Vigue 2021-06-25 00:35:21 -04:00
parent c203778d89
commit 28d2136a25
No known key found for this signature in database
GPG Key ID: B9A09843AB079D5B
17 changed files with 161 additions and 97 deletions

View File

@ -1066,7 +1066,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "JellyfinPlayer tvOS/JellyfinPlayer tvOS.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 53;
CURRENT_PROJECT_VERSION = 54;
DEVELOPMENT_ASSET_PATHS = "\"JellyfinPlayer tvOS/Preview Content\"";
DEVELOPMENT_TEAM = 9R8RREG67J;
ENABLE_PREVIEWS = YES;
@ -1094,7 +1094,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "JellyfinPlayer tvOS/JellyfinPlayer tvOS.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 53;
CURRENT_PROJECT_VERSION = 54;
DEVELOPMENT_ASSET_PATHS = "\"JellyfinPlayer tvOS/Preview Content\"";
DEVELOPMENT_TEAM = 9R8RREG67J;
ENABLE_PREVIEWS = YES;
@ -1243,7 +1243,7 @@
CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 53;
CURRENT_PROJECT_VERSION = 54;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = 9R8RREG67J;
ENABLE_BITCODE = NO;
@ -1277,7 +1277,7 @@
CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 53;
CURRENT_PROJECT_VERSION = 54;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = 9R8RREG67J;
@ -1309,7 +1309,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 53;
CURRENT_PROJECT_VERSION = 54;
DEVELOPMENT_TEAM = 9R8RREG67J;
INFOPLIST_FILE = WidgetExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
@ -1334,7 +1334,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 53;
CURRENT_PROJECT_VERSION = 54;
DEVELOPMENT_TEAM = 9R8RREG67J;
INFOPLIST_FILE = WidgetExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
@ -1438,8 +1438,8 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/kean/NukeUI";
requirement = {
kind = exactVersion;
version = 0.3.0;
kind = upToNextMajorVersion;
minimumVersion = 0.3.0;
};
};
625CB5782678C4A400530A6E /* XCRemoteSwiftPackageReference "ActivityIndicator" */ = {

View File

@ -60,8 +60,8 @@
"repositoryURL": "https://github.com/kean/NukeUI",
"state": {
"branch": null,
"revision": "d2580b8d22b29c6244418d8e4b568f3162191460",
"version": "0.3.0"
"revision": "4516371912149ac024dec361827931b46a69c217",
"version": "0.6.2"
}
},
{

View File

@ -105,7 +105,7 @@ struct ConnectToServerView: View {
}
}
} else {
Section(header: Text("Server Information")) {
Section(header: Text("Manual Connection")) {
TextField("Jellyfin Server URL", text: $uri)
.disableAutocorrection(true)
.autocapitalization(.none)
@ -123,23 +123,20 @@ struct ConnectToServerView: View {
.disabled(viewModel.isLoading || uri.isEmpty)
}
Section(header: Text("Local Servers")) {
Section(header: Text("Discovered Servers")) {
if self.viewModel.searching {
ProgressView()
}
ForEach(self.viewModel.servers, id: \.id) { server in
Button(action: {
print(server.url)
viewModel.connectToServer(at: server.url)
}, label: {
HStack {
VStack {
Text(server.name)
.font(.headline)
Text(server.host)
Text("\(server.host)")
.font(.subheadline)
}
.foregroundColor(.secondary)
Spacer()
if viewModel.isLoading {
ProgressView()

View File

@ -43,19 +43,6 @@ struct ContinueWatchingView: View {
.frame(width: 320, height: 180)
.cornerRadius(10)
.shadow(radius: 4)
.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))
@ -63,12 +50,22 @@ struct ContinueWatchingView: View {
.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)
HStack {
Text("\(item.seriesName ?? item.name ?? "")")
.font(.callout)
.fontWeight(.semibold)
.foregroundColor(.primary)
.lineLimit(1)
if(item.type == "Episode") {
Text("• S\(String(item.parentIndexNumber ?? 0)):E\(String(item.indexNumber ?? 0)) - \(item.name ?? "")")
.font(.callout)
.fontWeight(.semibold)
.foregroundColor(.secondary)
.lineLimit(1)
.offset(x: -1.4)
}
Spacer()
}.frame(width: 320, alignment: .leading)
}.padding(.top, 10)
.padding(.bottom, 5)
}

View File

@ -20,7 +20,7 @@ struct HomeView: View {
ProgressView()
} else {
ScrollView {
LazyVStack(alignment: .leading) {
VStack(alignment: .leading) {
if !viewModel.resumeItems.isEmpty {
ContinueWatchingView(items: viewModel.resumeItems)
}
@ -57,7 +57,6 @@ struct HomeView: View {
var body: some View {
innerBody
.navigationTitle(MainTabView.Tab.home.localized)
/*
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button {
@ -70,6 +69,5 @@ struct HomeView: View {
.fullScreenCover(isPresented: $showingSettings) {
SettingsView(viewModel: SettingsViewModel(), close: $showingSettings)
}
*/
}
}

View File

@ -21,6 +21,17 @@ struct LatestMediaView: View {
.frame(width: 100, height: 150)
.cornerRadius(10)
.shadow(radius: 4)
.overlay(
ZStack {
if(item.userData!.played ?? false) {
Image(systemName: "circle.fill")
.foregroundColor(.white)
Image(systemName: "checkmark.circle.fill")
.foregroundColor(Color(.systemBlue))
}
}.padding(2)
.opacity(1)
, alignment: .topTrailing).opacity(1)
Text(item.seriesName ?? item.name ?? "")
.font(.caption)
.fontWeight(.semibold)

View File

@ -21,45 +21,54 @@ struct LibraryFilterView: View {
var body: some View {
NavigationView {
ZStack {
Form {
if viewModel.enabledFilterType.contains(.genre) {
MultiSelector(label: "Genres",
options: viewModel.possibleGenres,
optionToString: { $0.name ?? "" },
selected: $viewModel.modifiedFilters.withGenres)
}
if viewModel.enabledFilterType.contains(.filter) {
MultiSelector(label: "Filters",
options: viewModel.possibleItemFilters,
optionToString: { $0.localized },
selected: $viewModel.modifiedFilters.filters)
}
if viewModel.enabledFilterType.contains(.tag) {
MultiSelector(label: "Tags",
options: viewModel.possibleTags,
optionToString: { $0 },
selected: $viewModel.modifiedFilters.tags)
}
if viewModel.enabledFilterType.contains(.sortBy) {
MultiSelector(label: "Sort by",
options: viewModel.possibleSortBys,
optionToString: { $0.localized },
selected: $viewModel.modifiedFilters.sortBy)
}
if viewModel.enabledFilterType.contains(.sortOrder) {
Picker(selection: $viewModel.modifiedFilters.sortOrder, label: Text("Order")) {
ForEach(viewModel.possibleSortOrders, id: \.self) { so in
Text("\(so.rawValue)").tag(so.rawValue)
VStack {
if viewModel.isLoading {
ProgressView()
} else {
Form {
if viewModel.enabledFilterType.contains(.genre) {
MultiSelector(label: "Genres",
options: viewModel.possibleGenres,
optionToString: { $0.name ?? "" },
selected: $viewModel.modifiedFilters.withGenres)
}
if viewModel.enabledFilterType.contains(.filter) {
MultiSelector(label: "Filters",
options: viewModel.possibleItemFilters,
optionToString: { $0.localized },
selected: $viewModel.modifiedFilters.filters)
}
if viewModel.enabledFilterType.contains(.tag) {
MultiSelector(label: "Tags",
options: viewModel.possibleTags,
optionToString: { $0 },
selected: $viewModel.modifiedFilters.tags)
}
if viewModel.enabledFilterType.contains(.sortBy) {
Picker(selection: $viewModel.selectedSortBy, label: Text("Sort by")) {
ForEach(viewModel.possibleSortBys, id: \.self) { so in
Text(so.localized).tag(so)
}
}
}
if viewModel.enabledFilterType.contains(.sortOrder) {
Picker(selection: $viewModel.selectedSortOrder, label: Text("Display order")) {
ForEach(viewModel.possibleSortOrders, id: \.self) { so in
Text(so.rawValue).tag(so)
}
}
}
}
}
if viewModel.isLoading {
ProgressView()
Button {
viewModel.resetFilters()
self.filters = viewModel.modifiedFilters
presentationMode.wrappedValue.dismiss()
} label: {
Text("Reset")
}
}
}
.navigationBarTitle("Filters", displayMode: .inline)
.navigationBarTitle("Filter Results", displayMode: .inline)
.toolbar {
ToolbarItemGroup(placement: .navigationBarLeading) {
Button {
@ -70,6 +79,7 @@ struct LibraryFilterView: View {
}
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button {
viewModel.updateModifiedFilter()
self.filters = viewModel.modifiedFilters
presentationMode.wrappedValue.dismiss()
} label: {

View File

@ -74,6 +74,7 @@ struct LibraryListView: View {
}.padding(32)
}.background(Color.black)
.frame(minWidth: 100, maxWidth: .infinity)
.frame(height: 72)
}
.cornerRadius(10)
.shadow(radius: 5)

View File

@ -35,6 +35,17 @@ struct LibrarySearchView: View {
ImageView(src: item.getPrimaryImage(maxWidth: 100), bh: item.getPrimaryImageBlurHash())
.frame(width: 100, height: 150)
.cornerRadius(10)
.overlay(
ZStack {
if(item.userData!.played ?? false) {
Image(systemName: "circle.fill")
.foregroundColor(.white)
Image(systemName: "checkmark.circle.fill")
.foregroundColor(Color(.systemBlue))
}
}.padding(2)
.opacity(1)
, alignment: .topTrailing).opacity(1)
Text(item.name ?? "")
.font(.caption)
.fontWeight(.semibold)

View File

@ -13,6 +13,7 @@ struct LibraryView: View {
var title: String
// MARK: tracks for grid
var defaultFilters = LibraryFilters(filters: [], sortOrder: [.ascending], withGenres: [], tags: [], sortBy: [.name])
@State var isShowingSearchView = false
@State var isShowingFilterView = false
@ -38,6 +39,17 @@ struct LibraryView: View {
ImageView(src: item.getPrimaryImage(maxWidth: 100), bh: item.getPrimaryImageBlurHash())
.frame(width: 100, height: 150)
.cornerRadius(10)
.overlay(
ZStack {
if(item.userData!.played ?? false) {
Image(systemName: "circle.fill")
.foregroundColor(.white)
Image(systemName: "checkmark.circle.fill")
.foregroundColor(Color(.systemBlue))
}
}.padding(2)
.opacity(1)
, alignment: .topTrailing).opacity(1)
Text(item.name ?? "")
.font(.caption)
.fontWeight(.semibold)
@ -107,14 +119,14 @@ struct LibraryView: View {
Image(systemName: "chevron.right")
}
}
Button(action: {
Label("Icon One", systemImage: "line.horizontal.3.decrease.circle")
.foregroundColor(viewModel.filters == defaultFilters ? .accentColor : Color(UIColor.systemOrange))
.onTapGesture {
isShowingFilterView = true
}) {
Image(systemName: "line.horizontal.3.decrease.circle")
}
Button(action: {
Button {
isShowingSearchView = true
}) {
} label: {
Image(systemName: "magnifyingglass")
}
}

View File

@ -76,6 +76,17 @@ struct SeasonItemView: View {
.frame(width: CGFloat(episode.userData!.playedPercentage ?? 0 * 1.5), height: 7)
.padding(0), alignment: .bottomLeading
)
.overlay(
ZStack {
if(episode.userData!.played ?? false) {
Image(systemName: "circle.fill")
.foregroundColor(.white)
Image(systemName: "checkmark.circle.fill")
.foregroundColor(Color(.systemBlue))
}
}.padding(2)
.opacity(1)
, alignment: .topTrailing).opacity(1)
VStack(alignment: .leading) {
HStack {
Text("S\(String(episode.parentIndexNumber ?? 0)):E\(String(episode.indexNumber ?? 0))").font(.subheadline)

View File

@ -65,6 +65,10 @@ struct SettingsView: View {
Text("Signed in as \(username)").foregroundColor(.primary)
Spacer()
Button {
let nc = NotificationCenter.default
nc.post(name: Notification.Name("didSignOut"), object: nil)
SessionManager.current.logout()
} label: {
Text("Log out").font(.callout)

View File

@ -24,14 +24,16 @@ struct ImageView: View {
}
var body: some View {
LazyImage(source: source)
.placeholder {
LazyImage(source: source) { state in
if let image = state.image {
image
} else if state.error != nil {
Rectangle()
.fill(Color.gray)
} else {
Image(uiImage: UIImage(blurHash: blurhash, size: CGSize(width: 16, height: 16))!)
.resizable()
}
.failure {
Rectangle()
.fill(Color.gray)
}
}
}
}

View File

@ -66,11 +66,9 @@ public class ServerDiscovery {
public init() {
func receiveHandler(_ ipAddress: String, _ port: Int, _ response: Data) {
print("RECIEVED \(ipAddress):\(String(port)) \(response)")
}
func errorHandler(error: UDPBroadcastConnection.ConnectionError) {
print(error)
}
self.broadcastConn = try! UDPBroadcastConnection(port: 7359, handler: receiveHandler, errorHandler: errorHandler)
}
@ -81,7 +79,6 @@ public class ServerDiscovery {
let response = try JSONDecoder().decode(ServerLookupResponse.self, from: data)
completion(response)
} catch {
print(error)
completion(nil)
}
}

View File

@ -123,7 +123,7 @@ open class UDPBroadcastConnection {
// Set up cancel handler
newResponseSource.setCancelHandler {
debugPrint("Closing UDP socket")
//debugPrint("Closing UDP socket")
let UDPSocket = Int32(newResponseSource.handle)
shutdown(UDPSocket, SHUT_RDWR)
close(UDPSocket)
@ -158,12 +158,12 @@ open class UDPBroadcastConnection {
guard let endpoint = withUnsafePointer(to: &socketAddress, { self.getEndpointFromSocketAddress(socketAddressPointer: UnsafeRawPointer($0).bindMemory(to: sockaddr.self, capacity: 1)) })
else {
debugPrint("Failed to get the address and port from the socket address received from recvfrom")
//debugPrint("Failed to get the address and port from the socket address received from recvfrom")
self.closeConnection()
return
}
debugPrint("UDP connection received \(bytesRead) bytes from \(endpoint.host):\(endpoint.port)")
//debugPrint("UDP connection received \(bytesRead) bytes from \(endpoint.host):\(endpoint.port)")
let responseBytes = Data(response[0..<bytesRead])
@ -213,7 +213,7 @@ open class UDPBroadcastConnection {
guard sent > 0 else {
if let errorString = String(validatingUTF8: strerror(errno)) {
debugPrint("UDP connection failed to send data: \(errorString)")
//debugPrint("UDP connection failed to send data: \(errorString)")
}
closeConnection()
throw ConnectionError.sendingMessageFailed(code: errno)
@ -221,7 +221,7 @@ open class UDPBroadcastConnection {
if sent == broadcastMessageLength {
// Success
debugPrint("UDP connection sent \(broadcastMessageLength) bytes")
//debugPrint("UDP connection sent \(broadcastMessageLength) bytes")
}
}
}

View File

@ -18,7 +18,6 @@ struct LibraryFilters: Codable, Hashable {
}
public enum SortBy: String, Codable, CaseIterable {
case productionYear = "ProductionYear"
case premiereDate = "PremiereDate"
case name = "SortName"
case dateAdded = "DateCreated"
@ -27,14 +26,12 @@ public enum SortBy: String, Codable, CaseIterable {
extension SortBy {
var localized: String {
switch self {
case .productionYear:
return "Release Year"
case .premiereDate:
return "Premiere date"
case .name:
return "Title"
return "Name"
case .dateAdded:
return "Date Added"
return "Date added"
}
}
}

View File

@ -35,10 +35,26 @@ final class LibraryFilterViewModel: ViewModel {
var possibleItemFilters = ItemFilter.supportedTypes
@Published
var enabledFilterType: [FilterType]
@Published
var selectedSortOrder: APISortOrder = .descending
@Published
var selectedSortBy: SortBy = .name
func updateModifiedFilter() {
modifiedFilters.sortOrder = [selectedSortOrder]
modifiedFilters.sortBy = [selectedSortBy]
}
func resetFilters() {
modifiedFilters = LibraryFilters(filters: [], sortOrder: [.ascending], withGenres: [], tags: [], sortBy: [.name])
}
init(filters: LibraryFilters? = nil,
enabledFilterType: [FilterType] = [.tag, .genre, .sortBy, .sortOrder, .filter]) {
self.enabledFilterType = enabledFilterType
self.selectedSortBy = filters!.sortBy.first!
self.selectedSortOrder = filters!.sortOrder.first!
super.init()
if let filters = filters {
self.modifiedFilters = filters