// // 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(_ name: String) -> Key { Key(name, suite: .appSuite) } static func AppKey(_ name: String, default: Value) -> Key { Key(name, default: `default`, suite: .appSuite) } static func UserKey(_ name: String, default: Value) -> Key { 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 = 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 = AppKey("appearance", default: .system) /// The accent color default for non-user contexts. /// Only use for `set`, use `accentColor` for `get`. static let appAccentColor: Key = AppKey("appAccentColor", default: .jellyfinPurple) /// The appearance default for non-user contexts. /// /// Only use for `set`, use `appearance` for `get`. static let appAppearance: Key = AppKey("appAppearance", default: .system) static let backgroundSignOutInterval: Key = AppKey("backgroundSignOutInterval", default: 3600) static let backgroundTimeStamp: Key = AppKey("backgroundTimeStamp", default: Date.now) static let lastSignedInUserID: Key = AppKey("lastSignedInUserID", default: .signedOut) static let selectUserDisplayType: Key = AppKey("selectUserDisplayType", default: .grid) static let selectUserServerSelection: Key = AppKey("selectUserServerSelection", default: .all) static let selectUserAllServersSplashscreen: Key = AppKey("selectUserAllServersSplashscreen", default: .all) static let selectUserUseSplashscreen: Key = AppKey("selectUserUseSplashscreen", default: true) static let signOutOnBackground: Key = AppKey("signOutOnBackground", default: true) static let signOutOnClose: Key = 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 { UserKey("userAccentColor", default: .jellyfinPurple) } /// The appearance default for user contexts. /// /// Only use for `set`, use `appearance` for `get`. static var userAppearance: Key { UserKey("userAppearance", default: .system) } enum Customization { static let itemViewType: Key = UserKey("itemViewType", default: .compactLogo) static let showPosterLabels: Key = UserKey("showPosterLabels", default: true) static let nextUpPosterType: Key = UserKey("nextUpPosterType", default: .portrait) static let recentlyAddedPosterType: Key = UserKey("recentlyAddedPosterType", default: .portrait) static let latestInLibraryPosterType: Key = UserKey("latestInLibraryPosterType", default: .portrait) static let shouldShowMissingSeasons: Key = UserKey("shouldShowMissingSeasons", default: true) static let shouldShowMissingEpisodes: Key = UserKey("shouldShowMissingEpisodes", default: true) static let similarPosterType: Key = UserKey("similarPosterType", default: .portrait) // TODO: have search poster type by types of items if applicable static let searchPosterType: Key = UserKey("searchPosterType", default: .portrait) enum CinematicItemViewType { static let usePrimaryImage: Key = UserKey("cinematicItemViewTypeUsePrimaryImage", default: false) } enum Episodes { static let useSeriesLandscapeBackdrop: Key = UserKey("useSeriesBackdrop", default: true) } enum Indicators { static let showFavorited: Key = UserKey("showFavoritedIndicator", default: true) static let showProgress: Key = UserKey("showProgressIndicator", default: true) static let showUnplayed: Key = UserKey("showUnplayedIndicator", default: true) static let showPlayed: Key = UserKey("showPlayedIndicator", default: true) } enum Library { static let cinematicBackground: Key = UserKey("libraryCinematicBackground", default: true) static let enabledDrawerFilters: Key<[ItemFilterType]> = UserKey( "libraryEnabledDrawerFilters", default: ItemFilterType.allCases ) static let letterPickerEnabled: Key = UserKey("letterPickerEnabled", default: false) static let letterPickerOrientation: Key = .init( "letterPickerOrientation", default: .trailing ) static let displayType: Key = UserKey("libraryViewType", default: .grid) static let posterType: Key = UserKey("libraryPosterType", default: .portrait) static let listColumnCount: Key = UserKey("listColumnCount", default: 1) static let randomImage: Key = UserKey("libraryRandomImage", default: true) static let showFavorites: Key = UserKey("libraryShowFavorites", default: true) static let rememberLayout: Key = UserKey("libraryRememberLayout", default: false) static let rememberSort: Key = UserKey("libraryRememberSort", default: false) } enum Home { static let showRecentlyAdded: Key = UserKey("showRecentlyAdded", default: true) static let resumeNextUp: Key = UserKey("homeResumeNextUp", default: false) static let maxNextUp: Key = UserKey( "homeMaxNextUp", default: 366 * 86400 ) } enum Search { static let enabledDrawerFilters: Key<[ItemFilterType]> = UserKey( "searchEnabledDrawerFilters", default: ItemFilterType.allCases ) } } enum VideoPlayer { static let appMaximumBitrate: Key = UserKey("appMaximumBitrate", default: .max) static let appMaximumBitrateTest: Key = UserKey("appMaximumBitrateTest", default: .regular) static let autoPlayEnabled: Key = UserKey("autoPlayEnabled", default: true) static let barActionButtons: Key<[VideoPlayerActionButton]> = UserKey( "barActionButtons", default: VideoPlayerActionButton.defaultBarActionButtons ) static let jumpBackwardLength: Key = UserKey("jumpBackwardLength", default: .fifteen) static let jumpForwardLength: Key = UserKey("jumpForwardLength", default: .fifteen) static let menuActionButtons: Key<[VideoPlayerActionButton]> = UserKey( "menuActionButtons", default: VideoPlayerActionButton.defaultMenuActionButtons ) static let resumeOffset: Key = UserKey("resumeOffset", default: 0) static let showJumpButtons: Key = UserKey("showJumpButtons", default: true) static let videoPlayerType: Key = UserKey("videoPlayerType", default: .swiftfin) enum Gesture { static let horizontalPanGesture: Key = UserKey("videoPlayerHorizontalPanGesture", default: .none) static let horizontalSwipeGesture: Key = UserKey("videoPlayerHorizontalSwipeGesture", default: .none) static let longPressGesture: Key = UserKey("videoPlayerLongPressGesture", default: .gestureLock) static let multiTapGesture: Key = UserKey("videoPlayerMultiTapGesture", default: .none) static let doubleTouchGesture: Key = UserKey("videoPlayerDoubleTouchGesture", default: .none) static let pinchGesture: Key = UserKey("videoPlayerSwipeGesture", default: .aspectFill) static let verticalPanGestureLeft: Key = UserKey("videoPlayerVerticalPanGestureLeft", default: .none) static let verticalPanGestureRight: Key = UserKey("videoPlayerVerticalPanGestureRight", default: .none) } enum Overlay { static let chapterSlider: Key = UserKey("chapterSlider", default: true) static let playbackButtonType: Key = UserKey("videoPlayerPlaybackButtonLocation", default: .large) static let sliderColor: Key = UserKey("sliderColor", default: Color.white) static let sliderType: Key = UserKey("sliderType", default: .capsule) // Timestamp static let trailingTimestampType: Key = UserKey("trailingTimestamp", default: .timeLeft) static let showCurrentTimeWhileScrubbing: Key = UserKey("showCurrentTimeWhileScrubbing", default: true) static let timestampType: Key = UserKey("timestampType", default: .split) } enum Playback { static let appMaximumBitrate: Key = UserKey("appMaximumBitrate", default: .auto) static let appMaximumBitrateTest: Key = UserKey("appMaximumBitrateTest", default: .regular) static let compatibilityMode: Key = UserKey("compatibilityMode", default: .auto) static let customDeviceProfileAction: Key = UserKey("customDeviceProfileAction", default: .add) } enum Subtitle { static let subtitleColor: Key = UserKey("subtitleColor", default: .white) static let subtitleFontName: Key = UserKey("subtitleFontName", default: UIFont.systemFont(ofSize: 14).fontName) static let subtitleSize: Key = UserKey("subtitleSize", default: 16) } enum Transition { static let pauseOnBackground: Key = UserKey("pauseOnBackground", default: false) static let playOnActive: Key = UserKey("playOnActive", default: false) } } // Experimental settings enum Experimental { static let downloads: Key = UserKey("experimentalDownloads", default: false) } // tvos specific static let downActionShowsMenu: Key = UserKey("downActionShowsMenu", default: true) static let confirmClose: Key = UserKey("confirmClose", default: false) } // MARK: Debug #if DEBUG extension UserDefaults { static let debugSuite = UserDefaults(suiteName: "swiftfinstore-debug-defaults")! } extension Defaults.Keys { static func DebugKey(_ name: String, default: Value) -> Key { Key(name, default: `default`, suite: .appSuite) } static let sendProgressReports: Key = DebugKey("sendProgressReports", default: true) } #endif