feat(event): ID 기반 이벤트 상세 API를 연동한다
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user