@@ -17,15 +17,21 @@ import androidx.compose.material.icons.Icons
17import androidx.compose.material.icons.filled.Lock
17import androidx.compose.material.icons.filled.Lock
18import androidx.compose.material.icons.filled.Person
18import androidx.compose.material.icons.filled.Person
19import androidx.compose.material.icons.filled.Terminal
19import androidx.compose.material.icons.filled.Terminal
20+import androidx.compose.material.icons.filled.Visibility
21+import androidx.compose.material.icons.filled.VisibilityOff
20import androidx.compose.material3.Button
22import androidx.compose.material3.Button
21import androidx.compose.material3.CircularProgressIndicator
23import androidx.compose.material3.CircularProgressIndicator
22import androidx.compose.material3.Icon
24import androidx.compose.material3.Icon
25+import androidx.compose.material3.IconButton
23import androidx.compose.material3.MaterialTheme
26import androidx.compose.material3.MaterialTheme
24import androidx.compose.material3.OutlinedTextField
27import androidx.compose.material3.OutlinedTextField
25import androidx.compose.material3.Text
28import androidx.compose.material3.Text
26import androidx.compose.runtime.Composable
29import androidx.compose.runtime.Composable
27import androidx.compose.runtime.LaunchedEffect
30import androidx.compose.runtime.LaunchedEffect
31+import androidx.compose.runtime.getValue
32+import androidx.compose.runtime.mutableStateOf
28import androidx.compose.runtime.remember
33import androidx.compose.runtime.remember
34+import androidx.compose.runtime.setValue
29import androidx.compose.ui.Alignment
35import androidx.compose.ui.Alignment
30import androidx.compose.ui.Modifier
36import androidx.compose.ui.Modifier
31import androidx.compose.ui.focus.FocusRequester
37import androidx.compose.ui.focus.FocusRequester
@@ -33,6 +39,7 @@ import androidx.compose.ui.focus.focusRequester
33import androidx.compose.ui.text.input.ImeAction
39import androidx.compose.ui.text.input.ImeAction
34import androidx.compose.ui.text.input.KeyboardType
40import androidx.compose.ui.text.input.KeyboardType
35import androidx.compose.ui.text.input.PasswordVisualTransformation
41import androidx.compose.ui.text.input.PasswordVisualTransformation
42+import androidx.compose.ui.text.input.VisualTransformation
36import androidx.compose.ui.text.style.TextAlign
43import androidx.compose.ui.text.style.TextAlign
37import androidx.compose.ui.unit.dp
44import androidx.compose.ui.unit.dp
38import soy.iko.opencode.core.Event
45import soy.iko.opencode.core.Event
@@ -41,6 +48,7 @@ import soy.iko.opencode.core.UiState
41@Composable
48@Composable
42fun ConnectScreen(state: UiState, dispatch: (Event) -> Unit) {
49fun ConnectScreen(state: UiState, dispatch: (Event) -> Unit) {
43 val focusRequester = remember { FocusRequester() }
50 val focusRequester = remember { FocusRequester() }
51+ var passwordVisible by remember { mutableStateOf(false) }
45 // Focus the server URL field when the screen opens, but only when it's empty
53 // Focus the server URL field when the screen opens, but only when it's empty
46 // so we don't steal focus (and pop the keyboard) on a return visit.
54 // so we don't steal focus (and pop the keyboard) on a return visit.
@@ -111,8 +119,16 @@ fun ConnectScreen(state: UiState, dispatch: (Event) -> Unit) {
111 onValueChange = { dispatch(Event.PasswordChanged(it)) },
119 onValueChange = { dispatch(Event.PasswordChanged(it)) },
112 label = { Text("Password") },
120 label = { Text("Password") },
113 singleLine = true,
121 singleLine = true,
114− visualTransformation = PasswordVisualTransformation(),
122+ visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
115 leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = null) },
123 leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = null) },
125+ IconButton(onClick = { passwordVisible = !passwordVisible }) {
127+ imageVector = if (passwordVisible) Icons.Filled.VisibilityOff else Icons.Filled.Visibility,
128+ contentDescription = if (passwordVisible) "Hide password" else "Show password",
116 modifier = Modifier.fillMaxWidth(),
132 modifier = Modifier.fillMaxWidth(),
117 keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Go),
133 keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Go),
118 keyboardActions = KeyboardActions(onGo = { dispatch(Event.Connect) }),
134 keyboardActions = KeyboardActions(onGo = { dispatch(Event.Connect) }),