// Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause package com.tailscale.ipn.ui.view import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.ArrowForward import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.tailscale.ipn.R import com.tailscale.ipn.ui.theme.ts_color_light_blue import com.tailscale.ipn.ui.util.LoadingIndicator import com.tailscale.ipn.ui.viewModel.ExitNodePickerNav import com.tailscale.ipn.ui.viewModel.RunExitNodeViewModel import com.tailscale.ipn.ui.viewModel.RunExitNodeViewModelFactory @Composable fun RunExitNodeView( nav: ExitNodePickerNav, model: RunExitNodeViewModel = viewModel(factory = RunExitNodeViewModelFactory()) ) { val isRunningExitNode = model.isRunningExitNode.collectAsState().value Scaffold( topBar = { Header(R.string.run_as_exit_node, onBack = nav.onNavigateToExitNodePicker) }) { innerPadding -> LoadingIndicator.Wrap { Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(16.dp, alignment = Alignment.Top), modifier = Modifier.padding(innerPadding).padding(16.dp).fillMaxHeight()) { RunExitNodeGraphic() if (isRunningExitNode) { Text( stringResource(R.string.running_as_exit_node), fontFamily = MaterialTheme.typography.titleLarge.fontFamily, fontSize = MaterialTheme.typography.titleLarge.fontSize, fontWeight = FontWeight.SemiBold) Text(stringResource(R.string.run_exit_node_explainer_running)) } else { Text( stringResource(R.string.run_this_device_as_an_exit_node), fontFamily = MaterialTheme.typography.titleLarge.fontFamily, fontSize = MaterialTheme.typography.titleLarge.fontSize, fontWeight = FontWeight.SemiBold) Text(stringResource(R.string.run_exit_node_explainer)) } Text(stringResource(R.string.run_exit_node_caution), color = Color.Red) PrimaryActionButton(onClick = { model.setRunningExitNode(!isRunningExitNode) }) { if (isRunningExitNode) { Text(stringResource(R.string.stop_running_as_exit_node)) } else { Text(stringResource(R.string.start_running_as_exit_node)) } } } } } } @Composable fun RunExitNodeGraphic() { @Composable fun ArrowForward() { Icon( Icons.AutoMirrored.Outlined.ArrowForward, "Arrow Forward", tint = MaterialTheme.colorScheme.secondary, modifier = Modifier.size(24.dp)) } Row( horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(vertical = 18.dp)) { Icon( painter = painterResource(id = R.drawable.computer), "Computer icon", tint = ts_color_light_blue, modifier = Modifier.size(36.dp)) ArrowForward() Icon( painter = painterResource(id = R.drawable.android), "Android icon", tint = ts_color_light_blue, modifier = Modifier.size(36.dp)) ArrowForward() Icon( painter = painterResource(id = R.drawable.globe), "Globe icon", tint = ts_color_light_blue, modifier = Modifier.size(36.dp)) } }