From f36498c5c23c39d1c941d13f59516a7ab2d07009 Mon Sep 17 00:00:00 2001 From: PranshulGG Date: Tue, 28 Jul 2026 16:30:47 +0530 Subject: [PATCH] weather alerts --- .../core/model/domain/alerts/Alert.kt | 15 ++++ .../core/model/weather/alerts/AlertResult.kt | 15 ++++ .../model/weather/alerts/AlertSeverity.kt | 30 ++++++++ .../sources/alerts/accu/AlertsAccuApi.kt | 59 +++++++++++++++ .../alerts/accu/AlertsAccuRepository.kt | 72 +++++++++++++++++++ .../alerts/accu/json/AlertsAccuJson.kt | 24 +++++++ .../utils/weather/cache/AlertCachePolicy.kt | 38 ++++++++++ .../data/local/dao/alerts/AlertsDao.kt | 31 ++++++++ .../data/local/entity/alerts/AlertEntity.kt | 34 +++++++++ .../local/mapper/alerts/AlertDomainMapper.kt | 31 ++++++++ .../alerts/sources/accu/AccuAlertsMapper.kt | 29 ++++++++ .../data/provider/AlertsRepositoryProvider.kt | 21 ++++++ .../data/repository/AlertRepository.kt | 12 ++++ .../feature/main/ui/AlertsSection.kt | 71 ++++++++++++++++++ 14 files changed, 482 insertions(+) create mode 100644 app/src/main/java/com/pranshulgg/weather_master_app/core/model/domain/alerts/Alert.kt create mode 100644 app/src/main/java/com/pranshulgg/weather_master_app/core/model/weather/alerts/AlertResult.kt create mode 100644 app/src/main/java/com/pranshulgg/weather_master_app/core/model/weather/alerts/AlertSeverity.kt create mode 100644 app/src/main/java/com/pranshulgg/weather_master_app/core/network/sources/alerts/accu/AlertsAccuApi.kt create mode 100644 app/src/main/java/com/pranshulgg/weather_master_app/core/network/sources/alerts/accu/AlertsAccuRepository.kt create mode 100644 app/src/main/java/com/pranshulgg/weather_master_app/core/network/sources/alerts/accu/json/AlertsAccuJson.kt create mode 100644 app/src/main/java/com/pranshulgg/weather_master_app/core/utils/weather/cache/AlertCachePolicy.kt create mode 100644 app/src/main/java/com/pranshulgg/weather_master_app/data/local/dao/alerts/AlertsDao.kt create mode 100644 app/src/main/java/com/pranshulgg/weather_master_app/data/local/entity/alerts/AlertEntity.kt create mode 100644 app/src/main/java/com/pranshulgg/weather_master_app/data/local/mapper/alerts/AlertDomainMapper.kt create mode 100644 app/src/main/java/com/pranshulgg/weather_master_app/data/local/mapper/alerts/sources/accu/AccuAlertsMapper.kt create mode 100644 app/src/main/java/com/pranshulgg/weather_master_app/data/provider/AlertsRepositoryProvider.kt create mode 100644 app/src/main/java/com/pranshulgg/weather_master_app/data/repository/AlertRepository.kt create mode 100644 app/src/main/java/com/pranshulgg/weather_master_app/feature/main/ui/AlertsSection.kt diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/model/domain/alerts/Alert.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/domain/alerts/Alert.kt new file mode 100644 index 000000000..433394a4d --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/domain/alerts/Alert.kt @@ -0,0 +1,15 @@ +package com.pranshulgg.weather_master_app.core.model.domain.alerts + +import com.pranshulgg.weather_master_app.core.model.weather.alerts.AlertSeverity + + +data class Alert( + val locationId: String, + val event: String, + val severity: AlertSeverity?, + val effective: Long, + val expires: Long, + val description: String, + val source: String?, + val lastUpdatedInMilli: Long +) \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/model/weather/alerts/AlertResult.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/weather/alerts/AlertResult.kt new file mode 100644 index 000000000..e4958f00d --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/weather/alerts/AlertResult.kt @@ -0,0 +1,15 @@ +package com.pranshulgg.weather_master_app.core.model.weather.alerts + +import com.pranshulgg.weather_master_app.core.model.domain.alerts.Alert + + +sealed class AlertResult { + data class Success(val alerts: List) : AlertResult() + data class Error(val exception: Exception, val cacheAlerts: List = emptyList()) : + AlertResult() +} + +enum class AlertResultType { + RETURN_CACHE, + ERROR +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/model/weather/alerts/AlertSeverity.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/weather/alerts/AlertSeverity.kt new file mode 100644 index 000000000..7203f0f83 --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/weather/alerts/AlertSeverity.kt @@ -0,0 +1,30 @@ +package com.pranshulgg.weather_master_app.core.model.weather.alerts + +import androidx.compose.ui.graphics.Color +import com.pranshulgg.weather_master_app.R + +enum class AlertSeverity( + val label: Int, + val color: Color +) { + CRITICAL( + R.string.alert_severity_critical, + Color(0xFFD32F2F) + ), + HIGH( + R.string.alert_severity_high, + Color(0xFFF57C00) + ), + MODERATE( + R.string.alert_severity_medium, + Color(0xFFFBC02D) + ), + LOW( + R.string.alert_severity_low, + Color(0xFF388E3C) + ), + UNKNOWN( + R.string.alert_severity_unknown, + Color(0xFF757575) + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/network/sources/alerts/accu/AlertsAccuApi.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/network/sources/alerts/accu/AlertsAccuApi.kt new file mode 100644 index 000000000..cbe21253e --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/network/sources/alerts/accu/AlertsAccuApi.kt @@ -0,0 +1,59 @@ +package com.pranshulgg.weather_master_app.core.network.sources.alerts.accu + +import com.pranshulgg.weather_master_app.BuildConfig +import com.pranshulgg.weather_master_app.core.network.sources.airquality.accu.json.AccuAqiForecastJson +import com.pranshulgg.weather_master_app.core.network.sources.airquality.accu.json.AccuAqiJson +import com.pranshulgg.weather_master_app.core.network.sources.alerts.accu.json.AlertsAccuJson +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import retrofit2.Response +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.Query +import java.util.concurrent.TimeUnit + + +interface AlertsAccuApi { + + @GET("alerts/v1/{locationKey}") + suspend fun fetchAlerts( + @Path("locationKey") locationKey: String, + @Query("details") details: Boolean = true + ): Response> + + + companion object { + const val BASE_URL = "https://api.accuweather.com/" + + fun create(): AlertsAccuApi { + + + val auth = Interceptor { chain -> + val original = chain.request() + + val newUrl = + original.url.newBuilder().addQueryParameter("apikey", BuildConfig.ACCU_KEY) + .build() + val request = original.newBuilder().url(newUrl).build() + + chain.proceed(request) + } + + val client = OkHttpClient.Builder() + .addInterceptor(auth) + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .build() + + return Retrofit.Builder() + .baseUrl(BASE_URL) + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(AlertsAccuApi::class.java) + } + } +} + diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/network/sources/alerts/accu/AlertsAccuRepository.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/network/sources/alerts/accu/AlertsAccuRepository.kt new file mode 100644 index 000000000..530a7f642 --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/network/sources/alerts/accu/AlertsAccuRepository.kt @@ -0,0 +1,72 @@ +package com.pranshulgg.weather_master_app.core.network.sources.alerts.accu + +import com.pranshulgg.weather_master_app.core.model.domain.AppException +import com.pranshulgg.weather_master_app.core.model.domain.location.Location +import com.pranshulgg.weather_master_app.core.model.weather.alerts.AlertResult +import com.pranshulgg.weather_master_app.core.model.weather.alerts.AlertResultType +import com.pranshulgg.weather_master_app.core.network.sources.weather.accu.AccuApi +import com.pranshulgg.weather_master_app.core.utils.weather.cache.shouldReturnAlertsCache +import com.pranshulgg.weather_master_app.data.local.dao.airquality.accu.AccuDao +import com.pranshulgg.weather_master_app.data.local.dao.alerts.AlertsDao +import com.pranshulgg.weather_master_app.data.local.entity.airquality.accu.AccuEntity +import com.pranshulgg.weather_master_app.data.local.mapper.alerts.sources.accu.toDomain +import com.pranshulgg.weather_master_app.data.local.mapper.alerts.toDomain +import com.pranshulgg.weather_master_app.data.local.mapper.alerts.toEntity +import com.pranshulgg.weather_master_app.data.local.mapper.weather.sources.accu.toDomain +import com.pranshulgg.weather_master_app.data.repository.AlertRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import javax.inject.Inject + + +class AlertsAccuRepository @Inject constructor( + private val api: AlertsAccuApi, + private val dao: AlertsDao, + private val accuDao: AccuDao, + private val accuApi: AccuApi, +) : AlertRepository { + override suspend fun getAlerts( + location: Location, + isManualRefresh: Boolean, + isForceRefresh: Boolean + ): AlertResult = withContext(Dispatchers.IO) { + + val cache = dao.getAlertsForLocation(location.id) + val shouldReturnCache = shouldReturnAlertsCache(cache, isManualRefresh, isForceRefresh) + + when (shouldReturnCache) { + AlertResultType.RETURN_CACHE -> return@withContext AlertResult.Success(cache.map { it!!.toDomain() }) + else -> {} + } + + return@withContext try { + val locationKey = accuDao.getCityKeyForLocation(location.id)?.toDomain()?.cityKey + ?: accuApi.getLocationKey("${location.latitude},${location.longitude}").body()?.key + ?: return@withContext AlertResult.Error(exception = AppException.Unknown()) + + val response = api.fetchAlerts(locationKey) + val body = response.body() + ?: return@withContext AlertResult.Error(exception = AppException.Unknown()) + + val domain = body.map { it.toDomain(location.id) } + + accuDao.insertCityKey( + AccuEntity( + locationId = location.id, + cityKey = locationKey + ) + ) + dao.insertAlerts( + domain.map { it.toEntity(location.id) }, + location.id + ) + + AlertResult.Success(domain) + + } catch (e: Exception) { + AlertResult.Error(exception = e, cacheAlerts = cache.map { it!!.toDomain() }) + } + + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/network/sources/alerts/accu/json/AlertsAccuJson.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/network/sources/alerts/accu/json/AlertsAccuJson.kt new file mode 100644 index 000000000..2611e840c --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/network/sources/alerts/accu/json/AlertsAccuJson.kt @@ -0,0 +1,24 @@ +package com.pranshulgg.weather_master_app.core.network.sources.alerts.accu.json + +import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Serializable + + +data class AlertsAccuJson( + @SerializedName("Description") val event: AlertsAccuEventJson, + @SerializedName("AlarmLevel") val alarmLevel: String?, + @SerializedName("Source") val source: String?, + @SerializedName("Area") val area: List, +) + +data class AlertsAccuEventJson( + @SerializedName("English") val english: String, + @SerializedName("Localized") val localized: String +) + +data class AlertsAccuAreaJson( + @SerializedName("EpochStartTime") val epochStartTimeSeconds: Long, + @SerializedName("EpochEndTime") val epochEndTimeSeconds: Long, + @SerializedName("Text") val text: String, + + ) \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/utils/weather/cache/AlertCachePolicy.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/utils/weather/cache/AlertCachePolicy.kt new file mode 100644 index 000000000..e07582ce5 --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/utils/weather/cache/AlertCachePolicy.kt @@ -0,0 +1,38 @@ +package com.pranshulgg.weather_master_app.core.utils.weather.cache + +import com.pranshulgg.weather_master_app.core.model.weather.airquality.AirQualityResultType +import com.pranshulgg.weather_master_app.core.model.weather.alerts.AlertResultType +import com.pranshulgg.weather_master_app.core.utils.weather.cache.CacheConfig.AUTO_REFRESH_MAX_MINUTES +import com.pranshulgg.weather_master_app.core.utils.weather.cache.CacheConfig.MANUAL_REFRESH_MINUTES +import com.pranshulgg.weather_master_app.data.local.entity.airquality.AirQualityWithRelations +import com.pranshulgg.weather_master_app.data.local.entity.alerts.AlertEntity +import java.util.concurrent.TimeUnit + + +fun shouldReturnAlertsCache( + cache: List, + isManualRefresh: Boolean, + isForceRefresh: Boolean +): AlertResultType { + + if (isForceRefresh) return AlertResultType.ERROR + + + if (cache.isEmpty() || cache[0] == null) { + return AlertResultType.ERROR + } + + + val cacheMilli = cache[0]!!.lastUpdatedInMilli + val ageMillis = System.currentTimeMillis() - cacheMilli + val ageMinutes = TimeUnit.MILLISECONDS.toMinutes(ageMillis) + + val tooEarly = isManualRefresh && ageMinutes < MANUAL_REFRESH_MINUTES + val maxAge = if (isManualRefresh) MANUAL_REFRESH_MINUTES else AUTO_REFRESH_MAX_MINUTES + + if (tooEarly) return AlertResultType.RETURN_CACHE + + val shouldReturnCache = ageMinutes < maxAge + + return if (shouldReturnCache) AlertResultType.RETURN_CACHE else AlertResultType.ERROR +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/dao/alerts/AlertsDao.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/dao/alerts/AlertsDao.kt new file mode 100644 index 000000000..7a207bf2b --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/dao/alerts/AlertsDao.kt @@ -0,0 +1,31 @@ +package com.pranshulgg.weather_master_app.data.local.dao.alerts + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Transaction +import com.pranshulgg.weather_master_app.data.local.entity.airquality.CurrentAirQualityEntity +import com.pranshulgg.weather_master_app.data.local.entity.alerts.AlertEntity + + +@Dao +interface AlertsDao { + + @Transaction + suspend fun insertAlerts(alerts: List, locationId: String) { + deleteAlertsForLocation(locationId) + insertAlertList(alerts) + } + + + @Insert(onConflict = OnConflictStrategy.Companion.REPLACE) + suspend fun insertAlertList(alerts: List) + + @Query("SELECT * FROM alerts WHERE locationId = :locationId") + suspend fun getAlertsForLocation(locationId: String): List + + @Query("DELETE FROM alerts WHERE locationId = :locationId") + suspend fun deleteAlertsForLocation(locationId: String) + +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/entity/alerts/AlertEntity.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/entity/alerts/AlertEntity.kt new file mode 100644 index 000000000..e72772fb0 --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/entity/alerts/AlertEntity.kt @@ -0,0 +1,34 @@ +package com.pranshulgg.weather_master_app.data.local.entity.alerts + +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Index +import androidx.room.PrimaryKey +import com.pranshulgg.weather_master_app.core.model.weather.alerts.AlertSeverity +import com.pranshulgg.weather_master_app.data.local.entity.location.WeatherLocationEntity + + +@Entity( + tableName = "alerts", + foreignKeys = [ + ForeignKey( + entity = WeatherLocationEntity::class, + parentColumns = ["id"], + childColumns = ["locationId"], + onDelete = ForeignKey.Companion.CASCADE + ) + ], + indices = [Index("locationId")], +) +data class AlertEntity( + @PrimaryKey(autoGenerate = true) val id: Long = 0, + val locationId: String, + + val event: String, + val severity: AlertSeverity?, + val effective: Long, + val expires: Long, + val description: String, + val source: String?, + val lastUpdatedInMilli: Long +) diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/mapper/alerts/AlertDomainMapper.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/mapper/alerts/AlertDomainMapper.kt new file mode 100644 index 000000000..23f4b7dd8 --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/mapper/alerts/AlertDomainMapper.kt @@ -0,0 +1,31 @@ +package com.pranshulgg.weather_master_app.data.local.mapper.alerts + +import com.pranshulgg.weather_master_app.core.model.domain.alerts.Alert +import com.pranshulgg.weather_master_app.data.local.entity.alerts.AlertEntity + + +fun Alert.toEntity(locationId: String): AlertEntity { + return AlertEntity( + locationId = locationId, + event = event, + severity = severity, + effective = effective, + expires = expires, + description = description, + source = source, + lastUpdatedInMilli = lastUpdatedInMilli + ) +} + +fun AlertEntity.toDomain(): Alert { + return Alert( + locationId = locationId, + event = event, + severity = severity, + effective = effective, + expires = expires, + description = description, + source = source, + lastUpdatedInMilli = lastUpdatedInMilli + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/mapper/alerts/sources/accu/AccuAlertsMapper.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/mapper/alerts/sources/accu/AccuAlertsMapper.kt new file mode 100644 index 000000000..4b90ed735 --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/mapper/alerts/sources/accu/AccuAlertsMapper.kt @@ -0,0 +1,29 @@ +package com.pranshulgg.weather_master_app.data.local.mapper.alerts.sources.accu + +import com.pranshulgg.weather_master_app.core.model.domain.alerts.Alert +import com.pranshulgg.weather_master_app.core.model.weather.alerts.AlertSeverity +import com.pranshulgg.weather_master_app.core.network.sources.alerts.accu.json.AlertsAccuJson +import com.pranshulgg.weather_master_app.core.utils.extensions.DateTimeExtensions.secondsToMilliseconds + + +fun AlertsAccuJson.toDomain(locationId: String): Alert { + return Alert( + locationId = locationId, + event = event.english, + severity = getSeverity(alarmLevel), + effective = area[0].epochStartTimeSeconds.secondsToMilliseconds(), + expires = area[0].epochEndTimeSeconds.secondsToMilliseconds(), + description = area[0].text, + source = source, + lastUpdatedInMilli = System.currentTimeMillis() + ) +} + +private fun getSeverity(severity: String?): AlertSeverity { + return when (severity) { + "Red" -> AlertSeverity.CRITICAL + "Orange" -> AlertSeverity.HIGH + "Yellow" -> AlertSeverity.MODERATE + else -> AlertSeverity.UNKNOWN + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/provider/AlertsRepositoryProvider.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/provider/AlertsRepositoryProvider.kt new file mode 100644 index 000000000..679336812 --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/provider/AlertsRepositoryProvider.kt @@ -0,0 +1,21 @@ +package com.pranshulgg.weather_master_app.data.provider + +import com.pranshulgg.weather_master_app.core.model.sources.AlertSource +import com.pranshulgg.weather_master_app.core.network.sources.alerts.accu.AlertsAccuRepository +import com.pranshulgg.weather_master_app.data.repository.AlertRepository +import javax.inject.Inject + + +class AlertsRepositoryProvider @Inject constructor( + private val alertsAccuRepository: AlertsAccuRepository +) { + + fun getRepository(source: AlertSource): AlertRepository? { + return when (source) { + AlertSource.ACCU_WEATHER -> alertsAccuRepository + AlertSource.WEATHER_API -> alertsAccuRepository + AlertSource.NONE -> null + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/AlertRepository.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/AlertRepository.kt new file mode 100644 index 000000000..f63897963 --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/AlertRepository.kt @@ -0,0 +1,12 @@ +package com.pranshulgg.weather_master_app.data.repository + +import com.pranshulgg.weather_master_app.core.model.domain.location.Location +import com.pranshulgg.weather_master_app.core.model.weather.alerts.AlertResult + +interface AlertRepository { + suspend fun getAlerts( + location: Location, + isManualRefresh: Boolean = false, + isForceRefresh: Boolean + ): AlertResult +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/ui/AlertsSection.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/ui/AlertsSection.kt new file mode 100644 index 000000000..224c65618 --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/main/ui/AlertsSection.kt @@ -0,0 +1,71 @@ +package com.pranshulgg.weather_master_app.feature.main.ui + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ListItem +import androidx.compose.material3.ListItemDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.pranshulgg.weather_master_app.R +import com.pranshulgg.weather_master_app.core.model.domain.alerts.Alert +import com.pranshulgg.weather_master_app.core.model.weather.alerts.AlertSeverity +import com.pranshulgg.weather_master_app.core.prefs.AppPrefs +import com.pranshulgg.weather_master_app.core.prefs.AppPrefsState +import com.pranshulgg.weather_master_app.core.ui.components.SettingSection +import com.pranshulgg.weather_master_app.core.ui.components.Symbol +import com.pranshulgg.weather_master_app.core.ui.theme.ShadowElevation +import com.pranshulgg.weather_master_app.core.utils.formatters.safeZoneId +import java.time.Instant +import java.time.ZoneId +import java.time.format.DateTimeFormatter + +@Composable +fun AlertsSection(alerts: List, prefs: AppPrefsState, zoneId: String) { + + val pattern = if (prefs.is24HrTimeFormat) "MMM dd, HH:mm" else "MMM dd, hh:mm a" + + + val formatter: (Long) -> String = { + val formatter = DateTimeFormatter.ofPattern(pattern) + val instant = Instant.ofEpochMilli(it) + val dateTime = instant.atZone(safeZoneId(zoneId)).toLocalDateTime() + formatter.format(dateTime) + } + + Surface( + color = MaterialTheme.colorScheme.surface, + shape = MaterialTheme.shapes.extraLarge, + shadowElevation = ShadowElevation.level2 + ) { + Column( + modifier = Modifier + .fillMaxWidth() + ) { + alerts.forEach { + ListItem( + colors = ListItemDefaults.colors( + containerColor = Color.Transparent + ), + leadingContent = { + Symbol( + R.drawable.warning_24px, + color = it.severity?.color ?: AlertSeverity.UNKNOWN.color, + size = 32.dp + ) + }, + headlineContent = { Text(it.event) }, + supportingContent = { + Text("${formatter(it.effective)} • ${formatter(it.expires)}") + } + ) + } + } + + } +} \ No newline at end of file