SHA256
feat(android): align native preview with measured PWA design
This commit is contained in:
1 parent
bd0e4cc4fb
commit
7ac9882307
30 files changed
+2793
-750
No files matched your search
@@ -0,0 +1,107 @@
|
||||
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
|
||||
Reference in new issue
Block a user