99 lines
3.7 KiB
Swift
99 lines
3.7 KiB
Swift
import Foundation
|
|
import AVKit
|
|
import Combine
|
|
|
|
@MainActor
|
|
class SightViewModel: ObservableObject {
|
|
@Published var sightName: String = "Загрузка..."
|
|
@Published var allArticles: [Article] = []
|
|
@Published var selectedArticle: Article?
|
|
@Published var articleHeading: String = ""
|
|
@Published var articleBody: String = ""
|
|
@Published var mediaState: MediaState = .loading
|
|
|
|
private var sightModel: SightModel?
|
|
|
|
enum MediaState {
|
|
case loading
|
|
case image(URL)
|
|
case video(AVPlayer)
|
|
case error
|
|
}
|
|
|
|
func loadInitialData(sightId: Int) async {
|
|
do {
|
|
async let sightModelTask = fetchJSON(from: "https://white-nights.krbl.ru/services/content/sight/\(sightId)", type: SightModel.self)
|
|
async let articlesTask = fetchJSON(from: "https://white-nights.krbl.ru/services/content/sight/\(sightId)/article", type: [Article].self)
|
|
|
|
let (fetchedSightModel, fetchedArticles) = try await (sightModelTask, articlesTask)
|
|
|
|
self.sightModel = fetchedSightModel
|
|
self.sightName = fetchedSightModel.name
|
|
|
|
let reviewArticle = Article(id: -1, body: "", heading: "Обзор", isReviewArticle: true)
|
|
self.allArticles = [reviewArticle] + fetchedArticles
|
|
|
|
selectArticle(reviewArticle)
|
|
|
|
} catch {
|
|
print("Ошибка начальной загрузки данных: \(error)")
|
|
self.mediaState = .error
|
|
}
|
|
}
|
|
|
|
func selectArticle(_ article: Article) {
|
|
guard selectedArticle != article else { return }
|
|
|
|
self.selectedArticle = article
|
|
self.articleHeading = article.heading
|
|
self.articleBody = article.body
|
|
|
|
Task {
|
|
await updateMedia(for: article)
|
|
}
|
|
}
|
|
|
|
private func updateMedia(for article: Article) async {
|
|
self.mediaState = .loading
|
|
|
|
if article.isReviewArticle == true {
|
|
guard let sight = sightModel else {
|
|
self.mediaState = .error
|
|
return
|
|
}
|
|
|
|
if let videoPreviewId = sight.video_preview,
|
|
let url = URL(string: "https://white-nights.krbl.ru/services/content/media/\(videoPreviewId)/download") {
|
|
let player = AVPlayer(url: url)
|
|
player.play()
|
|
self.mediaState = .video(player)
|
|
} else if let url = URL(string: "https://white-nights.krbl.ru/services/content/media/\(sight.preview_media)/download") {
|
|
self.mediaState = .image(url)
|
|
} else {
|
|
self.mediaState = .error
|
|
}
|
|
} else {
|
|
do {
|
|
let mediaItems = try await fetchJSON(from: "https://white-nights.krbl.ru/services/content/article/\(article.id)/media", type: [ArticleMedia].self)
|
|
if let firstMedia = mediaItems.first,
|
|
let url = URL(string: "https://white-nights.krbl.ru/services/content/media/\(firstMedia.id)/download") {
|
|
self.mediaState = .image(url)
|
|
} else {
|
|
self.mediaState = .error
|
|
}
|
|
} catch {
|
|
print("Ошибка загрузки медиа для статьи '\(article.heading)': \(error)")
|
|
self.mediaState = .error
|
|
}
|
|
}
|
|
}
|
|
|
|
private func fetchJSON<T: Decodable>(from urlString: String, type: T.Type) async throws -> T {
|
|
guard let url = URL(string: urlString) else {
|
|
throw URLError(.badURL)
|
|
}
|
|
let (data, _) = try await URLSession.shared.data(from: url)
|
|
return try JSONDecoder().decode(T.self, from: data)
|
|
}
|
|
}
|