269 lines
12 KiB
Swift
269 lines
12 KiB
Swift
//
|
|
// 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 Factory
|
|
import Foundation
|
|
import SwiftUI
|
|
import UIKit
|
|
|
|
// TODO: organize
|
|
// TODO: all user settings could be moved to `StoredValues`?
|
|
|
|
// Note: Only use Defaults for basic single-value settings.
|
|
// For larger data types and collections, use `StoredValue` instead.
|
|
|
|
// MARK: Suites
|
|
|
|
extension UserDefaults {
|
|
|
|
// MARK: App
|
|
|
|
/// Settings that should apply to the app
|
|
static let appSuite = UserDefaults(suiteName: "swiftfinApp")!
|
|
|
|
// MARK: Usser
|
|
|
|
// TODO: the Factory resolver cannot be used because it would cause freezes, but
|
|
// the Defaults value should always be in sync with the latest user and what
|
|
// views properly expect. However, this feels like a hack and should be changed?
|
|
static var currentUserSuite: UserDefaults {
|
|
switch Defaults[.lastSignedInUserID] {
|
|
case .signedOut:
|
|
return userSuite(id: "default")
|
|
case let .signedIn(userID):
|
|
return userSuite(id: userID)
|
|
}
|
|
}
|
|
|
|
static func userSuite(id: String) -> UserDefaults {
|
|
UserDefaults(suiteName: id)!
|
|
}
|
|
}
|
|
|
|
private extension Defaults.Keys {
|
|
|
|
static func AppKey<Value: Defaults.Serializable>(_ name: String) -> Key<Value?> {
|
|
Key(name, suite: .appSuite)
|
|
}
|
|
|
|
static func AppKey<Value: Defaults.Serializable>(_ name: String, default: Value) -> Key<Value> {
|
|
Key(name, default: `default`, suite: .appSuite)
|
|
}
|
|
|
|
static func UserKey<Value: Defaults.Serializable>(_ name: String, default: Value) -> Key<Value> {
|
|
Key(name, default: `default`, suite: .currentUserSuite)
|
|
}
|
|
}
|
|
|
|
// MARK: App
|
|
|
|
extension Defaults.Keys {
|
|
|
|
/// The _real_ accent color key to be used.
|
|
///
|
|
/// This is set externally whenever the app or user accent colors change,
|
|
/// depending on the current app state.
|
|
static var accentColor: Key<Color> = AppKey("accentColor", default: .jellyfinPurple)
|
|
|
|
/// The _real_ appearance key to be used.
|
|
///
|
|
/// This is set externally whenever the app or user appearances change,
|
|
/// depending on the current app state.
|
|
static let appearance: Key<AppAppearance> = AppKey("appearance", default: .system)
|
|
|
|
/// The appearance default for non-user contexts.
|
|
/// /// Only use for `set`, use `appearance` for `get`.
|
|
static let appAppearance: Key<AppAppearance> = AppKey("appAppearance", default: .system)
|
|
|
|
static let backgroundSignOutInterval: Key<TimeInterval> = AppKey("backgroundSignOutInterval", default: 3600)
|
|
static let backgroundTimeStamp: Key<Date> = AppKey("backgroundTimeStamp", default: Date.now)
|
|
static let lastSignedInUserID: Key<UserSignInState> = AppKey("lastSignedInUserID", default: .signedOut)
|
|
|
|
static let selectUserDisplayType: Key<LibraryDisplayType> = AppKey("selectUserDisplayType", default: .grid)
|
|
static let selectUserServerSelection: Key<SelectUserServerSelection> = AppKey("selectUserServerSelection", default: .all)
|
|
static let selectUserAllServersSplashscreen: Key<SelectUserServerSelection> = AppKey("selectUserAllServersSplashscreen", default: .all)
|
|
static let selectUserUseSplashscreen: Key<Bool> = AppKey("selectUserUseSplashscreen", default: true)
|
|
|
|
static let signOutOnBackground: Key<Bool> = AppKey("signOutOnBackground", default: true)
|
|
static let signOutOnClose: Key<Bool> = AppKey("signOutOnClose", default: false)
|
|
}
|
|
|
|
// MARK: User
|
|
|
|
extension Defaults.Keys {
|
|
|
|
/// The accent color default for user contexts.
|
|
/// Only use for `set`, use `accentColor` for `get`.
|
|
static var userAccentColor: Key<Color> { UserKey("userAccentColor", default: .jellyfinPurple) }
|
|
|
|
/// The appearance default for user contexts.
|
|
/// /// Only use for `set`, use `appearance` for `get`.
|
|
static var userAppearance: Key<AppAppearance> { UserKey("userAppearance", default: .system) }
|
|
|
|
enum Customization {
|
|
|
|
static let itemViewType: Key<ItemViewType> = UserKey("itemViewType", default: .compactLogo)
|
|
|
|
static let showPosterLabels: Key<Bool> = UserKey("showPosterLabels", default: true)
|
|
static let nextUpPosterType: Key<PosterDisplayType> = UserKey("nextUpPosterType", default: .portrait)
|
|
static let recentlyAddedPosterType: Key<PosterDisplayType> = UserKey("recentlyAddedPosterType", default: .portrait)
|
|
static let latestInLibraryPosterType: Key<PosterDisplayType> = UserKey("latestInLibraryPosterType", default: .portrait)
|
|
static let shouldShowMissingSeasons: Key<Bool> = UserKey("shouldShowMissingSeasons", default: true)
|
|
static let shouldShowMissingEpisodes: Key<Bool> = UserKey("shouldShowMissingEpisodes", default: true)
|
|
static let similarPosterType: Key<PosterDisplayType> = UserKey("similarPosterType", default: .portrait)
|
|
|
|
// TODO: have search poster type by types of items if applicable
|
|
static let searchPosterType: Key<PosterDisplayType> = UserKey("searchPosterType", default: .portrait)
|
|
|
|
enum CinematicItemViewType {
|
|
|
|
static let usePrimaryImage: Key<Bool> = UserKey("cinematicItemViewTypeUsePrimaryImage", default: false)
|
|
}
|
|
|
|
enum Episodes {
|
|
|
|
static let useSeriesLandscapeBackdrop: Key<Bool> = UserKey("useSeriesBackdrop", default: true)
|
|
}
|
|
|
|
enum Indicators {
|
|
|
|
static let showFavorited: Key<Bool> = UserKey("showFavoritedIndicator", default: true)
|
|
static let showProgress: Key<Bool> = UserKey("showProgressIndicator", default: true)
|
|
static let showUnplayed: Key<Bool> = UserKey("showUnplayedIndicator", default: true)
|
|
static let showPlayed: Key<Bool> = UserKey("showPlayedIndicator", default: true)
|
|
}
|
|
|
|
enum Library {
|
|
|
|
static let cinematicBackground: Key<Bool> = UserKey("libraryCinematicBackground", default: true)
|
|
static let enabledDrawerFilters: Key<[ItemFilterType]> = UserKey(
|
|
"libraryEnabledDrawerFilters",
|
|
default: ItemFilterType.allCases
|
|
)
|
|
static let letterPickerEnabled: Key<Bool> = UserKey("letterPickerEnabled", default: false)
|
|
static let letterPickerOrientation: Key<LetterPickerOrientation> = .init(
|
|
"letterPickerOrientation", default: .trailing
|
|
)
|
|
static let displayType: Key<LibraryDisplayType> = UserKey("libraryViewType", default: .grid)
|
|
static let posterType: Key<PosterDisplayType> = UserKey("libraryPosterType", default: .portrait)
|
|
static let listColumnCount: Key<Int> = UserKey("listColumnCount", default: 1)
|
|
static let randomImage: Key<Bool> = UserKey("libraryRandomImage", default: true)
|
|
static let showFavorites: Key<Bool> = UserKey("libraryShowFavorites", default: true)
|
|
|
|
static let rememberLayout: Key<Bool> = UserKey("libraryRememberLayout", default: false)
|
|
static let rememberSort: Key<Bool> = UserKey("libraryRememberSort", default: false)
|
|
}
|
|
|
|
enum Home {
|
|
static let showRecentlyAdded: Key<Bool> = UserKey("showRecentlyAdded", default: true)
|
|
static let resumeNextUp: Key<Bool> = UserKey("homeResumeNextUp", default: false)
|
|
static let maxNextUp: Key<TimeInterval> = UserKey(
|
|
"homeMaxNextUp",
|
|
default: 366 * 86400
|
|
)
|
|
}
|
|
|
|
enum Search {
|
|
|
|
static let enabledDrawerFilters: Key<[ItemFilterType]> = UserKey(
|
|
"searchEnabledDrawerFilters",
|
|
default: ItemFilterType.allCases
|
|
)
|
|
}
|
|
}
|
|
|
|
enum VideoPlayer {
|
|
|
|
static let appMaximumBitrate: Key<PlaybackBitrate> = UserKey("appMaximumBitrate", default: .max)
|
|
static let appMaximumBitrateTest: Key<PlaybackBitrateTestSize> = UserKey("appMaximumBitrateTest", default: .regular)
|
|
static let autoPlayEnabled: Key<Bool> = UserKey("autoPlayEnabled", default: true)
|
|
static let barActionButtons: Key<[VideoPlayerActionButton]> = UserKey(
|
|
"barActionButtons",
|
|
default: VideoPlayerActionButton.defaultBarActionButtons
|
|
)
|
|
static let jumpBackwardInterval: Key<MediaJumpInterval> = UserKey("jumpBackwardLength", default: .fifteen)
|
|
static let jumpForwardInterval: Key<MediaJumpInterval> = UserKey("jumpForwardLength", default: .fifteen)
|
|
static let menuActionButtons: Key<[VideoPlayerActionButton]> = UserKey(
|
|
"menuActionButtons",
|
|
default: VideoPlayerActionButton.defaultMenuActionButtons
|
|
)
|
|
static let resumeOffset: Key<Int> = UserKey("resumeOffset", default: 0)
|
|
static let videoPlayerType: Key<VideoPlayerType> = UserKey("videoPlayerType", default: .swiftfin)
|
|
|
|
enum Gesture {
|
|
|
|
static let horizontalPanAction: Key<PanGestureAction> = UserKey("videoPlayerHorizontalPanGesture", default: .none)
|
|
static let horizontalSwipeAction: Key<SwipeGestureAction> = UserKey("videoPlayerhorizontalSwipeAction", default: .none)
|
|
static let longPressAction: Key<LongPressGestureAction> = UserKey("videoPlayerLongPressGesture", default: .gestureLock)
|
|
static let multiTapGesture: Key<MultiTapGestureAction> = UserKey("videoPlayerMultiTapGesture", default: .none)
|
|
static let doubleTouchGesture: Key<DoubleTouchGestureAction> = UserKey("videoPlayerDoubleTouchGesture", default: .none)
|
|
static let pinchGesture: Key<PinchGestureAction> = UserKey("videoPlayerSwipeGesture", default: .aspectFill)
|
|
static let verticalPanLeftAction: Key<PanGestureAction> = UserKey("videoPlayerverticalPanLeftAction", default: .none)
|
|
static let verticalPanRightAction: Key<PanGestureAction> = UserKey("videoPlayerverticalPanRightAction", default: .none)
|
|
}
|
|
|
|
enum Overlay {
|
|
|
|
static let chapterSlider: Key<Bool> = UserKey("chapterSlider", default: true)
|
|
|
|
// Timestamp
|
|
static let trailingTimestampType: Key<TrailingTimestampType> = UserKey("trailingTimestamp", default: .timeLeft)
|
|
}
|
|
|
|
enum Playback {
|
|
static let appMaximumBitrate: Key<PlaybackBitrate> = UserKey("appMaximumBitrate", default: .auto)
|
|
static let appMaximumBitrateTest: Key<PlaybackBitrateTestSize> = UserKey("appMaximumBitrateTest", default: .regular)
|
|
static let compatibilityMode: Key<PlaybackCompatibility> = UserKey("compatibilityMode", default: .auto)
|
|
static let customDeviceProfileAction: Key<CustomDeviceProfileAction> = UserKey("customDeviceProfileAction", default: .add)
|
|
static let rates: Key<[Float]> = UserKey("videoPlayerPlaybackRates", default: [0.5, 1.0, 1.25, 1.5, 2.0])
|
|
}
|
|
|
|
// TODO: transition into a SubtitleConfiguration instead of multiple types
|
|
enum Subtitle {
|
|
|
|
static let subtitleColor: Key<Color> = UserKey("subtitleColor", default: .white)
|
|
static let subtitleFontName: Key<String> = UserKey("subtitleFontName", default: UIFont.systemFont(ofSize: 14).fontName)
|
|
static let subtitleSize: Key<Int> = UserKey("subtitleSize", default: 9)
|
|
}
|
|
|
|
enum Transition {
|
|
static let pauseOnBackground: Key<Bool> = UserKey("playInBackground", default: true)
|
|
}
|
|
}
|
|
|
|
// Experimental settings
|
|
enum Experimental {
|
|
|
|
static let downloads: Key<Bool> = UserKey("experimentalDownloads", default: false)
|
|
}
|
|
|
|
// tvos specific
|
|
static let downActionShowsMenu: Key<Bool> = UserKey("downActionShowsMenu", default: true)
|
|
static let confirmClose: Key<Bool> = UserKey("confirmClose", default: false)
|
|
}
|
|
|
|
// MARK: Debug
|
|
|
|
#if DEBUG
|
|
|
|
extension UserDefaults {
|
|
|
|
static let debugSuite = UserDefaults(suiteName: "swiftfinstore-debug-defaults")!
|
|
}
|
|
|
|
extension Defaults.Keys {
|
|
|
|
static func DebugKey<Value: Defaults.Serializable>(_ name: String, default: Value) -> Key<Value> {
|
|
Key(name, default: `default`, suite: .appSuite)
|
|
}
|
|
|
|
static let sendProgressReports: Key<Bool> = DebugKey("sendProgressReports", default: true)
|
|
}
|
|
#endif
|