276 lines
8.8 KiB
Kotlin
276 lines
8.8 KiB
Kotlin
package app.ui.dialogs
|
|
|
|
import androidx.compose.animation.AnimatedVisibility
|
|
import androidx.compose.animation.animateContentSize
|
|
import androidx.compose.foundation.background
|
|
import androidx.compose.foundation.layout.*
|
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
import androidx.compose.material.*
|
|
import androidx.compose.material.icons.Icons
|
|
import androidx.compose.material.icons.filled.Search
|
|
import androidx.compose.runtime.*
|
|
import androidx.compose.ui.Alignment
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.draw.clip
|
|
import androidx.compose.ui.focus.FocusRequester
|
|
import androidx.compose.ui.focus.focusOrder
|
|
import androidx.compose.ui.text.TextStyle
|
|
import androidx.compose.ui.unit.dp
|
|
import androidx.compose.ui.unit.sp
|
|
import app.git.CloneStatus
|
|
import app.theme.primaryTextColor
|
|
import app.ui.components.PrimaryButton
|
|
import app.viewmodels.CloneViewModel
|
|
import openDirectoryDialog
|
|
import java.io.File
|
|
|
|
@Composable
|
|
fun CloneDialog(
|
|
cloneViewModel: CloneViewModel,
|
|
onClose: () -> Unit,
|
|
onOpenRepository: (File) -> Unit,
|
|
) {
|
|
val cloneStatus = cloneViewModel.cloneStatus.collectAsState()
|
|
val cloneStatusValue = cloneStatus.value
|
|
|
|
MaterialDialog {
|
|
Box(
|
|
modifier = Modifier
|
|
.width(400.dp)
|
|
.animateContentSize()
|
|
) {
|
|
when (cloneStatusValue) {
|
|
is CloneStatus.Cloning -> {
|
|
Cloning(cloneViewModel, cloneStatusValue)
|
|
}
|
|
is CloneStatus.Cancelling -> {
|
|
onClose()
|
|
}
|
|
is CloneStatus.Completed -> {
|
|
onOpenRepository(cloneStatusValue.repoDir)
|
|
onClose()
|
|
}
|
|
is CloneStatus.Fail -> CloneInput(
|
|
cloneViewModel = cloneViewModel,
|
|
onClose = onClose,
|
|
errorMessage = cloneStatusValue.reason
|
|
)
|
|
CloneStatus.None -> CloneInput(
|
|
cloneViewModel = cloneViewModel,
|
|
onClose = onClose,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
private fun CloneInput(
|
|
cloneViewModel: CloneViewModel,
|
|
onClose: () -> Unit,
|
|
errorMessage: String? = null,
|
|
) {
|
|
var url by remember { mutableStateOf(cloneViewModel.url) }
|
|
var directory by remember { mutableStateOf(cloneViewModel.directory) }
|
|
var errorHasBeenNoticed by remember { mutableStateOf(false) }
|
|
|
|
val urlFocusRequester = remember { FocusRequester() }
|
|
val directoryFocusRequester = remember { FocusRequester() }
|
|
val directoryButtonFocusRequester = remember { FocusRequester() }
|
|
val cloneButtonFocusRequester = remember { FocusRequester() }
|
|
val cancelButtonFocusRequester = remember { FocusRequester() }
|
|
|
|
LaunchedEffect(Unit) {
|
|
urlFocusRequester.requestFocus()
|
|
}
|
|
|
|
Column(
|
|
modifier = Modifier.fillMaxWidth()
|
|
) {
|
|
Text(
|
|
"Clone a new repository",
|
|
color = MaterialTheme.colors.primaryTextColor,
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.padding(vertical = 4.dp, horizontal = 8.dp)
|
|
)
|
|
|
|
OutlinedTextField(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.padding(vertical = 4.dp, horizontal = 8.dp)
|
|
.focusOrder(urlFocusRequester) {
|
|
previous = cancelButtonFocusRequester
|
|
next = directoryFocusRequester
|
|
},
|
|
label = { Text("URL") },
|
|
textStyle = TextStyle(fontSize = 14.sp, color = MaterialTheme.colors.primaryTextColor),
|
|
maxLines = 1,
|
|
value = url,
|
|
onValueChange = {
|
|
errorHasBeenNoticed = true
|
|
url = it
|
|
}
|
|
)
|
|
|
|
Row(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.padding(vertical = 4.dp, horizontal = 8.dp),
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
) {
|
|
OutlinedTextField(
|
|
modifier = Modifier
|
|
.weight(1f)
|
|
.padding(end = 4.dp)
|
|
.focusOrder(directoryFocusRequester) {
|
|
previous = urlFocusRequester
|
|
next = directoryButtonFocusRequester
|
|
},
|
|
textStyle = TextStyle(fontSize = 14.sp, color = MaterialTheme.colors.primaryTextColor),
|
|
maxLines = 1,
|
|
label = { Text("Directory") },
|
|
value = directory,
|
|
onValueChange = {
|
|
errorHasBeenNoticed = true
|
|
directory = it
|
|
}
|
|
)
|
|
|
|
IconButton(
|
|
onClick = {
|
|
errorHasBeenNoticed = true
|
|
val newDirectory = openDirectoryDialog()
|
|
if (newDirectory != null)
|
|
directory = newDirectory
|
|
},
|
|
modifier = Modifier
|
|
.focusOrder(directoryButtonFocusRequester) {
|
|
previous = directoryFocusRequester
|
|
next = cloneButtonFocusRequester
|
|
}
|
|
) {
|
|
Icon(
|
|
Icons.Default.Search,
|
|
contentDescription = null,
|
|
tint = MaterialTheme.colors.primaryTextColor,
|
|
)
|
|
}
|
|
}
|
|
|
|
AnimatedVisibility(errorMessage != null && !errorHasBeenNoticed) {
|
|
Box(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.padding(vertical = 4.dp, horizontal = 8.dp)
|
|
.clip(RoundedCornerShape(4.dp))
|
|
.background(MaterialTheme.colors.error)
|
|
) {
|
|
Text(
|
|
errorMessage.orEmpty(),
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.padding(vertical = 4.dp, horizontal = 8.dp),
|
|
color = MaterialTheme.colors.onError,
|
|
)
|
|
}
|
|
|
|
}
|
|
|
|
Row(
|
|
modifier = Modifier
|
|
.padding(top = 16.dp)
|
|
.align(Alignment.End)
|
|
) {
|
|
TextButton(
|
|
modifier = Modifier
|
|
.padding(end = 8.dp)
|
|
.focusOrder(cancelButtonFocusRequester) {
|
|
previous = cloneButtonFocusRequester
|
|
next = urlFocusRequester
|
|
},
|
|
onClick = {
|
|
onClose()
|
|
}
|
|
) {
|
|
Text("Cancel")
|
|
}
|
|
PrimaryButton(
|
|
onClick = {
|
|
cloneViewModel.clone(directory, url)
|
|
},
|
|
modifier = Modifier
|
|
.focusOrder(cloneButtonFocusRequester) {
|
|
previous = directoryButtonFocusRequester
|
|
next = cancelButtonFocusRequester
|
|
},
|
|
text = "Clone"
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
private fun Cloning(cloneViewModel: CloneViewModel, cloneStatusValue: CloneStatus.Cloning) {
|
|
Column (
|
|
modifier = Modifier
|
|
.fillMaxWidth(),
|
|
horizontalAlignment = Alignment.CenterHorizontally,
|
|
) {
|
|
|
|
|
|
val progress = remember(cloneStatusValue) {
|
|
val total = cloneStatusValue.total
|
|
|
|
if(total == 0) // Prevent division by 0
|
|
-1f
|
|
else
|
|
cloneStatusValue.progress / total.toFloat()
|
|
}
|
|
|
|
if(progress >= 0f)
|
|
CircularProgressIndicator(
|
|
modifier = Modifier.padding(vertical = 16.dp),
|
|
progress = progress
|
|
)
|
|
else // Show indeterminate if we do not know the total (aka total == 0 or progress == -1)
|
|
CircularProgressIndicator(
|
|
modifier = Modifier.padding(vertical = 16.dp),
|
|
)
|
|
|
|
Text(cloneStatusValue.taskName, color = MaterialTheme.colors.primaryTextColor)
|
|
|
|
TextButton(
|
|
modifier = Modifier
|
|
.padding(
|
|
top = 36.dp,
|
|
end = 8.dp
|
|
)
|
|
.align(Alignment.End),
|
|
onClick = {
|
|
cloneViewModel.cancelClone()
|
|
}
|
|
) {
|
|
Text("Cancel")
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
private fun Cancelling() {
|
|
Column (
|
|
modifier = Modifier
|
|
.fillMaxWidth(),
|
|
horizontalAlignment = Alignment.CenterHorizontally,
|
|
) {
|
|
CircularProgressIndicator(
|
|
modifier = Modifier.padding(horizontal = 16.dp)
|
|
)
|
|
|
|
Text(
|
|
text = "Cancelling clone operation...",
|
|
color = MaterialTheme.colors.primaryTextColor,
|
|
modifier = Modifier.padding(vertical = 16.dp),
|
|
)
|
|
}
|
|
} |