Skip to content

Stateful - Set cleanup and final classes #1465

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

Merged
merged 2 commits into from
Mar 30, 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
17 changes: 2 additions & 15 deletions Shared/Objects/Stateful.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,11 @@
// Copyright (c) 2025 Jellyfin & Jellyfin Contributors
//

import Foundation
import OrderedCollections

// TODO: documentation
// TODO: find a better way to handle backgroundStates on action/state transitions
// so that conformers don't have to manually insert/remove them
// TODO: better/official way for subclasses of conformers to perform actions during
// parent class actions
// TODO: official way for a cleaner `respond` method so it doesn't have all Task
// construction and get bloated
// TODO: move backgroundStates to just a `Set`

protocol Stateful: AnyObject {

Expand All @@ -27,9 +21,8 @@ protocol Stateful: AnyObject {
/// Background states that the conformer can be in.
/// Usually used to indicate background events that shouldn't
/// set the conformer to a primary state.
var backgroundStates: OrderedSet<BackgroundState> { get set }
var backgroundStates: Set<BackgroundState> { get set }

var lastAction: Action? { get set }
var state: State { get set }

/// Respond to a sent action and return the new state
Expand All @@ -44,21 +37,15 @@ protocol Stateful: AnyObject {

extension Stateful {

var lastAction: Action? {
get { nil }
set {}
}

@MainActor
func send(_ action: Action) {
state = respond(to: action)
lastAction = action
}
}

extension Stateful where BackgroundState == Never {

var backgroundStates: OrderedSet<Never> {
var backgroundStates: Set<Never> {
get {
assertionFailure("Attempted to access `backgroundStates` when there are none")
return []
Expand Down
4 changes: 2 additions & 2 deletions Shared/ViewModels/AdminDashboard/APIKeysViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ final class APIKeysViewModel: ViewModel, Stateful {
// MARK: Published Variables

@Published
final var apiKeys: [AuthenticationInfo] = []
var apiKeys: [AuthenticationInfo] = []
@Published
final var state: State = .initial
var state: State = .initial

// MARK: Action Responses

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ final class ActiveSessionsViewModel: ViewModel, Stateful {
}

@Published
final var backgroundStates: OrderedSet<BackgroundState> = []
var backgroundStates: Set<BackgroundState> = []
@Published
final var sessions: OrderedDictionary<String, BindingBox<SessionInfo?>> = [:]
var sessions: OrderedDictionary<String, BindingBox<SessionInfo?>> = [:]
@Published
final var state: State = .initial
var state: State = .initial

private let activeWithinSeconds: Int = 960
private var sessionTask: AnyCancellable?
Expand All @@ -52,7 +52,7 @@ final class ActiveSessionsViewModel: ViewModel, Stateful {

sessionTask = Task { [weak self] in
await MainActor.run {
let _ = self?.backgroundStates.append(.gettingSessions)
let _ = self?.backgroundStates.insert(.gettingSessions)
}

do {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ final class AddServerUserViewModel: ViewModel, Eventful, Stateful, Identifiable
}

@Published
final var state: State = .initial
var state: State = .initial

private var userTask: AnyCancellable?
private var eventSubject: PassthroughSubject<Event, Never> = .init()
Expand Down
6 changes: 3 additions & 3 deletions Shared/ViewModels/AdminDashboard/DeviceDetailViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Foundation
import JellyfinAPI
import OrderedCollections

class DeviceDetailViewModel: ViewModel, Stateful, Eventful {
final class DeviceDetailViewModel: ViewModel, Stateful, Eventful {

enum Event {
case error(JellyfinAPIError)
Expand All @@ -31,7 +31,7 @@ class DeviceDetailViewModel: ViewModel, Stateful, Eventful {
}

@Published
var backgroundStates: OrderedSet<BackgroundState> = []
var backgroundStates: Set<BackgroundState> = []
@Published
var state: State = .initial

Expand All @@ -57,7 +57,7 @@ class DeviceDetailViewModel: ViewModel, Stateful, Eventful {

Task {
await MainActor.run {
_ = backgroundStates.append(.updating)
_ = backgroundStates.insert(.updating)
}

do {
Expand Down
10 changes: 5 additions & 5 deletions Shared/ViewModels/AdminDashboard/DevicesViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ final class DevicesViewModel: ViewModel, Eventful, Stateful {
// MARK: Published Values

@Published
final var backgroundStates: OrderedSet<BackgroundState> = []
var backgroundStates: Set<BackgroundState> = []
@Published
final var devices: [DeviceInfo] = []
var devices: [DeviceInfo] = []
@Published
final var state: State = .initial
var state: State = .initial

var events: AnyPublisher<Event, Never> {
eventSubject
Expand All @@ -69,7 +69,7 @@ final class DevicesViewModel: ViewModel, Eventful, Stateful {
case .refresh:
deviceTask?.cancel()

backgroundStates.append(.refreshing)
backgroundStates.insert(.refreshing)

deviceTask = Task { [weak self] in
do {
Expand Down Expand Up @@ -98,7 +98,7 @@ final class DevicesViewModel: ViewModel, Eventful, Stateful {
case let .delete(ids):
deviceTask?.cancel()

backgroundStates.append(.deleting)
backgroundStates.insert(.deleting)

deviceTask = Task { [weak self] in
do {
Expand Down
8 changes: 4 additions & 4 deletions Shared/ViewModels/AdminDashboard/ServerTaskObserver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ final class ServerTaskObserver: ViewModel, Stateful, Eventful, Identifiable {
// MARK: Published Values

@Published
final var backgroundStates: OrderedSet<BackgroundState> = []
var backgroundStates: Set<BackgroundState> = []
@Published
final var state: State = .initial
var state: State = .initial
@Published
private(set) var task: TaskInfo

Expand Down Expand Up @@ -138,7 +138,7 @@ final class ServerTaskObserver: ViewModel, Stateful, Eventful, Identifiable {
.appending(trigger)

await MainActor.run {
_ = self.backgroundStates.append(.updatingTriggers)
_ = self.backgroundStates.insert(.updatingTriggers)
}

do {
Expand All @@ -165,7 +165,7 @@ final class ServerTaskObserver: ViewModel, Stateful, Eventful, Identifiable {
updatedTriggers.removeAll { $0 == trigger }

await MainActor.run {
_ = self.backgroundStates.append(.updatingTriggers)
_ = self.backgroundStates.insert(.updatingTriggers)
}

do {
Expand Down
6 changes: 3 additions & 3 deletions Shared/ViewModels/AdminDashboard/ServerTasksViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ final class ServerTasksViewModel: ViewModel, Stateful {
}

@Published
final var backgroundStates: OrderedSet<BackgroundState> = []
var backgroundStates: Set<BackgroundState> = []
@Published
final var state: State = .initial
var state: State = .initial
@Published
final var tasks: OrderedDictionary<String, [ServerTaskObserver]> = [:]
var tasks: OrderedDictionary<String, [ServerTaskObserver]> = [:]

private var getTasksCancellable: AnyCancellable?

Expand Down
14 changes: 7 additions & 7 deletions Shared/ViewModels/AdminDashboard/ServerUserAdminViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ final class ServerUserAdminViewModel: ViewModel, Eventful, Stateful, Identifiabl
// MARK: - Published Values

@Published
final var state: State = .initial
var state: State = .initial
@Published
final var backgroundStates: OrderedSet<BackgroundState> = []
var backgroundStates: Set<BackgroundState> = []

@Published
private(set) var user: UserDto
Expand Down Expand Up @@ -99,7 +99,7 @@ final class ServerUserAdminViewModel: ViewModel, Eventful, Stateful, Identifiabl
userTaskCancellable = Task {
do {
await MainActor.run {
_ = backgroundStates.append(.refreshing)
_ = backgroundStates.insert(.refreshing)
}

try await loadDetails()
Expand All @@ -126,7 +126,7 @@ final class ServerUserAdminViewModel: ViewModel, Eventful, Stateful, Identifiabl
userTaskCancellable = Task {
do {
await MainActor.run {
_ = backgroundStates.append(.refreshing)
_ = backgroundStates.insert(.refreshing)
}

try await loadLibraries(isHidden: isHidden)
Expand All @@ -153,7 +153,7 @@ final class ServerUserAdminViewModel: ViewModel, Eventful, Stateful, Identifiabl
userTaskCancellable = Task {
do {
await MainActor.run {
_ = backgroundStates.append(.updating)
_ = backgroundStates.insert(.updating)
}

try await updatePolicy(policy: policy)
Expand Down Expand Up @@ -181,7 +181,7 @@ final class ServerUserAdminViewModel: ViewModel, Eventful, Stateful, Identifiabl
userTaskCancellable = Task {
do {
await MainActor.run {
_ = backgroundStates.append(.updating)
_ = backgroundStates.insert(.updating)
}

try await updateConfiguration(configuration: configuration)
Expand Down Expand Up @@ -209,7 +209,7 @@ final class ServerUserAdminViewModel: ViewModel, Eventful, Stateful, Identifiabl
userTaskCancellable = Task {
do {
await MainActor.run {
_ = backgroundStates.append(.updating)
_ = backgroundStates.insert(.updating)
}

try await updateUsername(username: username)
Expand Down
14 changes: 7 additions & 7 deletions Shared/ViewModels/AdminDashboard/ServerUsersViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ final class ServerUsersViewModel: ViewModel, Eventful, Stateful, Identifiable {
// MARK: Published Values

@Published
final var backgroundStates: OrderedSet<BackgroundState> = []
var backgroundStates: Set<BackgroundState> = []

@Published
final var users: IdentifiedArrayOf<UserDto> = []
var users: IdentifiedArrayOf<UserDto> = []

@Published
final var state: State = .initial
var state: State = .initial

var events: AnyPublisher<Event, Never> {
eventSubject
Expand Down Expand Up @@ -88,7 +88,7 @@ final class ServerUsersViewModel: ViewModel, Eventful, Stateful, Identifiable {
switch action {
case let .refreshUser(userID):
userTask?.cancel()
backgroundStates.append(.gettingUsers)
backgroundStates.insert(.gettingUsers)

userTask = Task {
do {
Expand All @@ -114,7 +114,7 @@ final class ServerUsersViewModel: ViewModel, Eventful, Stateful, Identifiable {

case let .getUsers(isHidden, isDisabled):
userTask?.cancel()
backgroundStates.append(.gettingUsers)
backgroundStates.insert(.gettingUsers)

userTask = Task {
do {
Expand All @@ -140,7 +140,7 @@ final class ServerUsersViewModel: ViewModel, Eventful, Stateful, Identifiable {

case let .deleteUsers(ids):
userTask?.cancel()
backgroundStates.append(.deletingUsers)
backgroundStates.insert(.deletingUsers)

userTask = Task {
do {
Expand All @@ -167,7 +167,7 @@ final class ServerUsersViewModel: ViewModel, Eventful, Stateful, Identifiable {

case let .appendUser(user):
userTask?.cancel()
backgroundStates.append(.appendingUsers)
backgroundStates.insert(.appendingUsers)

userTask = Task {
do {
Expand Down
2 changes: 1 addition & 1 deletion Shared/ViewModels/ConnectToServerViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ final class ConnectToServerViewModel: ViewModel, Eventful, Stateful {
}

@Published
var backgroundStates: OrderedSet<BackgroundState> = []
var backgroundStates: Set<BackgroundState> = []

// no longer-found servers are not cleared, but not an issue
@Published
Expand Down
6 changes: 3 additions & 3 deletions Shared/ViewModels/FilterViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ final class FilterViewModel: ViewModel, Stateful {

/// ViewModel Background State(s)
@Published
var backgroundStates: OrderedSet<BackgroundState> = []
var backgroundStates: Set<BackgroundState> = []

/// ViewModel State
@Published
Expand Down Expand Up @@ -89,13 +89,13 @@ final class FilterViewModel: ViewModel, Stateful {
queryFiltersTask = Task {
do {
await MainActor.run {
_ = self.backgroundStates.append(.gettingQueryFilters)
_ = self.backgroundStates.insert(.gettingQueryFilters)
}

try await setQueryFilters()
} catch {
await MainActor.run {
_ = self.backgroundStates.append(.failedToGetQueryFilters)
_ = self.backgroundStates.insert(.failedToGetQueryFilters)
}
}

Expand Down
6 changes: 2 additions & 4 deletions Shared/ViewModels/HomeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@ final class HomeViewModel: ViewModel, Stateful {
var resumeItems: OrderedSet<BaseItemDto> = []

@Published
var backgroundStates: OrderedSet<BackgroundState> = []
@Published
var lastAction: Action? = nil
var backgroundStates: Set<BackgroundState> = []
@Published
var state: State = .initial

Expand Down Expand Up @@ -83,7 +81,7 @@ final class HomeViewModel: ViewModel, Stateful {
case .backgroundRefresh:

backgroundRefreshTask?.cancel()
backgroundStates.append(.refresh)
backgroundStates.insert(.refresh)

backgroundRefreshTask = Task { [weak self] in
do {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Combine
import Foundation
import JellyfinAPI

class DeleteItemViewModel: ViewModel, Stateful, Eventful {
final class DeleteItemViewModel: ViewModel, Stateful, Eventful {

// MARK: - Events

Expand All @@ -33,7 +33,7 @@ class DeleteItemViewModel: ViewModel, Stateful, Eventful {
}

@Published
final var state: State = .initial
var state: State = .initial

// MARK: - Published Item

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Get
import JellyfinAPI
import OrderedCollections

class IdentifyItemViewModel: ViewModel, Stateful, Eventful {
final class IdentifyItemViewModel: ViewModel, Stateful, Eventful {

// MARK: - Events

Expand Down
Loading