SHA256
108 lines
4.1 KiB
Kotlin
108 lines
4.1 KiB
Kotlin
package community.yuvomi.mobile
|
|
|
|
import java.time.LocalDate
|
|
import java.time.ZoneId
|
|
import java.time.ZonedDateTime
|
|
import java.time.format.DateTimeFormatter
|
|
import java.util.Locale
|
|
import org.json.JSONArray
|
|
import org.json.JSONObject
|
|
|
|
val pwaSupportedWidgets = listOf("tasks", "calendar", "shopping", "notes")
|
|
|
|
/** Presentation only. Household business rules and permissions remain server-owned. */
|
|
fun pwaGreeting(displayName: String, hour: Int): String {
|
|
val first = displayName.trim().split(Regex("\\s+")).firstOrNull().orEmpty()
|
|
val greeting = when (hour) {
|
|
in 5..11 -> "Guten Morgen"
|
|
in 12..17 -> "Guten Tag"
|
|
else -> "Guten Abend"
|
|
}
|
|
return if (first.isBlank()) greeting else "$greeting, $first"
|
|
}
|
|
|
|
fun pwaDueLabel(value: String, today: LocalDate = LocalDate.now()): String {
|
|
if (value.isBlank()) return ""
|
|
val date = runCatching { LocalDate.parse(value.take(10)) }.getOrNull() ?: return value
|
|
return when (date) {
|
|
today -> "Heute fällig"
|
|
today.plusDays(1) -> "Morgen fällig"
|
|
today.minusDays(1) -> "Gestern fällig"
|
|
else -> date.format(DateTimeFormatter.ofPattern("d. MMM", Locale.GERMAN))
|
|
}
|
|
}
|
|
|
|
fun pwaPriority(value: String) = when (value) {
|
|
"low" -> "Niedrig"
|
|
"medium" -> "Mittel"
|
|
"high" -> "Hoch"
|
|
"urgent" -> "Dringend"
|
|
else -> ""
|
|
}
|
|
|
|
fun pwaGroup(value: String) = value.takeUnless { it.isBlank() || it in setOf("general", "other", "misc") } ?: "Sonstiges"
|
|
|
|
fun NativeState.householdNow(instant: java.time.Instant = java.time.Instant.now()): ZonedDateTime {
|
|
val zone = runCatching { ZoneId.of(preferences.text("timezone_effective").ifBlank { preferences.text("timezone") }) }
|
|
.getOrDefault(ZoneId.systemDefault())
|
|
return instant.atZone(zone)
|
|
}
|
|
|
|
fun NativeState.moduleVisible(module: String): Boolean = access(module) != "none" &&
|
|
listOf("disabled_modules", "hidden_modules").none { key ->
|
|
val values = preferences.optJSONArray(key) ?: JSONArray()
|
|
(0 until values.length()).any { values.optString(it) == module }
|
|
}
|
|
|
|
fun NativeState.dashboardTiles(): List<String> {
|
|
val config = preferences.rows("dashboard_widgets")
|
|
return (
|
|
if (preferences.isNull("dashboard_widgets")) {
|
|
pwaSupportedWidgets
|
|
} else {
|
|
config.filter { it.optBoolean("visible", true) }.map { it.text("id") }
|
|
}
|
|
)
|
|
.filter { it in pwaSupportedWidgets && moduleVisible(it) && widget(it) }.distinct()
|
|
}
|
|
|
|
fun pwaWidgetVisibility(preferences: JSONObject, visible: Set<String>, editable: Set<String> = pwaSupportedWidgets.toSet()): JSONArray {
|
|
val existing = preferences.rows("dashboard_widgets")
|
|
val rows = if (preferences.isNull("dashboard_widgets")) {
|
|
pwaSupportedWidgets.mapIndexed { index, id ->
|
|
JSONObject().put("id", id).put("visible", true).put("order", index).put("size", "1x1")
|
|
}
|
|
} else {
|
|
existing
|
|
}
|
|
val result = JSONArray(
|
|
rows.map { row ->
|
|
JSONObject(row.toString()).apply {
|
|
if (text("id") in editable && text("id") in pwaSupportedWidgets) put("visible", text("id") in visible)
|
|
}
|
|
},
|
|
)
|
|
val configured = rows.map { it.text("id") }.toSet()
|
|
pwaSupportedWidgets.filter { it in visible && it in editable && it !in configured }.forEach { id ->
|
|
result.put(JSONObject().put("id", id).put("visible", true).put("order", result.length()).put("size", "1x1"))
|
|
}
|
|
return result
|
|
}
|
|
|
|
fun NativeState.nativeTabs(): List<NativePage> {
|
|
val order = preferences.optJSONArray("mobile_nav_order")?.let { array ->
|
|
(0 until array.length()).map { array.optString(it) }
|
|
}.orEmpty()
|
|
val pages = (order + listOf("tasks", "kitchen", "notes")).mapNotNull {
|
|
when (it) {
|
|
"tasks" -> NativePage.TASKS
|
|
"shopping", "kitchen" -> NativePage.SHOPPING
|
|
"notes" -> NativePage.NOTES
|
|
else -> null // Unimplemented modules are never fake navigation targets.
|
|
}
|
|
}.distinct().filter { moduleVisible(it.name.lowercase()) }
|
|
return listOf(NativePage.HOME) + pages + NativePage.MORE
|
|
}
|
|
|
|
fun NativePage.navigationLabel() = if (this == NativePage.SHOPPING) "Küche" else title
|