big update
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
import SwiftUI
|
||||
|
||||
private let WEATHER_STATUS_MAP: [String: String] = [
|
||||
private let WEATHER_STATUS_MAP_RU: [String: String] = [
|
||||
"Rain": "дождливо",
|
||||
"Clouds": "облачно",
|
||||
"Clear": "солнечно",
|
||||
@ -10,52 +10,81 @@ private let WEATHER_STATUS_MAP: [String: String] = [
|
||||
"Fog": "туман"
|
||||
]
|
||||
|
||||
private let WEATHER_STATUS_MAP_ZH: [String: String] = [
|
||||
"Rain": "下雨",
|
||||
"Clouds": "多云",
|
||||
"Clear": "晴朗",
|
||||
"Thunderstorm": "雷雨",
|
||||
"Snow": "下雪",
|
||||
"Drizzle": "毛毛雨",
|
||||
"Fog": "雾"
|
||||
]
|
||||
|
||||
private let WEATHER_STATUS_MAP_EN: [String: String] = [
|
||||
"Rain": "Rain",
|
||||
"Clouds": "Cloudy",
|
||||
"Clear": "Sunny",
|
||||
"Thunderstorm": "Thunderstorm",
|
||||
"Snow": "Snow",
|
||||
"Drizzle": "Drizzle",
|
||||
"Fog": "Fog"
|
||||
]
|
||||
|
||||
struct FormattedWeather {
|
||||
let temperature: Int
|
||||
let status: String
|
||||
let statusCode: String
|
||||
let precipitation: Int?
|
||||
let windSpeed: Double?
|
||||
let dayOfWeek: String?
|
||||
var dayOfWeek: String?
|
||||
let originalDate: Date? // добавлено для пересчета дня недели
|
||||
|
||||
func localizedStatus(language: String) -> String {
|
||||
switch language {
|
||||
case "ru": return WEATHER_STATUS_MAP_RU[statusCode] ?? statusCode
|
||||
case "zh": return WEATHER_STATUS_MAP_ZH[statusCode] ?? statusCode
|
||||
default: return WEATHER_STATUS_MAP_EN[statusCode] ?? statusCode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct WeatherView: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
|
||||
@State private var todayWeather: FormattedWeather?
|
||||
@State private var forecast: [FormattedWeather] = []
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .center, spacing: 4) {
|
||||
if let today = todayWeather {
|
||||
// ЛЕВЫЙ СТОЛБЕЦ: Иконка, температура и статус
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Image(getWeatherIconName(for: today.status))
|
||||
VStack(alignment: .center, spacing: 2) {
|
||||
Image(getWeatherIconName(for: today.localizedStatus(language: appState.selectedLanguage)))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 36, height: 36)
|
||||
.padding(.bottom, 5)
|
||||
|
||||
|
||||
Text("\(today.temperature)°")
|
||||
.font(.system(size: 30, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
|
||||
Text(today.status)
|
||||
Text(today.localizedStatus(language: appState.selectedLanguage))
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.white)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
|
||||
// ПРАВЫЙ СТОЛБЕЦ: Прогноз с иконками
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
// Прогноз на 3 дня
|
||||
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
ForEach(forecast.prefix(3).indices, id: \.self) { index in
|
||||
let day = forecast[index]
|
||||
HStack(spacing: 5) {
|
||||
Image(getWeatherIconName(for: day.status))
|
||||
HStack(spacing: 4) {
|
||||
Image(getWeatherIconName(for: day.localizedStatus(language: appState.selectedLanguage)))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 14, height: 14)
|
||||
|
||||
if let dayName = day.dayOfWeek {
|
||||
Text(dayName)
|
||||
.font(.system(size: 12))
|
||||
if let date = day.originalDate {
|
||||
Text(getDayOfWeek(from: date))
|
||||
.font(.system(size: 10))
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
|
||||
@ -66,9 +95,12 @@ struct WeatherView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// Влажность и скорость ветра
|
||||
Divider()
|
||||
.background(Color.white.opacity(0.8))
|
||||
.padding(.vertical, 1)
|
||||
|
||||
if let precipitation = today.precipitation {
|
||||
HStack(spacing: 5) {
|
||||
HStack(spacing: 4) {
|
||||
Image("det_humidity")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
@ -78,20 +110,25 @@ struct WeatherView: View {
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
}
|
||||
|
||||
if let windSpeed = today.windSpeed {
|
||||
HStack(spacing: 5) {
|
||||
Image("det_wind_speed")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 14, height: 14)
|
||||
Text("\(Int(windSpeed)) м/с")
|
||||
Text(appState.selectedLanguage == "ru" ? "\(Int(windSpeed)) м/с" :
|
||||
appState.selectedLanguage == "zh" ? "\(Int(windSpeed)) 米/秒" :
|
||||
"\(Int(windSpeed)) m/s")
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Text("Загрузка погоды...")
|
||||
Text(appState.selectedLanguage == "ru" ? "Загрузка погоды..." :
|
||||
appState.selectedLanguage == "zh" ? "正在加载天气..." :
|
||||
"Loading weather...")
|
||||
.foregroundColor(.white)
|
||||
.padding()
|
||||
}
|
||||
@ -117,13 +154,16 @@ struct WeatherView: View {
|
||||
.task {
|
||||
await fetchAndFormatWeather()
|
||||
}
|
||||
.onChange(of: appState.selectedLanguage) { _ in
|
||||
// при смене языка день недели пересчитывается динамически в View
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchAndFormatWeather() async {
|
||||
let lat = 59.938784
|
||||
let lng = 30.314997
|
||||
|
||||
guard let url = URL(string: "https://white-nights.krbl.ru/services/weather") else { return }
|
||||
guard let url = URL(string: "https://white-nights.krbl.ru/services/weather?lang=\(appState.selectedLanguage)") else { return }
|
||||
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
@ -136,14 +176,8 @@ struct WeatherView: View {
|
||||
do {
|
||||
request.httpBody = try JSONSerialization.data(withJSONObject: parameters)
|
||||
let (data, _) = try await URLSession.shared.data(for: request)
|
||||
|
||||
// 💡 Отладка: Печатаем полученные данные для проверки
|
||||
// print("Received Data (String): \(String(data: data, encoding: .utf8) ?? "N/A")")
|
||||
|
||||
let weatherResponse = try JSONDecoder().decode(WeatherResponse.self, from: data)
|
||||
|
||||
formatWeatherData(data: weatherResponse)
|
||||
|
||||
} catch {
|
||||
print("Ошибка загрузки погоды: \(error)")
|
||||
}
|
||||
@ -152,103 +186,85 @@ struct WeatherView: View {
|
||||
private func formatWeatherData(data: WeatherResponse) {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
|
||||
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) // UTC
|
||||
dateFormatter.locale = Locale(identifier: "en_US_POSIX") // Добавление POSIX-локали для надежного парсинга
|
||||
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
|
||||
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
|
||||
|
||||
var calendar = Calendar.current
|
||||
calendar.timeZone = TimeZone(secondsFromGMT: 0)! // UTC
|
||||
calendar.timeZone = TimeZone(secondsFromGMT: 0)!
|
||||
|
||||
// 1. Фильтруем все записи, которые соответствуют 12:00 UTC
|
||||
let middayForecast = data.forecast.filter { item in
|
||||
guard let date = dateFormatter.date(from: item.date) else {
|
||||
// 💡 Отладка: если здесь вы увидите сообщения, значит, формат даты API не совпадает с форматом dateFormatter
|
||||
// print("Парсинг даты провален для: \(item.date)")
|
||||
return false
|
||||
}
|
||||
guard let date = dateFormatter.date(from: item.date) else { return false }
|
||||
return calendar.component(.hour, from: date) == 12
|
||||
}
|
||||
|
||||
// 💡 Отладка: Проверяем, сколько записей найдено
|
||||
// print("Найдено записей на 12:00 UTC: \(middayForecast.count)")
|
||||
|
||||
// Сегодня
|
||||
|
||||
if let today = data.currentWeather {
|
||||
self.todayWeather = FormattedWeather(
|
||||
todayWeather = FormattedWeather(
|
||||
temperature: Int(today.temperatureCelsius.rounded()),
|
||||
status: WEATHER_STATUS_MAP[today.description] ?? today.description,
|
||||
statusCode: today.description,
|
||||
precipitation: today.humidity,
|
||||
windSpeed: today.windSpeed,
|
||||
dayOfWeek: nil
|
||||
dayOfWeek: nil,
|
||||
originalDate: nil
|
||||
)
|
||||
}
|
||||
|
||||
// 🚀 ИСПРАВЛЕНИЕ: Прогноз на 3 дня. Берем первые 3 доступные записи с 12:00.
|
||||
var formattedForecast: [FormattedWeather] = []
|
||||
|
||||
// Перебираем первые 3 записи с 12:00 UTC
|
||||
|
||||
for item in middayForecast.prefix(3) {
|
||||
guard let date = dateFormatter.date(from: item.date) else { continue }
|
||||
|
||||
let averageTemp = (item.minTemperatureCelsius + item.maxTemperatureCelsius) / 2
|
||||
let dayOfWeekString = getDayOfWeek(from: date)
|
||||
|
||||
// 💡 Отладка: Печатаем найденный прогноз
|
||||
// print("Прогноз найден: \(dayOfWeekString) - \(Int(averageTemp.rounded()))°")
|
||||
|
||||
formattedForecast.append(FormattedWeather(
|
||||
temperature: Int(averageTemp.rounded()),
|
||||
status: WEATHER_STATUS_MAP[item.description] ?? item.description,
|
||||
statusCode: item.description,
|
||||
precipitation: item.humidity,
|
||||
windSpeed: item.windSpeed,
|
||||
dayOfWeek: dayOfWeekString
|
||||
dayOfWeek: getDayOfWeek(from: date),
|
||||
originalDate: date
|
||||
))
|
||||
}
|
||||
|
||||
// Если данных меньше 3, заполняем оставшиеся места нулями (N/A)
|
||||
let daysToFill = 3 - formattedForecast.count
|
||||
if daysToFill > 0 {
|
||||
let baseDate = Date()
|
||||
|
||||
for i in 1...daysToFill {
|
||||
guard let nextDayForNA = Calendar.current.date(byAdding: .day, value: formattedForecast.count + i, to: baseDate) else { continue }
|
||||
|
||||
let dayOfWeekString = getDayOfWeek(from: nextDayForNA)
|
||||
|
||||
guard let nextDay = Calendar.current.date(byAdding: .day, value: formattedForecast.count + i, to: baseDate) else { continue }
|
||||
formattedForecast.append(FormattedWeather(
|
||||
temperature: 0,
|
||||
status: "N/A",
|
||||
statusCode: "N/A",
|
||||
precipitation: nil,
|
||||
windSpeed: nil,
|
||||
dayOfWeek: dayOfWeekString
|
||||
dayOfWeek: getDayOfWeek(from: nextDay),
|
||||
originalDate: nextDay
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
self.forecast = formattedForecast
|
||||
}
|
||||
|
||||
private func getDayOfWeek(from date: Date) -> String {
|
||||
let formatter = DateFormatter()
|
||||
formatter.locale = Locale(identifier: "ru_RU")
|
||||
formatter.locale = appState.selectedLanguage == "ru" ? Locale(identifier: "ru_RU") :
|
||||
appState.selectedLanguage == "zh" ? Locale(identifier: "zh_CN") :
|
||||
Locale(identifier: "en_US")
|
||||
formatter.dateFormat = "E"
|
||||
return formatter.string(from: date).capitalized
|
||||
}
|
||||
|
||||
|
||||
private func getWeatherIconName(for status: String) -> String {
|
||||
let normalizedStatus = status.lowercased()
|
||||
|
||||
switch normalizedStatus {
|
||||
case "солнечно":
|
||||
case "солнечно", "晴朗", "sunny":
|
||||
return "cond_sunny"
|
||||
case "облачно":
|
||||
case "облачно", "多云", "cloudy":
|
||||
return "cond_cloudy"
|
||||
case "дождливо", "мелкий дождь":
|
||||
case "дождливо", "мелкий дождь", "下雨", "毛毛雨", "rainy", "drizzle":
|
||||
return "cond_rainy"
|
||||
case "снег":
|
||||
case "снег", "下雪", "snowy":
|
||||
return "cond_snowy"
|
||||
case "гроза":
|
||||
case "гроза", "雷雨", "thunderstorm":
|
||||
return "cond_thunder"
|
||||
case "туман":
|
||||
case "туман", "雾", "fog":
|
||||
return "det_humidity"
|
||||
default:
|
||||
return "cond_sunny"
|
||||
|
Reference in New Issue
Block a user