package com.peachpasswords.ui.components import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.peachpasswords.ui.theme.LocalPeachAccent import com.peachpasswords.ui.theme.peachColors import com.peachpasswords.vault.TOTPGenerator import kotlinx.coroutines.delay /** * A small ring that sweeps once per TOTP period and turns breach-red in the * final 5 seconds. Port of the brief's TOTPRing. */ @Composable fun TOTPRing(modifier: Modifier = Modifier, size: Dp = 28.dp) { val accent = LocalPeachAccent.current val progress by produceState(TOTPGenerator.periodProgress()) { while (true) { value = TOTPGenerator.periodProgress(); delay(100) } } val isHot = TOTPGenerator.remainingSeconds() <= 5 val track = peachColors.T3 val breach = peachColors.Breach Canvas(modifier = modifier.size(size)) { val stroke = 2.dp.toPx() val radius = (this.size.minDimension - stroke) / 2f val center = Offset(this.size.width / 2f, this.size.height / 2f) drawCircle(color = track, radius = radius, style = Stroke(stroke)) drawArc( color = if (isHot) breach else accent, startAngle = -90f, // Starts full at period start (progress=0), depletes to empty as the // code approaches expiry (progress=1) — the conventional countdown // direction, not "fills up as time passes." sweepAngle = 360f * (1f - progress), useCenter = false, style = Stroke(stroke, cap = StrokeCap.Round), topLeft = Offset(center.x - radius, center.y - radius), size = Size(radius * 2, radius * 2), ) } }