|
|
@ -48,6 +48,7 @@ import androidx.compose.runtime.remember
|
|
|
|
import androidx.compose.runtime.setValue
|
|
|
|
import androidx.compose.runtime.setValue
|
|
|
|
import androidx.compose.ui.Alignment
|
|
|
|
import androidx.compose.ui.Alignment
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
|
|
|
|
import androidx.compose.ui.draw.alpha
|
|
|
|
import androidx.compose.ui.draw.clip
|
|
|
|
import androidx.compose.ui.draw.clip
|
|
|
|
import androidx.compose.ui.focus.onFocusChanged
|
|
|
|
import androidx.compose.ui.focus.onFocusChanged
|
|
|
|
import androidx.compose.ui.platform.LocalFocusManager
|
|
|
|
import androidx.compose.ui.platform.LocalFocusManager
|
|
|
@ -94,7 +95,11 @@ data class MainViewNavigation(
|
|
|
|
|
|
|
|
|
|
|
|
@OptIn(ExperimentalPermissionsApi::class)
|
|
|
|
@OptIn(ExperimentalPermissionsApi::class)
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
fun MainView(loginAtUrl: (String) -> Unit, navigation: MainViewNavigation, viewModel: MainViewModel = viewModel()) {
|
|
|
|
fun MainView(
|
|
|
|
|
|
|
|
loginAtUrl: (String) -> Unit,
|
|
|
|
|
|
|
|
navigation: MainViewNavigation,
|
|
|
|
|
|
|
|
viewModel: MainViewModel = viewModel()
|
|
|
|
|
|
|
|
) {
|
|
|
|
LoadingIndicator.Wrap {
|
|
|
|
LoadingIndicator.Wrap {
|
|
|
|
Scaffold(contentWindowInsets = WindowInsets.Companion.statusBars) { paddingInsets ->
|
|
|
|
Scaffold(contentWindowInsets = WindowInsets.Companion.statusBars) { paddingInsets ->
|
|
|
|
Column(
|
|
|
|
Column(
|
|
|
@ -194,9 +199,7 @@ fun ExitNodeStatus(navAction: () -> Unit, viewModel: MainViewModel) {
|
|
|
|
modifier = Modifier.clickable { navAction() },
|
|
|
|
modifier = Modifier.clickable { navAction() },
|
|
|
|
colors =
|
|
|
|
colors =
|
|
|
|
if (active) MaterialTheme.colorScheme.primaryListItem
|
|
|
|
if (active) MaterialTheme.colorScheme.primaryListItem
|
|
|
|
else
|
|
|
|
else ListItemDefaults.colors(containerColor = MaterialTheme.colorScheme.surface),
|
|
|
|
ListItemDefaults.colors(
|
|
|
|
|
|
|
|
containerColor = MaterialTheme.colorScheme.surfaceContainerLowest),
|
|
|
|
|
|
|
|
overlineContent = {
|
|
|
|
overlineContent = {
|
|
|
|
Text(
|
|
|
|
Text(
|
|
|
|
stringResource(R.string.exit_node),
|
|
|
|
stringResource(R.string.exit_node),
|
|
|
@ -238,10 +241,7 @@ fun ExitNodeStatus(navAction: () -> Unit, viewModel: MainViewModel) {
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
fun SettingsButton(action: () -> Unit) {
|
|
|
|
fun SettingsButton(action: () -> Unit) {
|
|
|
|
IconButton(modifier = Modifier.size(24.dp), onClick = { action() }) {
|
|
|
|
IconButton(modifier = Modifier.size(24.dp), onClick = { action() }) {
|
|
|
|
Icon(
|
|
|
|
Icon(Icons.Outlined.Settings, null, tint = MaterialTheme.colorScheme.onSurfaceVariant)
|
|
|
|
Icons.Outlined.Settings,
|
|
|
|
|
|
|
|
null,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -251,7 +251,8 @@ fun StartingView() {
|
|
|
|
modifier = Modifier.fillMaxSize(),
|
|
|
|
modifier = Modifier.fillMaxSize(),
|
|
|
|
verticalArrangement = Arrangement.Center,
|
|
|
|
verticalArrangement = Arrangement.Center,
|
|
|
|
horizontalAlignment = Alignment.CenterHorizontally) {
|
|
|
|
horizontalAlignment = Alignment.CenterHorizontally) {
|
|
|
|
TailscaleLogoView(animated = true, usesOnBackgroundColors = false, Modifier.size(40.dp))
|
|
|
|
TailscaleLogoView(
|
|
|
|
|
|
|
|
animated = true, usesOnBackgroundColors = false, Modifier.size(40.dp).alpha(0.3f))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -286,12 +287,11 @@ fun ConnectView(
|
|
|
|
textAlign = TextAlign.Center)
|
|
|
|
textAlign = TextAlign.Center)
|
|
|
|
Spacer(modifier = Modifier.size(1.dp))
|
|
|
|
Spacer(modifier = Modifier.size(1.dp))
|
|
|
|
selfNode?.let {
|
|
|
|
selfNode?.let {
|
|
|
|
PrimaryActionButton(
|
|
|
|
PrimaryActionButton(onClick = { loginAtUrlAction(it.nodeAdminUrl) }) {
|
|
|
|
onClick = { loginAtUrlAction(it.nodeAdminUrl) }) {
|
|
|
|
Text(
|
|
|
|
Text(
|
|
|
|
text = stringResource(id = R.string.open_admin_console),
|
|
|
|
text = stringResource(id = R.string.open_admin_console),
|
|
|
|
fontSize = MaterialTheme.typography.titleMedium.fontSize)
|
|
|
|
fontSize = MaterialTheme.typography.titleMedium.fontSize)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (state != Ipn.State.NeedsLogin && user != null && !user.isEmpty()) {
|
|
|
|
} else if (state != Ipn.State.NeedsLogin && user != null && !user.isEmpty()) {
|
|
|
|
Icon(
|
|
|
|
Icon(
|
|
|
@ -356,7 +356,8 @@ fun PeerList(
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
val peerList = viewModel.peers.collectAsState(initial = emptyList<PeerSet>())
|
|
|
|
val peerList = viewModel.peers.collectAsState(initial = emptyList<PeerSet>())
|
|
|
|
val searchTermStr by viewModel.searchTerm.collectAsState(initial = "")
|
|
|
|
val searchTermStr by viewModel.searchTerm.collectAsState(initial = "")
|
|
|
|
val showNoResults = remember { derivedStateOf { searchTermStr.isNotEmpty() && peerList.value.isEmpty() } }.value
|
|
|
|
val showNoResults =
|
|
|
|
|
|
|
|
remember { derivedStateOf { searchTermStr.isNotEmpty() && peerList.value.isEmpty() } }.value
|
|
|
|
val netmap = viewModel.netmap.collectAsState()
|
|
|
|
val netmap = viewModel.netmap.collectAsState()
|
|
|
|
|
|
|
|
|
|
|
|
val focusManager = LocalFocusManager.current
|
|
|
|
val focusManager = LocalFocusManager.current
|
|
|
|