1package com.peachpasswords.ui.vault23456789101112131415161718import androidx.compose.foundation.background19import androidx.compose.foundation.border20import androidx.compose.foundation.clickable21import androidx.compose.foundation.layout.Box22import androidx.compose.foundation.layout.Column23import androidx.compose.foundation.layout.Spacer24import androidx.compose.foundation.layout.fillMaxWidth25import androidx.compose.foundation.layout.height26import androidx.compose.foundation.layout.padding27import androidx.compose.foundation.layout.size28import androidx.compose.foundation.shape.RoundedCornerShape29import androidx.compose.material.icons.Icons30import androidx.compose.material.icons.outlined.Build31import androidx.compose.material3.ExperimentalMaterial3Api32import androidx.compose.material3.Icon33import androidx.compose.material3.ModalBottomSheet34import androidx.compose.material3.Text35import androidx.compose.material3.rememberModalBottomSheetState36import androidx.compose.runtime.Composable37import androidx.compose.runtime.getValue38import androidx.compose.runtime.mutableStateOf39import androidx.compose.runtime.remember40import androidx.compose.runtime.setValue41import androidx.compose.ui.Alignment42import androidx.compose.ui.Modifier43import androidx.compose.ui.draw.clip44import androidx.compose.ui.text.font.FontStyle45import androidx.compose.ui.unit.dp46import androidx.compose.ui.unit.sp47import com.peachpasswords.ui.theme.LocalPeachAccent48import com.peachpasswords.ui.theme.peachColors49import com.peachpasswords.ui.theme.PeachType50import com.peachpasswords.vault.PasswordGenerator5152535455565758@Composable59internal fun WrenchButton(onClick: () -> Unit) {60 Box(61 Modifier62 .size(36.dp)63 .clip(RoundedCornerShape(6.dp))64 .background(peachColors.S2)65 .clickable(onClick = onClick),66 contentAlignment = Alignment.Center,67 ) {68 Icon(69 Icons.Outlined.Build,70 contentDescription = "generate password",71 tint = peachColors.T2,72 modifier = Modifier.size(15.dp),73 )74 }75}76777879808182@param83@param84@param85@param8687@OptIn(ExperimentalMaterial3Api::class)88@Composable89internal fun GeneratorSheet(90 copied: Boolean,91 onCopy: (String) -> Unit,92 onUse: (String) -> Unit,93 onDismiss: () -> Unit,94) {95 val accent = LocalPeachAccent.current96 val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)9798 var mode by remember { mutableStateOf(GeneratorMode.Password) }99 var length by remember { mutableStateOf(20) }100 var wordCount by remember { mutableStateOf(PasswordGenerator.DEFAULT_PASSPHRASE_WORDS) }101 var separator by remember { mutableStateOf("-") }102 var upper by remember { mutableStateOf(true) }103 var lower by remember { mutableStateOf(true) }104 var numbers by remember { mutableStateOf(true) }105 var symbols by remember { mutableStateOf(true) }106 var draft by remember { mutableStateOf("") }107108 fun gen() {109 draft = if (mode == GeneratorMode.Passphrase) {110 PasswordGenerator.generatePassphrase(wordCount, separator)111 } else {112 PasswordGenerator.generate(113 PasswordGenerator.Options(114 length = length,115 includeUppercase = upper,116 includeLowercase = lower,117 includeNumbers = numbers,118 includeSymbols = symbols,119 ),120 )121 }122 }123124 ModalBottomSheet(125 onDismissRequest = onDismiss,126 sheetState = sheetState,127 containerColor = peachColors.S1,128 ) {129 Column(Modifier.fillMaxWidth().padding(horizontal = 20.dp, vertical = 4.dp)) {130 Text(131 "generate password",132 color = peachColors.T0,133 fontFamily = PeachType.Display, fontStyle = FontStyle.Italic, fontSize = 18.sp,134 )135 Spacer(Modifier.height(8.dp))136137 138 Box(139 Modifier.fillMaxWidth().clip(RoundedCornerShape(10.dp))140 .border(1.dp, peachColors.T3, RoundedCornerShape(10.dp))141 .padding(horizontal = 12.dp, vertical = 14.dp),142 ) {143 Text(144 text = draft.ifEmpty { "tap generate" },145 color = if (draft.isEmpty()) peachColors.T2 else peachColors.T0,146 fontFamily = PeachType.Mono, fontSize = 13.sp,147 )148 }149 Spacer(Modifier.height(6.dp))150151 152 GeneratorBody(153 mode = mode,154 length = length,155 wordCount = wordCount,156 separator = separator,157 upper = upper, lower = lower, numbers = numbers, symbols = symbols,158 password = draft, copied = copied,159 onMode = { mode = it; gen() },160 onLength = { length = it },161 onWordCount = { wordCount = it },162 onSeparator = { separator = it; gen() },163 onUpper = { upper = it }, onLower = { lower = it },164 onNumbers = { numbers = it }, onSymbols = { symbols = it },165 onGenerate = { gen() },166 onCopy = { if (draft.isNotEmpty()) onCopy(draft) },167 )168169 170 Box(171 Modifier.fillMaxWidth().clip(RoundedCornerShape(10.dp))172 .background(if (draft.isEmpty()) accent.copy(alpha = 0.10f) else accent.copy(alpha = 0.18f))173 .border(1.dp, accent.copy(alpha = 0.5f), RoundedCornerShape(10.dp))174 .clickable(enabled = draft.isNotEmpty()) { onUse(draft) }175 .padding(vertical = 14.dp),176 contentAlignment = Alignment.Center,177 ) {178 Text(179 "use this ${if (mode == GeneratorMode.Passphrase) "passphrase" else "password"}",180 color = if (draft.isEmpty()) accent.copy(alpha = 0.5f) else accent,181 fontFamily = PeachType.Mono, fontSize = 12.sp,182 )183 }184 Spacer(Modifier.height(20.dp))185 }186 }187}