115 lines
3.4 KiB
Swift
115 lines
3.4 KiB
Swift
import Foundation
|
|
import Combine
|
|
|
|
final class EventDetailViewModel: ObservableObject {
|
|
enum State: Equatable {
|
|
case idle
|
|
case loading
|
|
case loaded(EventItem)
|
|
case unavailable
|
|
case error
|
|
}
|
|
|
|
@Published private(set) var state: State = .idle
|
|
@Published var isLoading = false
|
|
@Published var errorMessage = ""
|
|
@Published var isShowPopup = false
|
|
|
|
private let eventId: Int
|
|
private let repository = EventRepository()
|
|
private var subscription: AnyCancellable?
|
|
private var activeRequestID: UUID?
|
|
private var isUnavailableDismissalPending = false
|
|
|
|
init(eventId: Int) {
|
|
self.eventId = eventId
|
|
}
|
|
|
|
var participationURL: URL? {
|
|
guard case .loaded(let event) = state,
|
|
let link = event.link,
|
|
let url = URL(string: link),
|
|
let scheme = url.scheme?.lowercased(),
|
|
["http", "https"].contains(scheme),
|
|
let host = url.host, !host.isEmpty else { return nil }
|
|
return url
|
|
}
|
|
|
|
func load() {
|
|
guard state == .idle else { return }
|
|
guard eventId > 0 else {
|
|
showUnavailable()
|
|
return
|
|
}
|
|
|
|
isLoading = true
|
|
state = .loading
|
|
let requestID = UUID()
|
|
activeRequestID = requestID
|
|
|
|
subscription = repository.getEventDetail(id: eventId)
|
|
.receive(on: DispatchQueue.main)
|
|
.sink { [weak self] _ in
|
|
guard let self, self.activeRequestID == requestID else { return }
|
|
self.activeRequestID = nil
|
|
self.showError()
|
|
} receiveValue: { [weak self] response in
|
|
guard let self, self.activeRequestID == requestID else { return }
|
|
self.activeRequestID = nil
|
|
self.isLoading = false
|
|
|
|
guard response.statusCode == 200 else {
|
|
self.showError()
|
|
return
|
|
}
|
|
|
|
do {
|
|
let decoder = JSONDecoder()
|
|
let envelope = try decoder.decode(ApiResponseWithoutData.self, from: response.data)
|
|
guard envelope.success else {
|
|
self.showUnavailable()
|
|
return
|
|
}
|
|
let decoded = try decoder.decode(ApiResponse<EventItem>.self, from: response.data)
|
|
guard let event = decoded.data, event.id > 0, event.id == self.eventId else {
|
|
self.showError()
|
|
return
|
|
}
|
|
self.state = .loaded(event)
|
|
} catch {
|
|
self.showError()
|
|
}
|
|
}
|
|
}
|
|
|
|
func cancel() {
|
|
activeRequestID = nil
|
|
isUnavailableDismissalPending = false
|
|
subscription?.cancel()
|
|
subscription = nil
|
|
isLoading = false
|
|
if state == .loading {
|
|
state = .idle
|
|
}
|
|
}
|
|
|
|
func takeUnavailableDismissal() -> Bool {
|
|
guard isUnavailableDismissalPending else { return false }
|
|
isUnavailableDismissalPending = false
|
|
return true
|
|
}
|
|
|
|
private func showUnavailable() {
|
|
isLoading = false
|
|
isUnavailableDismissalPending = true
|
|
state = .unavailable
|
|
}
|
|
|
|
private func showError() {
|
|
isLoading = false
|
|
errorMessage = I18n.Common.commonError
|
|
isShowPopup = true
|
|
state = .error
|
|
}
|
|
}
|