@ -10,21 +10,23 @@ 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.WindowInsets
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material.icons.outlined.ArrowDropDown
import androidx.compose.material.icons.outlined.Clear
import androidx.compose.material.icons.outlined.Close
import androidx.compose.material.icons.outlined.Search
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
@ -32,7 +34,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SearchBar
import androidx.compose.material3.SearchBarDefaults
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
@ -68,17 +69,23 @@ data class MainViewNavigation(
@Composable
fun MainView ( navigation : MainViewNavigation , viewModel : MainViewModel = viewModel ( ) ) {
Scaffold { _ ->
Column ( modifier = Modifier . fillMaxWidth ( ) , verticalArrangement = Arrangement . Center ) {
Scaffold ( contentWindowInsets = WindowInsets . Companion . statusBars ) { paddingInsets ->
Column (
modifier = Modifier . fillMaxWidth ( ) . padding ( paddingInsets ) ,
verticalArrangement = Arrangement . Center ) {
val state = viewModel . ipnState . collectAsState ( initial = Ipn . State . NoState )
val user = viewModel . loggedInUser . collectAsState ( initial = null )
Row (
modifier = Modifier . fillMaxWidth ( ) . padding ( horizontal = 8. dp ) ,
modifier =
Modifier . fillMaxWidth ( )
. background ( MaterialTheme . colorScheme . secondaryContainer )
. padding ( horizontal = 8. dp )
. padding ( top = 10. dp ) ,
verticalAlignment = Alignment . CenterVertically ) {
val isOn = viewModel . vpnToggleState . collectAsState ( initial = false )
if ( state . value != Ipn . State . NeedsLogin && state . value != Ipn . State . NoState ) {
Switch ( onCheckedChange = { viewModel . toggleVpn ( ) } , checked = isOn . value )
Tinted Switch( onCheckedChange = { viewModel . toggleVpn ( ) } , checked = isOn . value )
Spacer ( Modifier . size ( 3. dp ) )
}
@ -98,7 +105,13 @@ fun MainView(navigation: MainViewNavigation, viewModel: MainViewModel = viewMode
Ipn . State . Running -> {
val selfPeerId = viewModel . selfPeerId . collectAsState ( initial = " " )
ExitNodeStatus ( navAction = navigation . onNavigateToExitNodes , viewModel = viewModel )
Row (
modifier =
Modifier . background ( MaterialTheme . colorScheme . secondaryContainer )
. padding ( top = 10. dp , bottom = 20. dp ) ) {
ExitNodeStatus (
navAction = navigation . onNavigateToExitNodes , viewModel = viewModel )
}
PeerList (
searchTerm = viewModel . searchTerm ,
state = viewModel . ipnState ,
@ -135,16 +148,17 @@ fun ExitNodeStatus(navAction: () -> Unit, viewModel: MainViewModel) {
Modifier . clickable { navAction ( ) }
. padding ( horizontal = 8. dp )
. clip ( shape = RoundedCornerShape ( 10. dp , 10. dp , 10. dp , 10. dp ) )
. background ( MaterialTheme . colorScheme . secondaryContainer )
. background ( MaterialTheme . colorScheme . background )
. fillMaxWidth ( ) ) {
Column ( modifier = Modifier . padding ( 6 .dp ) ) {
Column ( modifier = Modifier . padding ( vertical = 15. dp , horizontal = 18 .dp ) ) {
Text (
text = stringResource ( id = R . string . exit _node ) ,
style = MaterialTheme . typography . titleMedium )
color = MaterialTheme . colorScheme . secondary ,
style = MaterialTheme . typography . titleSmall )
Row ( verticalAlignment = Alignment . CenterVertically ) {
Text (
text = exitNode ?: stringResource ( id = R . string . none ) ,
style = MaterialTheme . typography . body Medium )
style = MaterialTheme . typography . body Large )
Icon (
Icons . Outlined . ArrowDropDown ,
null ,
@ -208,12 +222,13 @@ fun StartingView() {
fun ConnectView ( user : IpnLocal . LoginProfile ? , connectAction : ( ) -> Unit , loginAction : ( ) -> Unit ) {
Row ( horizontalArrangement = Arrangement . Center , modifier = Modifier . fillMaxWidth ( ) ) {
Column (
horizontalAlignment = Alignment . CenterHorizontally ,
modifier =
Modifier . background ( MaterialTheme . colorScheme . secondaryContainer )
. padding ( 8. dp )
. fillMaxWidth ( 0.7f )
. fillMaxHeight ( ) ,
verticalArrangement = Arrangement . spacedBy ( 8. dp , alignment = Alignment . CenterVertically ) ,
Modifier . background ( MaterialTheme . colorScheme . secondaryContainer ) . fillMaxWidth ( ) ) {
Column (
modifier = Modifier . padding ( 8. dp ) . fillMaxWidth ( 0.7f ) . fillMaxHeight ( ) ,
verticalArrangement =
Arrangement . spacedBy ( 8. dp , alignment = Alignment . CenterVertically ) ,
horizontalAlignment = Alignment . CenterHorizontally ,
) {
if ( user != null && ! user . isEmpty ( ) ) {
@ -266,6 +281,7 @@ fun ConnectView(user: IpnLocal.LoginProfile?, connectAction: () -> Unit, loginAc
}
}
}
}
@Composable
fun ClearButton ( onClick : ( ) -> Unit ) {
@ -308,9 +324,11 @@ fun PeerList(
trailingIcon = {
if ( searchTermStr . isNotEmpty ( ) ) ClearButton ( { onSearch ( " " ) } ) else CloseButton ( )
} ,
tonalElevation = 2. dp ,
shadowElevation = 2. dp ,
colors = SearchBarDefaults . colors ( ) ,
tonalElevation = 0. dp ,
shadowElevation = 0. dp ,
colors =
SearchBarDefaults . colors (
containerColor = Color . Transparent , dividerColor = Color . Transparent ) ,
modifier = Modifier . fillMaxWidth ( ) ) {
LazyColumn (
modifier =
@ -323,7 +341,8 @@ fun PeerList(
Text (
text =
peerSet . user ?. DisplayName ?: stringResource ( id = R . string . unknown _user ) ,
style = MaterialTheme . typography . titleLarge )
style = MaterialTheme . typography . titleLarge ,
fontWeight = FontWeight . SemiBold )
} )
}
peerSet . peers . forEach { peer ->
@ -344,19 +363,20 @@ fun PeerList(
}
Box (
modifier =
Modifier . size ( 8 .dp )
Modifier . size ( 10 .dp )
. background (
color = color , shape = RoundedCornerShape ( percent = 50 ) ) ) { }
Spacer ( modifier = Modifier . size ( 8 .dp ) )
Spacer ( modifier = Modifier . size ( 6 .dp ) )
Text ( text = peer . ComputedName , style = MaterialTheme . typography . titleMedium )
}
} ,
supportingContent = {
Text (
text = peer . Addresses ?. first ( ) ?. split ( " / " ) ?. first ( ) ?: " " ,
style = MaterialTheme . typography . bodyMedium )
} ,
trailingContent = { Icon ( Icons . AutoMirrored . Filled . KeyboardArrowRight , null ) } )
style = MaterialTheme . typography . bodyMedium ,
color = MaterialTheme . colorScheme . secondary )
} )
HorizontalDivider ( color = MaterialTheme . colorScheme . secondaryContainer )
}
}
}