package com.peachpasswords.ui.vault import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.peachpasswords.ui.theme.LocalPeachAccent import com.peachpasswords.ui.theme.PeachSpacing import com.peachpasswords.ui.theme.peachColors import com.peachpasswords.ui.theme.PeachType import com.peachpasswords.vault.PasswordHistoryItem /** * Shared past-password history UI, extracted from EntryEditScreen so both the * editor and EntryDetailScreen render the same rows. Behavior is unchanged from * the original private copy. * * - [PasswordHistorySection] is the original combined toggle + content block used * by EntryEditScreen (toggle always present so the affordance is discoverable; * empty state explains how it fills). * - [PasswordHistoryToggle] + [PasswordHistoryList] split the same pieces apart so * EntryDetailScreen can drive expand/collapse with AnimatedVisibility itself. * * Rows are masked until revealed; "use" hands the value to [onRestore]. */ @Composable internal fun PasswordHistorySection( history: List, expanded: Boolean, onToggle: () -> Unit, onRestore: (String) -> Unit, onCopy: (String) -> Unit, ) { PasswordHistoryToggle( count = history.size, expanded = expanded, onToggle = onToggle, modifier = Modifier.fillMaxWidth().padding(horizontal = PeachSpacing.lg, vertical = PeachSpacing.sm), ) if (expanded) { Box(Modifier.fillMaxWidth().height(1.dp).background(peachColors.T3)) Column(Modifier.fillMaxWidth().padding(horizontal = PeachSpacing.lg, vertical = PeachSpacing.md)) { PasswordHistoryList(history = history, onRestore = onRestore, onCopy = onCopy) } } } /** * The "↺ history (N)" affordance — Cormorant italic, accent-tinted. Shows a bare * "↺ history" when empty and "↺ hide history" when [expanded]. */ @Composable internal fun PasswordHistoryToggle( count: Int, expanded: Boolean, onToggle: () -> Unit, modifier: Modifier = Modifier, ) { val accent = LocalPeachAccent.current val label = if (count == 0) "↺ history" else "↺ history ($count)" Text( if (expanded) "↺ hide history" else label, color = accent.copy(alpha = 0.7f), fontFamily = PeachType.Display, fontStyle = FontStyle.Italic, fontSize = 15.sp, modifier = modifier.clip(RoundedCornerShape(6.dp)).clickable(onClick = onToggle).padding(vertical = 4.dp), ) } /** * The expanded body: either the empty-state explainer or the list of past * passwords. Caller owns the expand/collapse (and any AnimatedVisibility wrap). */ @Composable internal fun PasswordHistoryList( history: List, onRestore: (String) -> Unit, onCopy: (String) -> Unit, ) { val accent = LocalPeachAccent.current if (history.isEmpty()) { Text( "No earlier passwords yet. When you change this password, the old one is saved here so you can copy or restore it.", color = peachColors.T2, fontFamily = PeachType.Mono, fontSize = 10.sp, modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp), ) } else { Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { history.forEach { item -> HistoryRow(item, onRestore, onCopy) } } } } @Composable private fun HistoryRow( item: PasswordHistoryItem, onRestore: (String) -> Unit, onCopy: (String) -> Unit, ) { val accent = LocalPeachAccent.current var revealed by remember { mutableStateOf(false) } Row( Modifier.fillMaxWidth().clip(RoundedCornerShape(8.dp)) .border(1.dp, accent.copy(alpha = 0.12f), RoundedCornerShape(8.dp)) .background(accent.copy(alpha = 0.06f)).padding(horizontal = 10.dp, vertical = 8.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, ) { Column(Modifier.weight(1f)) { Text( if (revealed) item.password else "••••••••••••", color = peachColors.T0, fontFamily = PeachType.Mono, fontSize = 12.sp, maxLines = 1, ) Text( timeSince(item.changedAt), color = accent.copy(alpha = 0.55f), fontFamily = PeachType.Mono, fontSize = 8.sp, ) } Spacer(Modifier.width(6.dp)) HistoryAction(if (revealed) "hide" else "show") { revealed = !revealed } HistoryAction("copy") { onCopy(item.password) } HistoryAction("use") { onRestore(item.password) } } } @Composable private fun HistoryAction(label: String, onClick: () -> Unit) { Text( label, color = LocalPeachAccent.current.copy(alpha = 0.7f), fontFamily = PeachType.Mono, fontSize = 9.sp, modifier = Modifier.clip(RoundedCornerShape(5.dp)).clickable(onClick = onClick).padding(horizontal = 5.dp, vertical = 3.dp), ) } /** Coarse "time since" label for the history list. Mirrors the browser helper. */ private fun timeSince(whenMs: Long): String { val secs = ((System.currentTimeMillis() - whenMs) / 1000).coerceAtLeast(0) if (secs < 60) return "just now" val mins = secs / 60 if (mins < 60) return "${mins}m ago" val hours = mins / 60 if (hours < 24) return "${hours}h ago" val days = hours / 24 if (days < 30) return "${days}d ago" val months = days / 30 if (months < 12) return "${months}mo ago" return "${months / 12}y ago" }