jellyflood/Shared/SwiftfinStore/StoredValue/StoredValues+User.swift
Joe Kribs 216375905c
[iOS & tvOS] Trailers (#1456)
* ItemViewModel Trailers

* iOS done.

* Sections >>> Divider

* tvOS kind of.

* Button/Menu cleanup

* Huge ActionButton overhaul

* Error Handling, ActionButton/Menu standardization, and ActionButtonLayout cleanup part 1.

* cleanup

* cleanup

* Combine ActionButton logic. Complete ActionButton rework and animation/style rework. Should this be 3 files??

* Dumb sizing error. Get size from WIDTH not HEIGHT! Height is always 100 and Width is larger.

* Pressed buttons are but focused buttons but slight less. Pressed buttons are still bigger than default, unfocused buttons. TIL.

* Cleanup / Structure

* Remove Test.

* New Setting. Version on PlayButton Row. Complete TrailerMenu revamp. Make ActionButtonLayout a single row.

* Spacing & remove test logic

* VERY WIP

* Fix the compact-ness

* Linting.

* Remove Testing logic.

* Pre-Cleanup - WIP

* Finalized. Moved ScrollingText to tvOS Only.

* MediaURL? = nil but it's already nil by default.

* Error on the View not the button. This was NOT showing for the button since it lived on the Menu. This resolves this.

* wip

* Update VersionMenu.swift

* Remove scrollingText from this PR.

* Remove labels & iOS Action Button cleanup / no foregroundStyle on de-selected.

* ActionButtonScaling

* .card all buttons in ActionButton

* Slow and less bounce-i-fy the menu animations. Also, slight padding

* Wait, don't add this padding this isn't needed.

* localize

---------

Co-authored-by: Ethan Pippin <ethanpippin2343@gmail.com>
2025-04-05 01:13:20 -04:00

193 lines
5.5 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 JellyfinAPI
// TODO: also have matching properties on `UserState` that get/set values
// TODO: cleanup/organize
// MARK: keys
extension StoredValues.Keys {
/// Construct a key where `ownerID` is the id of the user in the
/// current user session, or always returns the default if there
/// isn't a current session user.
static func CurrentUserKey<Value: Codable>(
_ name: String?,
domain: String,
default defaultValue: Value
) -> Key<Value> {
guard let name, let currentUser = Container.shared.currentUserSession()?.user else {
return Key(always: defaultValue)
}
return Key(
name,
ownerID: currentUser.id,
domain: domain,
default: defaultValue
)
}
static func UserKey<Value: Codable>(
_ name: String?,
ownerID: String,
domain: String,
default defaultValue: Value
) -> Key<Value> {
guard let name else {
return Key(always: defaultValue)
}
return Key(
name,
ownerID: ownerID,
domain: domain,
default: defaultValue
)
}
static func UserKey<Value: Codable>(always: Value) -> Key<Value> {
Key(always: always)
}
}
// MARK: values
extension StoredValues.Keys {
enum User {
// Doesn't use `CurrentUserKey` because data may be
// retrieved and stored without a user session
static func accessPolicy(id: String) -> Key<UserAccessPolicy> {
UserKey(
"accessPolicy",
ownerID: id,
domain: "accessPolicy",
default: .none
)
}
// Doesn't use `CurrentUserKey` because data may be
// retrieved and stored without a user session
static func data(id: String) -> Key<UserDto> {
UserKey(
"userData",
ownerID: id,
domain: "userData",
default: .init()
)
}
static var accessPolicy: Key<UserAccessPolicy> {
CurrentUserKey(
"currentUserAccessPolicy",
domain: "currentUserAccessPolicy",
default: .none
)
}
static func libraryDisplayType(parentID: String?) -> Key<LibraryDisplayType> {
CurrentUserKey(
parentID,
domain: "setting-libraryDisplayType",
default: Defaults[.Customization.Library.displayType]
)
}
static func libraryListColumnCount(parentID: String?) -> Key<Int> {
CurrentUserKey(
parentID,
domain: "setting-libraryListColumnCount",
default: Defaults[.Customization.Library.listColumnCount]
)
}
static func libraryPosterType(parentID: String?) -> Key<PosterDisplayType> {
CurrentUserKey(
parentID,
domain: "setting-libraryPosterType",
default: Defaults[.Customization.Library.posterType]
)
}
// TODO: for now, only used for `sortBy` and `sortOrder`. Need to come up with
// rules for how stored filters work with libraries that should init
// with non-default filters (atow ex: favorites)
static func libraryFilters(parentID: String?) -> Key<ItemFilterCollection> {
CurrentUserKey(
parentID,
domain: "setting-libraryFilters",
default: ItemFilterCollection.default
)
}
static func pinHint(id: String) -> Key<String> {
UserKey(
"pinHint",
ownerID: id,
domain: "pinHint",
default: ""
)
}
static var customDeviceProfiles: Key<[CustomDeviceProfile]> {
CurrentUserKey(
"customDeviceProfiles",
domain: "customDeviceProfiles",
default: []
)
}
static var enableItemEditing: Key<Bool> {
CurrentUserKey(
"enableItemEditing",
domain: "enableItemEditing",
default: false
)
}
static var enableItemDeletion: Key<Bool> {
CurrentUserKey(
"enableItemDeletion",
domain: "enableItemDeletion",
default: false
)
}
static var enableCollectionManagement: Key<Bool> {
CurrentUserKey(
"enableCollectionManagement",
domain: "enableCollectionManagement",
default: false
)
}
static var enabledTrailers: Key<TrailerSelection> {
CurrentUserKey(
"enabledTrailers",
domain: "enabledTrailers",
default: .all
)
}
static var itemViewAttributes: Key<[ItemViewAttribute]> {
CurrentUserKey(
"itemViewAttributes",
domain: "itemViewAttributes",
default: ItemViewAttribute.allCases
)
}
}
}