Skip to content
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

[tvOS] App Settings & Splashscreen #1419

Merged
merged 14 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 16 additions & 0 deletions Shared/Strings/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,8 @@ internal enum L10n {
internal static func duplicateUserSaved(_ p1: Any) -> String {
return L10n.tr("Localizable", "duplicateUserSaved", String(describing: p1), fallback: "%@ is already saved")
}
/// Duration
internal static let duration = L10n.tr("Localizable", "duration", fallback: "Duration")
/// DVD
internal static let dvd = L10n.tr("Localizable", "dvd", fallback: "DVD")
/// Edit
Expand Down Expand Up @@ -1202,6 +1204,14 @@ internal enum L10n {
internal static func signInToServer(_ p1: UnsafePointer<CChar>) -> String {
return L10n.tr("Localizable", "signInToServer", p1, fallback: "Sign In to %s")
}
/// Sign out on background
internal static let signoutBackground = L10n.tr("Localizable", "signoutBackground", fallback: "Sign out on background")
/// Signs out the last user when Swiftfin has been in the background without media playback after some time
internal static let signoutBackgroundFooter = L10n.tr("Localizable", "signoutBackgroundFooter", fallback: "Signs out the last user when Swiftfin has been in the background without media playback after some time")
/// Sign out on close
internal static let signoutClose = L10n.tr("Localizable", "signoutClose", fallback: "Sign out on close")
/// Signs out the last user when Swiftfin has been force closed
internal static let signoutCloseFooter = L10n.tr("Localizable", "signoutCloseFooter", fallback: "Signs out the last user when Swiftfin has been force closed")
/// Slider
internal static let slider = L10n.tr("Localizable", "slider", fallback: "Slider")
/// Slider Color
Expand All @@ -1222,6 +1232,10 @@ internal enum L10n {
internal static let sourceCode = L10n.tr("Localizable", "sourceCode", fallback: "Source Code")
/// Special Features
internal static let specialFeatures = L10n.tr("Localizable", "specialFeatures", fallback: "Special Features")
/// Splashscreen
internal static let splashscreen = L10n.tr("Localizable", "splashscreen", fallback: "Splashscreen")
/// When All Servers is selected, use the splashscreen from a single server or a random server
internal static let splashscreenFooter = L10n.tr("Localizable", "splashscreenFooter", fallback: "When All Servers is selected, use the splashscreen from a single server or a random server")
/// Sports
internal static let sports = L10n.tr("Localizable", "sports", fallback: "Sports")
/// Start Time
Expand Down Expand Up @@ -1402,6 +1416,8 @@ internal enum L10n {
}
/// Users
internal static let users = L10n.tr("Localizable", "users", fallback: "Users")
/// Use splashscreen
internal static let useSplashscreen = L10n.tr("Localizable", "useSplashscreen", fallback: "Use splashscreen")
/// Version
internal static let version = L10n.tr("Localizable", "version", fallback: "Version")
/// Video
Expand Down
38 changes: 18 additions & 20 deletions Shared/ViewModels/SettingsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,30 +26,28 @@ final class SettingsViewModel: ViewModel {

override init() {

guard let iconName = UIApplication.shared.alternateIconName else {
currentAppIcon = PrimaryAppIcon.primary
super.init()
return
}

if let appicon = PrimaryAppIcon.createCase(iconName: iconName) {
currentAppIcon = appicon
}
if let iconName = UIApplication.shared.alternateIconName {
if let appicon = PrimaryAppIcon.createCase(iconName: iconName) {
currentAppIcon = appicon
}

if let appicon = DarkAppIcon.createCase(iconName: iconName) {
currentAppIcon = appicon
}
if let appicon = DarkAppIcon.createCase(iconName: iconName) {
currentAppIcon = appicon
}

if let appicon = InvertedDarkAppIcon.createCase(iconName: iconName) {
currentAppIcon = appicon
}
if let appicon = InvertedDarkAppIcon.createCase(iconName: iconName) {
currentAppIcon = appicon
}

if let appicon = InvertedLightAppIcon.createCase(iconName: iconName) {
currentAppIcon = appicon
}
if let appicon = InvertedLightAppIcon.createCase(iconName: iconName) {
currentAppIcon = appicon
}

if let appicon = LightAppIcon.createCase(iconName: iconName) {
currentAppIcon = appicon
if let appicon = LightAppIcon.createCase(iconName: iconName) {
currentAppIcon = appicon
}
} else {
currentAppIcon = PrimaryAppIcon.primary
}

super.init()
Expand Down
5 changes: 5 additions & 0 deletions Swiftfin tvOS/App/SwiftfinApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ struct SwiftfinApp: App {
// UIKit

UINavigationBar.appearance().titleTextAttributes = [.foregroundColor: UIColor.label]

// don't keep last user id
if Defaults[.signOutOnClose] {
Defaults[.lastSignedInUserID] = .signedOut
}
}

var body: some Scene {
Expand Down
100 changes: 100 additions & 0 deletions Swiftfin tvOS/Views/AppSettingsView/AppSettingsView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//
// 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 (c) 2025 Jellyfin & Jellyfin Contributors
//

import Defaults
import Stinsen
import SwiftUI

struct AppSettingsView: View {

@Default(.selectUserUseSplashscreen)
private var selectUserUseSplashscreen
@Default(.selectUserAllServersSplashscreen)
private var selectUserAllServersSplashscreen

@Default(.appAppearance)
private var appearance

@EnvironmentObject
private var router: AppSettingsCoordinator.Router

@StateObject
private var viewModel = SettingsViewModel()

@State
private var resetUserSettingsSelected: Bool = false
@State
private var removeAllServersSelected: Bool = false

var body: some View {
SplitFormWindowView()
.descriptionView {
Image(.jellyfinBlobBlue)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 400)
}
.contentView {

TextPairView(
leading: L10n.version,
trailing: "\(UIApplication.appVersion ?? .emptyDash) (\(UIApplication.bundleVersion ?? .emptyDash))"
)

Section {

Toggle(L10n.useSplashscreen, isOn: $selectUserUseSplashscreen)

if selectUserUseSplashscreen {
Menu {
Picker(L10n.servers, selection: $selectUserAllServersSplashscreen) {

Label(L10n.random, systemImage: "dice.fill")
.tag(SelectUserServerSelection.all)

ForEach(viewModel.servers) { server in
Text(server.name)
.tag(SelectUserServerSelection.server(id: server.id))
}
}
} label: {
HStack {
Text(L10n.servers)
.frame(maxWidth: .infinity, alignment: .leading)

if selectUserAllServersSplashscreen == .all {
Label(L10n.random, systemImage: "dice.fill")
} else if let server = viewModel.servers.first(
where: { server in
selectUserAllServersSplashscreen == .server(id: server.id)
}
) {
Text(server.name)
}
}
}
.listRowBackground(Color.clear)
.listRowInsets(.zero)
}
} header: {
Text(L10n.splashscreen)
} footer: {
if selectUserUseSplashscreen {
Text(L10n.splashscreenFooter)
}
}

SignOutIntervalSection()

ChevronButton(L10n.logs)
.onSelect {
router.route(to: \.log)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// 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 (c) 2025 Jellyfin & Jellyfin Contributors
//

import Defaults
import SwiftUI

extension AppSettingsView {

struct SignOutIntervalSection: View {

@Default(.backgroundSignOutInterval)
private var backgroundSignOutInterval
@Default(.signOutOnBackground)
private var signOutOnBackground
@Default(.signOutOnClose)
private var signOutOnClose

@State
private var isEditingBackgroundSignOutInterval: Bool = false

var body: some View {
Section {
Toggle(L10n.signoutClose, isOn: $signOutOnClose)
} footer: {
Text(L10n.signoutCloseFooter)
}

// TODO: need to consider date picker options to re-enable
// Section {
// Toggle(L10n.signoutBackground, isOn: $signOutOnBackground)
//
// if signOutOnBackground {
// HStack {
// Text(L10n.duration)
//
// Spacer()
//
// Button {
// isEditingBackgroundSignOutInterval.toggle()
// } label: {
// HStack {
// Text(backgroundSignOutInterval, format: .hourMinute)
// .foregroundStyle(.secondary)
//
// Image(systemName: "chevron.right")
// .font(.body.weight(.semibold))
// .foregroundStyle(.secondary)
// .rotationEffect(isEditingBackgroundSignOutInterval ? .degrees(90) : .zero)
// .animation(.linear(duration: 0.075), value: isEditingBackgroundSignOutInterval)
// }
// }
// .foregroundStyle(.primary, .secondary)
// }
//
// if isEditingBackgroundSignOutInterval {
// HourMinutePicker(interval: $backgroundSignOutInterval)
// }
// }
// } footer: {
// Text(
// L10n.signoutBackgroundFooter
// )
// }
// .animation(.linear(duration: 0.15), value: isEditingBackgroundSignOutInterval)
}
}
}
90 changes: 0 additions & 90 deletions Swiftfin tvOS/Views/BasicAppSettingsView.swift

This file was deleted.

Loading