jellyflood/Shared/ViewModels/QuickConnectAuthorizeViewMo...

97 lines
2.4 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 Combine
import Foundation
import JellyfinAPI
final class QuickConnectAuthorizeViewModel: ViewModel, Eventful, Stateful {
// MARK: Event
enum Event {
case authorized
case error(JellyfinAPIError)
}
// MARK: Action
enum Action: Equatable {
case authorize(code: String)
case cancel
}
// MARK: State
enum State: Hashable {
case authorizing
case initial
}
@Published
var state: State = .initial
var events: AnyPublisher<Event, Never> {
eventSubject
.receive(on: RunLoop.main)
.eraseToAnyPublisher()
}
private var authorizeTask: AnyCancellable?
private var eventSubject: PassthroughSubject<Event, Never> = .init()
let user: UserDto
init(user: UserDto) {
self.user = user
}
func respond(to action: Action) -> State {
switch action {
case let .authorize(code):
authorizeTask = Task {
try? await Task.sleep(nanoseconds: 10_000_000_000)
do {
try await authorize(code: code, userID: user.id)
await MainActor.run {
self.eventSubject.send(.authorized)
self.state = .initial
}
} catch {
await MainActor.run {
self.eventSubject.send(.error(.init(error.localizedDescription)))
self.state = .initial
}
}
}
.asAnyCancellable()
return .authorizing
case .cancel:
authorizeTask?.cancel()
return .initial
}
}
private func authorize(code: String, userID: String? = nil) async throws {
let request = Paths.authorizeQuickConnect(code: code, userID: userID)
let response = try await userSession.client.send(request)
let decoder = JSONDecoder()
let isAuthorized = (try? decoder.decode(Bool.self, from: response.value)) ?? false
if !isAuthorized {
throw JellyfinAPIError("Authorization unsuccessful")
}
}
}