@ -8,11 +8,12 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Scaffold
@ -31,6 +32,7 @@ import coil.compose.AsyncImage
import com.tailscale.ipn.R
import com.tailscale.ipn.R
import com.tailscale.ipn.ui.model.Ipn
import com.tailscale.ipn.ui.model.Ipn
import com.tailscale.ipn.ui.model.Tailcfg
import com.tailscale.ipn.ui.model.Tailcfg
import com.tailscale.ipn.ui.util.Lists.SectionDivider
import com.tailscale.ipn.ui.util.set
import com.tailscale.ipn.ui.util.set
import com.tailscale.ipn.ui.viewModel.TaildropViewModel
import com.tailscale.ipn.ui.viewModel.TaildropViewModel
import com.tailscale.ipn.ui.viewModel.TaildropViewModelFactory
import com.tailscale.ipn.ui.viewModel.TaildropViewModelFactory
@ -44,33 +46,36 @@ fun TaildropView(
viewModel : TaildropViewModel =
viewModel : TaildropViewModel =
viewModel ( factory = TaildropViewModelFactory ( requestedTransfers , applicationScope ) )
viewModel ( factory = TaildropViewModelFactory ( requestedTransfers , applicationScope ) )
) {
) {
Scaffold ( topBar = { Header ( R . string . share ) } ) { paddingInsets ->
Scaffold (
val showDialog = viewModel . showDialog . collectAsState ( ) . value
contentWindowInsets = WindowInsets . Companion . statusBars ,
topBar = { Header ( R . string . share ) } ) { paddingInsets ->
val showDialog = viewModel . showDialog . collectAsState ( ) . value
// Show the error overlay
// Show the error overlay
showDialog ?. let { ErrorDialog ( type = it , action = { viewModel . showDialog . set ( null ) } ) }
showDialog ?. let { ErrorDialog ( type = it , action = { viewModel . showDialog . set ( null ) } ) }
Column ( modifier = Modifier . padding ( paddingInsets ) ) {
Column ( modifier = Modifier . padding ( paddingInsets ) ) {
FileShareHeader (
FileShareHeader (
fileTransfers = requestedTransfers . collectAsState ( ) . value ,
fileTransfers = requestedTransfers . collectAsState ( ) . value ,
totalSize = viewModel . totalSize )
totalSize = viewModel . totalSize )
Spacer ( modifier = Modifier . size ( 8. dp ) )
when ( viewModel . state . collectAsState ( ) . value ) {
when ( viewModel . state . collectAsState ( ) . value ) {
Ipn . State . Running -> {
Ipn . State . Running -> {
val peers by viewModel . myPeers . collectAsState ( )
val peers by viewModel . myPeers . collectAsState ( )
val context = LocalContext . current
val context = LocalContext . current
FileSharePeerList (
FileSharePeerList (
peers = peers ,
peers = peers ,
stateViewGenerator = { peerId -> viewModel . TrailingContentForPeer ( peerId = peerId ) } ,
stateViewGenerator = { peerId ->
onShare = { viewModel . share ( context , it ) } )
viewModel . TrailingContentForPeer ( peerId = peerId )
}
} ,
else -> {
onShare = { viewModel . share ( context , it ) } )
FileShareConnectView { viewModel . startVPN ( ) }
}
else -> {
FileShareConnectView { viewModel . startVPN ( ) }
}
}
}
}
}
}
}
}
}
}
@Composable
@Composable
@ -79,9 +84,7 @@ fun FileSharePeerList(
stateViewGenerator : @Composable ( String ) -> Unit ,
stateViewGenerator : @Composable ( String ) -> Unit ,
onShare : ( Tailcfg . Node ) -> Unit
onShare : ( Tailcfg . Node ) -> Unit
) {
) {
Column ( modifier = Modifier . padding ( horizontal = 8. dp ) ) {
SectionDivider ( stringResource ( R . string . my _devices ) )
Text ( stringResource ( R . string . my _devices ) , style = MaterialTheme . typography . titleMedium )
}
when ( peers . isEmpty ( ) ) {
when ( peers . isEmpty ( ) ) {
true -> {
true -> {
@ -113,8 +116,8 @@ fun FileSharePeerList(
@Composable
@Composable
fun FileShareConnectView ( onToggle : ( ) -> Unit ) {
fun FileShareConnectView ( onToggle : ( ) -> Unit ) {
Column (
Column (
modifier = Modifier . padding ( horizontal = 8 .dp ) . fillMaxHeight ( ) ,
modifier = Modifier . padding ( horizontal = 16 .dp ) . fillMaxHeight ( ) ,
verticalArrangement = Arrangement . Center,
verticalArrangement = Arrangement . spacedBy( 6. dp , alignment = Alignment . CenterVertically) ,
horizontalAlignment = Alignment . CenterHorizontally ) {
horizontalAlignment = Alignment . CenterHorizontally ) {
Text (
Text (
stringResource ( R . string . connect _to _your _tailnet _to _share _files ) ,
stringResource ( R . string . connect _to _your _tailnet _to _share _files ) ,
@ -130,7 +133,7 @@ fun FileShareConnectView(onToggle: () -> Unit) {
@Composable
@Composable
fun FileShareHeader ( fileTransfers : List < Ipn . OutgoingFile > , totalSize : Long ) {
fun FileShareHeader ( fileTransfers : List < Ipn . OutgoingFile > , totalSize : Long ) {
Column ( modifier = Modifier . padding ( horizontal = 8 .dp ) ) {
Column ( modifier = Modifier . padding ( horizontal = 12 .dp ) ) {
Row ( verticalAlignment = Alignment . CenterVertically ) {
Row ( verticalAlignment = Alignment . CenterVertically ) {
IconForTransfer ( fileTransfers )
IconForTransfer ( fileTransfers )
Column ( modifier = Modifier . padding ( horizontal = 8. dp ) ) {
Column ( modifier = Modifier . padding ( horizontal = 8. dp ) ) {
@ -151,10 +154,12 @@ fun FileShareHeader(fileTransfers: List<Ipn.OutgoingFile>, totalSize: Long) {
}
}
}
}
val size = Formatter . formatFileSize ( LocalContext . current , totalSize . toLong ( ) )
val size = Formatter . formatFileSize ( LocalContext . current , totalSize . toLong ( ) )
Text ( size , style = MaterialTheme . typography . titleMedium )
Text (
size ,
style = MaterialTheme . typography . titleSmall ,
color = MaterialTheme . colorScheme . secondary )
}
}
}
}
HorizontalDivider ( )
}
}
}
}