Added option to specify folder name when clonning a repository

Closes #96
This commit is contained in:
Abdelilah El Aissaoui 2023-05-26 00:16:27 +02:00
parent f98caf9890
commit c8653233f4
No known key found for this signature in database
GPG Key ID: 7587FC860F594869
3 changed files with 93 additions and 73 deletions

View File

@ -6,7 +6,7 @@ fun File.openDirectory(dirName: String): File {
val newDir = File(this, dirName)
if (!newDir.exists() || !newDir.isDirectory) {
newDir.mkdir()
newDir.mkdirs()
}
return newDir

View File

@ -7,16 +7,11 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
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.FocusProperties
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusProperties
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.res.painterResource
@ -29,7 +24,6 @@ import com.jetpackduba.gitnuro.extensions.handMouseClickable
import com.jetpackduba.gitnuro.extensions.handOnHover
import com.jetpackduba.gitnuro.git.CloneState
import com.jetpackduba.gitnuro.theme.outlinedTextFieldColors
import com.jetpackduba.gitnuro.theme.textButtonColors
import com.jetpackduba.gitnuro.ui.components.AdjustableOutlinedTextField
import com.jetpackduba.gitnuro.ui.components.PrimaryButton
@ -85,12 +79,14 @@ private fun CloneDialogView(
) {
var url by remember(cloneViewModel) { mutableStateOf(cloneViewModel.repositoryUrl.value) }
var directory by remember(cloneViewModel) { mutableStateOf(cloneViewModel.directoryPath.value) }
var folder by remember(cloneViewModel) { mutableStateOf(cloneViewModel.folder.value) }
var cloneSubmodules by remember { mutableStateOf(true) }
val error by cloneViewModel.error.collectAsState()
val urlFocusRequester = remember { FocusRequester() }
val directoryFocusRequester = remember { FocusRequester() }
val folderFocusRequester = remember { FocusRequester() }
val directoryButtonFocusRequester = remember { FocusRequester() }
val cloneButtonFocusRequester = remember { FocusRequester() }
val cancelButtonFocusRequester = remember { FocusRequester() }
@ -109,7 +105,11 @@ private fun CloneDialogView(
)
TextInput(
modifier = Modifier.padding(top = 8.dp),
modifier = Modifier.padding(top = 8.dp).onFocusChanged {
if (!it.hasFocus) {
folder = TextFieldValue(cloneViewModel.repoName(url.text))
}
},
title = "URL",
value = url,
focusRequester = urlFocusRequester,
@ -124,57 +124,65 @@ private fun CloneDialogView(
}
)
Row(
modifier = Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.Bottom,
) {
TextInput(
modifier = Modifier.padding(top = 16.dp),
title = "Directory",
value = directory,
focusRequester = directoryFocusRequester,
focusProperties = {
previous = urlFocusRequester
next = directoryButtonFocusRequester
},
onValueChange = {
directory = it
cloneViewModel.onDirectoryPathChanged(directory)
cloneViewModel.resetStateIfError()
},
trailingIcon = {
IconButton(
onClick = {
TextInput(
modifier = Modifier.padding(top = 16.dp),
title = "Directory",
value = directory,
focusRequester = directoryFocusRequester,
focusProperties = {
previous = urlFocusRequester
next = directoryButtonFocusRequester
},
onValueChange = {
directory = it
cloneViewModel.onDirectoryPathChanged(directory)
cloneViewModel.resetStateIfError()
},
trailingIcon = {
IconButton(
onClick = {
cloneViewModel.resetStateIfError()
val newDirectory = cloneViewModel.openDirectoryPicker()
if (newDirectory != null) {
directory = TextFieldValue(newDirectory, selection = TextRange(newDirectory.count()))
cloneViewModel.onDirectoryPathChanged(directory)
cloneViewModel.resetStateIfError()
val newDirectory = cloneViewModel.openDirectoryPicker()
if (newDirectory != null) {
directory = TextFieldValue(newDirectory, selection = TextRange(newDirectory.count()))
cloneViewModel.onDirectoryPathChanged(directory)
cloneViewModel.resetStateIfError()
directoryFocusRequester.requestFocus()
}
},
modifier = Modifier
.focusRequester(directoryButtonFocusRequester)
.focusProperties {
previous = directoryFocusRequester
next = cloneButtonFocusRequester
}
.handOnHover()
.size(40.dp),
) {
Icon(
painterResource(AppIcons.SEARCH),
contentDescription = "Search",
tint = MaterialTheme.colors.onBackground,
)
}
directoryFocusRequester.requestFocus()
}
},
modifier = Modifier
.focusRequester(directoryButtonFocusRequester)
.focusProperties {
previous = directoryFocusRequester
next = folderFocusRequester
}
.handOnHover()
.size(40.dp),
) {
Icon(
painterResource(AppIcons.SEARCH),
contentDescription = "Search",
tint = MaterialTheme.colors.onBackground,
)
}
)
}
)
}
TextInput(
modifier = Modifier.padding(top = 16.dp),
title = "Folder",
value = folder,
focusRequester = folderFocusRequester,
focusProperties = {
previous = cancelButtonFocusRequester
next = directoryFocusRequester
},
onValueChange = { folderName ->
folder = folderName
cloneViewModel.onFolderNameChanged(folderName)
cloneViewModel.resetStateIfError()
}
)
Row(
verticalAlignment = Alignment.CenterVertically,
@ -204,7 +212,7 @@ private fun CloneDialogView(
)
}
AnimatedVisibility (error.isNotBlank()) {
AnimatedVisibility(error.isNotBlank()) {
Box(
modifier = Modifier
.fillMaxWidth()
@ -241,7 +249,7 @@ private fun CloneDialogView(
)
PrimaryButton(
onClick = {
cloneViewModel.clone(directory.text, url.text, cloneSubmodules)
cloneViewModel.clone(directory.text, url.text, folder.text, cloneSubmodules)
},
modifier = Modifier
.focusRequester(cloneButtonFocusRequester)

View File

@ -26,6 +26,9 @@ class CloneViewModel @Inject constructor(
private val _directoryPath = MutableStateFlow(TextFieldValue(""))
val directoryPath = _directoryPath.asStateFlow()
private val _folder = MutableStateFlow(TextFieldValue(""))
val folder = _folder.asStateFlow()
private val _cloneState = MutableStateFlow<CloneState>(CloneState.None)
val cloneState = _cloneState.asStateFlow()
@ -34,7 +37,7 @@ class CloneViewModel @Inject constructor(
private var cloneJob: Job? = null
fun clone(directoryPath: String, url: String, cloneSubmodules: Boolean) {
fun clone(directoryPath: String, url: String, folder: String, cloneSubmodules: Boolean) {
cloneJob = tabState.safeProcessingWithoutGit {
if (directoryPath.isBlank()) {
_error.value = "Invalid empty directory"
@ -46,6 +49,11 @@ class CloneViewModel @Inject constructor(
return@safeProcessingWithoutGit
}
if (folder.isBlank()) {
_error.value = "Invalid empty folder name"
return@safeProcessingWithoutGit
}
val urlSplit = url.split("/", "\\").toMutableList()
// Removes the last element for URLs that end with "/" or "\" instead of the repo name like https://github.com/JetpackDuba/Gitnuro/
@ -53,25 +61,13 @@ class CloneViewModel @Inject constructor(
urlSplit.removeLast()
}
// Take the last element of the path/URL to generate obtain the repo name
var repoName = urlSplit.lastOrNull()
if (repoName?.endsWith(".git") == true) {
repoName = repoName.removeSuffix(".git")
}
if (repoName.isNullOrBlank()) {
_error.value = "Check your URL and try again"
return@safeProcessingWithoutGit
}
val directory = File(directoryPath)
if (!directory.exists()) {
directory.mkdirs()
}
val repoDir = File(directory, repoName)
val repoDir = File(directory, folder)
if (!repoDir.exists()) {
repoDir.mkdir()
}
@ -99,6 +95,18 @@ class CloneViewModel @Inject constructor(
_error.value = ""
}
fun repoName(url: String): String {
val urlSplit = url.split("/", "\\").toMutableList()
// Removes the last element for URLs that end with "/" or "\" instead of the repo name like https://github.com/JetpackDuba/Gitnuro/
if (urlSplit.isNotEmpty() && urlSplit.last().isBlank()) {
urlSplit.removeLast()
}
// Take the last element of the path/URL to generate obtain the repo name
return urlSplit.lastOrNull()?.removeSuffix(".git").orEmpty()
}
fun openDirectoryPicker(): String? {
return openFilePickerUseCase(PickerType.DIRECTORIES, null)
}
@ -110,4 +118,8 @@ class CloneViewModel @Inject constructor(
fun onRepositoryUrlChanged(repositoryUrl: TextFieldValue) {
_repositoryUrl.value = repositoryUrl
}
fun onFolderNameChanged(folderName: TextFieldValue) {
_folder.value = folderName
}
}