@ -4,6 +4,7 @@
package com.tailscale.ipn.ui.view
package com.tailscale.ipn.ui.view
import android.text.format.Formatter
import android.text.format.Formatter
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Arrangement
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
@ -19,10 +20,14 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.stringResource
@ -36,6 +41,7 @@ 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
import com.tailscale.ipn.util.TSLog
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.StateFlow
@ -46,36 +52,44 @@ fun TaildropView(
viewModel : TaildropViewModel =
viewModel : TaildropViewModel =
viewModel ( factory = TaildropViewModelFactory ( requestedTransfers , applicationScope ) )
viewModel ( factory = TaildropViewModelFactory ( requestedTransfers , applicationScope ) )
) {
) {
Scaffold (
val TAG = " TaildropView "
contentWindowInsets = WindowInsets . Companion . statusBars ,
val focusRequester = remember { FocusRequester ( ) }
topBar = { Header ( R . string . share ) } ) { paddingInsets ->
val showDialog = viewModel . showDialog . collectAsState ( ) . value
// Show the error overlay
// Automatically request focus when the composable is displayed
showDialog ?. let { ErrorDialog ( type = it , action = { viewModel . showDialog . set ( null ) } ) }
LaunchedEffect ( Unit ) {
try {
focusRequester . requestFocus ( )
} catch ( e : Exception ) {
TSLog . w ( TAG , " Focus request failed: ${e.message} " )
}
}
Column ( modifier = Modifier . padding ( paddingInsets ) ) {
Scaffold ( contentWindowInsets = WindowInsets . statusBars , topBar = { Header ( R . string . share ) } ) {
FileShareHeader (
paddingInsets ->
fileTransfers = requestedTransfers . collectAsState ( ) . value ,
Column ( modifier = Modifier . focusRequester ( focusRequester ) . focusable ( ) . padding ( paddingInsets ) ) {
totalSize = viewModel . totalSize )
val showDialog = viewModel . showDialog . collectAsState ( ) . value
when ( viewModel . state . collectAsState ( ) . value ) {
showDialog ?. let { ErrorDialog ( type = it , action = { viewModel . showDialog . set ( null ) } ) }
Ipn . State . Running -> {
val peers by viewModel . myPeers . collectAsState ( )
FileShareHeader (
val context = LocalContext . current
fileTransfers = requestedTransfers . collectAsState ( ) . value ,
FileSharePeerList (
totalSize = viewModel . totalSize )
peers = peers ,
stateViewGenerator = { peerId ->
when ( viewModel . state . collectAsState ( ) . value ) {
viewModel . TrailingContentForPeer ( peerId = peerId )
Ipn . State . Running -> {
} ,
val peers by viewModel . myPeers . collectAsState ( )
onShare = { viewModel . share ( context , it ) } )
val context = LocalContext . current
}
FileSharePeerList (
else -> {
peers = peers ,
FileShareConnectView { viewModel . startVPN ( ) }
stateViewGenerator = { peerId -> viewModel . TrailingContentForPeer ( peerId = peerId ) } ,
}
onShare = { viewModel . share ( context , it ) } )
}
}
else -> {
FileShareConnectView { viewModel . startVPN ( ) }
}
}
}
}
}
}
}
}
@Composable
@Composable