diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/git/CloneStatus.kt b/src/main/kotlin/com/jetpackduba/gitnuro/git/CloneState.kt similarity index 83% rename from src/main/kotlin/com/jetpackduba/gitnuro/git/CloneStatus.kt rename to src/main/kotlin/com/jetpackduba/gitnuro/git/CloneState.kt index 335d106..518ca89 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/git/CloneStatus.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/git/CloneState.kt @@ -3,12 +3,12 @@ package com.jetpackduba.gitnuro.git import org.eclipse.jgit.transport.RemoteRefUpdate import java.io.File -sealed class CloneStatus { - object None : CloneStatus() - data class Cloning(val taskName: String, val progress: Int, val total: Int) : CloneStatus() - object Cancelling : CloneStatus() - data class Fail(val reason: String) : CloneStatus() - data class Completed(val repoDir: File) : CloneStatus() +sealed class CloneState { + object None : CloneState() + data class Cloning(val taskName: String, val progress: Int, val total: Int) : CloneState() + object Cancelling : CloneState() + data class Fail(val reason: String) : CloneState() + data class Completed(val repoDir: File) : CloneState() } val RemoteRefUpdate.Status.isRejected: Boolean diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/git/remote_operations/CloneRepositoryUseCase.kt b/src/main/kotlin/com/jetpackduba/gitnuro/git/remote_operations/CloneRepositoryUseCase.kt index b18031b..6117105 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/git/remote_operations/CloneRepositoryUseCase.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/git/remote_operations/CloneRepositoryUseCase.kt @@ -1,6 +1,6 @@ package com.jetpackduba.gitnuro.git.remote_operations -import com.jetpackduba.gitnuro.git.CloneStatus +import com.jetpackduba.gitnuro.git.CloneState import com.jetpackduba.gitnuro.logging.printDebug import kotlinx.coroutines.CancellationException import kotlinx.coroutines.channels.awaitClose @@ -18,14 +18,14 @@ private const val TAG = "CloneRepositoryUseCase" class CloneRepositoryUseCase @Inject constructor( private val handleTransportUseCase: HandleTransportUseCase, ) { - operator fun invoke(directory: File, url: String, cloneSubmodules: Boolean): Flow = callbackFlow { + operator fun invoke(directory: File, url: String, cloneSubmodules: Boolean): Flow = callbackFlow { var lastTitle: String = "" var lastTotalWork = 0 var progress = 0 try { ensureActive() - trySend(CloneStatus.Cloning("Starting...", progress, lastTotalWork)) + trySend(CloneState.Cloning("Starting...", progress, lastTotalWork)) Git.cloneRepository() .setDirectory(directory) @@ -41,7 +41,7 @@ class CloneRepositoryUseCase @Inject constructor( lastTitle = title.orEmpty() lastTotalWork = totalWork progress = 0 - trySend(CloneStatus.Cloning(lastTitle, progress, lastTotalWork)) + trySend(CloneState.Cloning(lastTitle, progress, lastTotalWork)) } override fun update(completed: Int) { @@ -49,7 +49,7 @@ class CloneRepositoryUseCase @Inject constructor( ensureActive() progress += completed - trySend(CloneStatus.Cloning(lastTitle, progress, lastTotalWork)) + trySend(CloneState.Cloning(lastTitle, progress, lastTotalWork)) } override fun endTask() { @@ -68,13 +68,13 @@ class CloneRepositoryUseCase @Inject constructor( .call() ensureActive() - trySend(CloneStatus.Completed(directory)) + trySend(CloneState.Completed(directory)) channel.close() } catch (ex: Exception) { if (ex.cause?.cause is CancellationException) { printDebug(TAG, "Clone cancelled") } else { - trySend(CloneStatus.Fail(ex.localizedMessage)) + trySend(CloneState.Fail(ex.localizedMessage)) } channel.close() diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/dialogs/CloneDialog.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/dialogs/CloneDialog.kt index 856d5bb..4c3c6e2 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/dialogs/CloneDialog.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/dialogs/CloneDialog.kt @@ -1,5 +1,6 @@ package com.jetpackduba.gitnuro.ui.dialogs +import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize import androidx.compose.foundation.background import androidx.compose.foundation.interaction.MutableInteractionSource @@ -20,7 +21,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.unit.dp import com.jetpackduba.gitnuro.extensions.handMouseClickable -import com.jetpackduba.gitnuro.git.CloneStatus +import com.jetpackduba.gitnuro.git.CloneState import com.jetpackduba.gitnuro.theme.outlinedTextFieldColors import com.jetpackduba.gitnuro.theme.textButtonColors @@ -36,7 +37,7 @@ fun CloneDialog( onClose: () -> Unit, onOpenRepository: (File) -> Unit, ) { - val cloneStatus = cloneViewModel.cloneStatus.collectAsState() + val cloneStatus = cloneViewModel.cloneState.collectAsState() val cloneStatusValue = cloneStatus.value MaterialDialog( @@ -46,30 +47,29 @@ fun CloneDialog( Box( modifier = Modifier .width(720.dp) - .height(280.dp) .animateContentSize() ) { when (cloneStatusValue) { - is CloneStatus.Cloning -> { + is CloneState.Cloning -> { Cloning(cloneViewModel, cloneStatusValue) } - is CloneStatus.Cancelling -> { + is CloneState.Cancelling -> { Cancelling() } - is CloneStatus.Completed -> { + is CloneState.Completed -> { onOpenRepository(cloneStatusValue.repoDir) onClose() } - is CloneStatus.Fail -> CloneInput( + is CloneState.Fail -> CloneInput( cloneViewModel = cloneViewModel, onClose = onClose, errorMessage = cloneStatusValue.reason ) - CloneStatus.None -> CloneInput( + CloneState.None -> CloneInput( cloneViewModel = cloneViewModel, onClose = onClose, ) @@ -94,10 +94,6 @@ private fun CloneInput( val cloneButtonFocusRequester = remember { FocusRequester() } val cancelButtonFocusRequester = remember { FocusRequester() } - LaunchedEffect(Unit) { - urlFocusRequester.requestFocus() - } - Column( modifier = Modifier.fillMaxWidth() .padding(horizontal = 8.dp) @@ -133,7 +129,7 @@ private fun CloneInput( ) { TextInput( - modifier = Modifier.weight(1f), + modifier = Modifier.padding(top = 16.dp), title = "Directory", value = directory, focusRequester = directoryFocusRequester, @@ -204,7 +200,7 @@ private fun CloneInput( ) } - if (errorMessage != null) { + AnimatedVisibility (errorMessage != null) { Box( modifier = Modifier .fillMaxWidth() @@ -213,17 +209,15 @@ private fun CloneInput( .background(MaterialTheme.colors.error) ) { Text( - errorMessage, + errorMessage.orEmpty(), modifier = Modifier .fillMaxWidth() .padding(vertical = 4.dp, horizontal = 8.dp), color = MaterialTheme.colors.onError, ) } - } - Spacer(Modifier.weight(1f)) Row( modifier = Modifier .padding(top = 16.dp) @@ -258,18 +252,18 @@ private fun CloneInput( } @Composable -private fun Cloning(cloneViewModel: CloneViewModel, cloneStatusValue: CloneStatus.Cloning) { +private fun Cloning(cloneViewModel: CloneViewModel, cloneStateValue: CloneState.Cloning) { Box( modifier = Modifier - .fillMaxSize(), + .fillMaxWidth(), ) { - val progress = remember(cloneStatusValue) { - val total = cloneStatusValue.total + val progress = remember(cloneStateValue) { + val total = cloneStateValue.total if (total == 0) // Prevent division by 0 -1f else - cloneStatusValue.progress / total.toFloat() + cloneStateValue.progress / total.toFloat() } Column( @@ -278,7 +272,7 @@ private fun Cloning(cloneViewModel: CloneViewModel, cloneStatusValue: CloneStatu horizontalAlignment = Alignment.CenterHorizontally, ) { - Text(cloneStatusValue.taskName, color = MaterialTheme.colors.onBackground) + Text(cloneStateValue.taskName, color = MaterialTheme.colors.onBackground) if (progress >= 0f) CircularProgressIndicator( @@ -318,7 +312,7 @@ private fun Cloning(cloneViewModel: CloneViewModel, cloneStatusValue: CloneStatu private fun Cancelling() { Column( modifier = Modifier - .fillMaxSize(), + .fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) { @@ -346,23 +340,19 @@ private fun TextInput( onValueChange: (String) -> Unit, textFieldShape: Shape = RoundedCornerShape(4.dp), ) { - Row( - modifier = modifier - .padding(vertical = 8.dp), - verticalAlignment = Alignment.CenterVertically + Column( + modifier = modifier, ) { Text( text = title, style = MaterialTheme.typography.body1, modifier = Modifier - .width(100.dp) - .padding(end = 16.dp), + .padding(bottom = 8.dp), ) AdjustableOutlinedTextField( value = value, modifier = Modifier - .weight(1f) .focusRequester(focusRequester) .focusProperties(focusProperties), enabled = enabled, diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/CloneViewModel.kt b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/CloneViewModel.kt index 398bda5..6386051 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/CloneViewModel.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/CloneViewModel.kt @@ -1,6 +1,6 @@ package com.jetpackduba.gitnuro.viewmodels -import com.jetpackduba.gitnuro.git.CloneStatus +import com.jetpackduba.gitnuro.git.CloneState import com.jetpackduba.gitnuro.git.TabState import com.jetpackduba.gitnuro.git.remote_operations.CloneRepositoryUseCase import com.jetpackduba.gitnuro.system.OpenFilePickerUseCase @@ -20,9 +20,9 @@ class CloneViewModel @Inject constructor( private val openFilePickerUseCase: OpenFilePickerUseCase, ) { - private val _cloneStatus = MutableStateFlow(CloneStatus.None) - val cloneStatus: StateFlow - get() = _cloneStatus + private val _cloneState = MutableStateFlow(CloneState.None) + val cloneState: StateFlow + get() = _cloneState var url: String = "" var directory: String = "" @@ -32,12 +32,12 @@ class CloneViewModel @Inject constructor( fun clone(directoryPath: String, url: String, cloneSubmodules: Boolean) { cloneJob = tabState.safeProcessingWithoutGit { if (directoryPath.isBlank()) { - _cloneStatus.value = CloneStatus.Fail("Invalid empty directory") + _cloneState.value = CloneState.Fail("Invalid empty directory") return@safeProcessingWithoutGit } if (url.isBlank()) { - _cloneStatus.value = CloneStatus.Fail("Invalid empty URL") + _cloneState.value = CloneState.Fail("Invalid empty URL") return@safeProcessingWithoutGit } @@ -57,7 +57,7 @@ class CloneViewModel @Inject constructor( } if (repoName.isNullOrBlank()) { - _cloneStatus.value = CloneStatus.Fail("Check your URL and try again") + _cloneState.value = CloneState.Fail("Check your URL and try again") return@safeProcessingWithoutGit } @@ -75,25 +75,25 @@ class CloneViewModel @Inject constructor( cloneRepositoryUseCase(repoDir, url, cloneSubmodules) .flowOn(Dispatchers.IO) .collect { newCloneStatus -> - _cloneStatus.value = newCloneStatus + _cloneState.value = newCloneStatus } } } fun reset() { - _cloneStatus.value = CloneStatus.None + _cloneState.value = CloneState.None url = "" directory = "" } fun cancelClone() = tabState.safeProcessingWithoutGit { - _cloneStatus.value = CloneStatus.Cancelling + _cloneState.value = CloneState.Cancelling cloneJob?.cancelAndJoin() - _cloneStatus.value = CloneStatus.None + _cloneState.value = CloneState.None } fun resetStateIfError() { - _cloneStatus.value = CloneStatus.None + _cloneState.value = CloneState.None } fun openDirectoryPicker(): String? {