Files
WhiteNights_iOS/WhiteNights/Widgets/SightView.swift
15lu.akari a87a3d12ab big update
2025-08-26 23:37:39 +03:00

157 lines
6.2 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import SwiftUI
import AVKit
import NukeUI
struct SightView: View {
let sightId: Int
@StateObject private var viewModel = SightViewModel()
@EnvironmentObject private var appState: AppState
var body: some View {
VStack(alignment: .leading, spacing: 4) {
mediaSection
VStack(alignment: .leading, spacing: 8) {
// Заголовок статьи
Text(viewModel.selectedArticle?.isReviewArticle == true
? viewModel.sightName
: viewModel.articleHeading)
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.white)
.frame(maxWidth: .infinity,
alignment: viewModel.selectedArticle?.isReviewArticle == true ? .center : .leading)
.multilineTextAlignment(viewModel.selectedArticle?.isReviewArticle == true ? .center : .leading)
// Тело статьи
ScrollView {
if viewModel.selectedArticle?.isReviewArticle == true {
VStack {
Text(viewModel.articleBody)
.font(.system(size: 13))
.foregroundColor(.white)
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
} else {
Text(viewModel.articleBody)
.font(.system(size: 13))
.foregroundColor(.white)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
// Список статей
GeometryReader { geometry in
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 10) {
Spacer(minLength: 0)
ForEach(viewModel.allArticles) { article in
Text(localizedHeading(article))
.font(.system(size: 12))
.lineLimit(1)
.padding(.vertical, 6)
.padding(.horizontal, 6)
.frame(minWidth: 70)
.foregroundColor(.white)
.overlay(
Rectangle()
.frame(height: 2)
.foregroundColor(viewModel.selectedArticle == article ? Color.white : Color.clear),
alignment: .bottom
)
.onTapGesture {
viewModel.selectArticle(article)
}
}
Spacer(minLength: 0)
}
.frame(minWidth: geometry.size.width)
}
.scrollIndicators(.hidden)
}
.frame(height: 34)
.frame(maxWidth: .infinity)
}
.padding(.horizontal, 8)
.padding(.bottom, 4)
}
// MARK: - Initial load
.task(id: sightId) {
viewModel.setLanguage(appState.selectedLanguage)
await viewModel.loadInitialData(sightId: sightId)
}
// MARK: - Reload on language change
.onChange(of: appState.selectedLanguage) { newLang in
viewModel.setLanguage(newLang)
Task {
await viewModel.loadInitialData(sightId: sightId)
}
}
.blockStyle(cornerRadius: 25)
}
// MARK: - Медиа
@ViewBuilder
private var mediaSection: some View {
Group {
switch viewModel.mediaState {
case .loading:
ZStack {
Color.gray.opacity(0.3)
ProgressView()
.progressViewStyle(.circular)
.tint(.white)
}
.frame(maxWidth: .infinity)
.frame(height: 160)
.cornerRadius(24, corners: [.topLeft, .topRight])
.clipped()
case .image(let url):
LazyImage(url: url) { state in
if let image = state.image {
image.resizable().scaledToFit()
} else {
ZStack {
Color.gray.opacity(0.3)
ProgressView()
.progressViewStyle(.circular)
.tint(.white)
}
}
}
.cornerRadius(24, corners: [.topLeft, .topRight])
.clipped()
case .video(let player):
VideoPlayer(player: player)
.aspectRatio(16/9, contentMode: .fit)
.cornerRadius(24, corners: [.topLeft, .topRight])
.clipped()
case .error:
Image(systemName: "photo")
.resizable()
.aspectRatio(contentMode: .fit)
.foregroundColor(.gray)
.frame(maxWidth: .infinity, minHeight: 200)
.cornerRadius(24, corners: [.topLeft, .topRight])
.clipped()
}
}
.padding(4)
.frame(maxWidth: .infinity)
}
// MARK: - Локализованные заголовки статей
private func localizedHeading(_ article: Article) -> String {
if article.isReviewArticle == true {
switch appState.selectedLanguage {
case "ru": return "Обзор"
case "en": return "Review"
case "zh": return "奥布佐尔"
default: return "Обзор"
}
} else {
return article.heading
}
}
}