feat: HA/Google edit, full-page event detail, KW+zigzag divider, HSV picker, splash

- Home Assistant & Google events now editable/deletable (server PUT/DELETE
  endpoints wired; saveEvent had no HA/Google update branch -> created dupes)
- Event detail is now a full screen (Scaffold) instead of a bottom sheet:
  long notes scroll, action buttons no longer hidden behind the system nav bar;
  distinct calendar vs notes icons
- Month view: calendar-week number on Mondays, month-boundary divider with
  zigzag step (month_divider_color), month label in month_label_color,
  text/line contrast applied
- Compact top bar with smaller title; prev/next chevrons present in month view
  and scroll one month at a time
- Real HSV colour picker (SV field + hue slider + hex) for primary, accent,
  today, month-divider and month-label colours
- Black status/navigation bars; portrait-locked; crisp custom splash with app
  name, high-res icon and © Scarriffle

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Guido Schmit
2026-05-31 14:19:56 +02:00
parent 608580fc7e
commit b8eb6597ec
18 changed files with 738 additions and 315 deletions

View File

@@ -305,31 +305,56 @@ class CalendarRepository @Inject constructor(
calendarDbId: Int, title: String, start: Instant, end: Instant,
isAllDay: Boolean, location: String, description: String,
) = guarded {
api.createGoogleEvent(
jsonBody(
"calendar_db_id" to calendarDbId, "title" to title,
"start" to Dates.format(start, isAllDay), "end" to Dates.format(end, isAllDay),
"allDay" to isAllDay, "location" to location, "description" to description,
)
).ensureSuccess()
api.createGoogleEvent(simpleEventBody("calendar_db_id", calendarDbId, title, start, end, isAllDay, location, description))
.ensureSuccess()
}
suspend fun updateGoogleEvent(
gcalDbId: Int, eventId: String, title: String, start: Instant, end: Instant,
isAllDay: Boolean, location: String, description: String,
) = guarded {
api.updateGoogleEvent(gcalDbId, eventId, simpleEventBody(null, null, title, start, end, isAllDay, location, description))
.ensureSuccess()
}
suspend fun deleteGoogleEvent(gcalDbId: Int, eventId: String) =
guarded { api.deleteGoogleEvent(gcalDbId, eventId).ensureSuccess() }
suspend fun createHAEvent(
calendarId: Int, title: String, start: Instant, end: Instant,
isAllDay: Boolean, location: String, description: String,
) = guarded {
api.createHAEvent(
jsonBody(
"calendar_id" to calendarId, "title" to title,
"start" to Dates.format(start, isAllDay), "end" to Dates.format(end, isAllDay),
"allDay" to isAllDay, "location" to location, "description" to description,
)
).ensureSuccess()
api.createHAEvent(simpleEventBody("calendar_id", calendarId, title, start, end, isAllDay, location, description))
.ensureSuccess()
}
suspend fun updateHAEvent(
calendarId: Int, uid: String, title: String, start: Instant, end: Instant,
isAllDay: Boolean, location: String, description: String,
) = guarded {
api.updateHAEvent(calendarId, uid, simpleEventBody(null, null, title, start, end, isAllDay, location, description))
.ensureSuccess()
}
suspend fun deleteHAEvent(calendarId: Int, uid: String) =
guarded { api.deleteHAEvent(calendarId, uid).ensureSuccess() }
/** Body for Google/HA events (no per-event colour). */
private fun simpleEventBody(
calKey: String?, calId: Int?, title: String, start: Instant, end: Instant,
isAllDay: Boolean, location: String, description: String,
) = jsonBody(
buildMap {
if (calKey != null && calId != null) put(calKey, calId)
put("title", title)
put("start", Dates.format(start, isAllDay))
put("end", Dates.format(end, isAllDay))
put("allDay", isAllDay)
put("location", location)
put("description", description)
}
)
private fun eventBody(
calendarId: Int?, title: String, start: Instant, end: Instant,
isAllDay: Boolean, location: String, description: String, color: String?,

View File

@@ -170,10 +170,30 @@ interface CalendarrApi {
@POST("api/google/events")
suspend fun createGoogleEvent(@Body body: RequestBody): Response<ResponseBody>
@PUT("api/google/events/{gcalDbId}/{eventId}")
suspend fun updateGoogleEvent(
@Path("gcalDbId") gcalDbId: Int,
@Path("eventId") eventId: String,
@Body body: RequestBody,
): Response<ResponseBody>
@HTTP(method = "DELETE", path = "api/google/events/{gcalDbId}/{eventId}", hasBody = false)
suspend fun deleteGoogleEvent(
@Path("gcalDbId") gcalDbId: Int,
@Path("eventId") eventId: String,
): Response<ResponseBody>
@POST("api/homeassistant/events")
suspend fun createHAEvent(@Body body: RequestBody): Response<ResponseBody>
@DELETE("api/homeassistant/events/{calendarId}/{uid}")
@PUT("api/homeassistant/events/{calendarId}/{uid}")
suspend fun updateHAEvent(
@Path("calendarId") calendarId: Int,
@Path("uid") uid: String,
@Body body: RequestBody,
): Response<ResponseBody>
@HTTP(method = "DELETE", path = "api/homeassistant/events/{calendarId}/{uid}", hasBody = false)
suspend fun deleteHAEvent(
@Path("calendarId") calendarId: Int,
@Path("uid") uid: String,