Files
WhiteNights_iOS/WhiteNights/Widgets/SightView.swift
15lu.akari 30d97f420e fix video
2025-08-27 00:19:05 +03:00

173 lines
7.0 KiB
Swift
Raw Permalink 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 {
@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 and reload on sight change
.task(id: appState.sightId) {
guard let currentSightId = appState.sightId else { return }
viewModel.setLanguage(appState.selectedLanguage)
await viewModel.loadInitialData(sightId: currentSightId)
}
// MARK: - Reload on language change
.onChange(of: appState.selectedLanguage) { newLang in
guard let currentSightId = appState.sightId else { return }
viewModel.setLanguage(newLang)
Task {
await viewModel.loadInitialData(sightId: currentSightId)
}
}
.blockStyle(cornerRadius: 25)
}
// MARK: - Медиа
@ViewBuilder
private var mediaSection: some View {
Group {
switch viewModel.mediaState {
case .loading:
ZStack {
Color.gray.opacity(0.3)
if let progress = viewModel.downloadProgress {
VStack {
ProgressView(value: progress)
.progressViewStyle(.linear)
.tint(.white)
Text("\(Int(progress * 100))%")
.foregroundColor(.white)
.font(.caption)
}
.padding()
} else {
ProgressView()
.progressViewStyle(.circular)
.tint(.white)
}
}
.frame(maxWidth: .infinity, minHeight: 200)
.cornerRadius(24, corners: [.topLeft, .topRight])
.clipped()
case .image(let url):
if let uiImage = UIImage(contentsOfFile: url.path) {
Image(uiImage: uiImage)
.resizable()
.scaledToFit()
.cornerRadius(24, corners: [.topLeft, .topRight])
.clipped()
} else {
Image(systemName: "photo")
.resizable()
.scaledToFit()
.foregroundColor(.gray)
.frame(maxWidth: .infinity, minHeight: 200)
.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
}
}
}