Added option to specify folder name when clonning a repository
Closes #96
This commit is contained in:
parent
f98caf9890
commit
c8653233f4
@ -6,7 +6,7 @@ fun File.openDirectory(dirName: String): File {
|
|||||||
val newDir = File(this, dirName)
|
val newDir = File(this, dirName)
|
||||||
|
|
||||||
if (!newDir.exists() || !newDir.isDirectory) {
|
if (!newDir.exists() || !newDir.isDirectory) {
|
||||||
newDir.mkdir()
|
newDir.mkdirs()
|
||||||
}
|
}
|
||||||
|
|
||||||
return newDir
|
return newDir
|
||||||
|
@ -7,16 +7,11 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
|||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.Search
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.focus.FocusProperties
|
import androidx.compose.ui.focus.*
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
|
||||||
import androidx.compose.ui.focus.focusProperties
|
|
||||||
import androidx.compose.ui.focus.focusRequester
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.Shape
|
import androidx.compose.ui.graphics.Shape
|
||||||
import androidx.compose.ui.res.painterResource
|
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.extensions.handOnHover
|
||||||
import com.jetpackduba.gitnuro.git.CloneState
|
import com.jetpackduba.gitnuro.git.CloneState
|
||||||
import com.jetpackduba.gitnuro.theme.outlinedTextFieldColors
|
import com.jetpackduba.gitnuro.theme.outlinedTextFieldColors
|
||||||
|
|
||||||
import com.jetpackduba.gitnuro.theme.textButtonColors
|
import com.jetpackduba.gitnuro.theme.textButtonColors
|
||||||
import com.jetpackduba.gitnuro.ui.components.AdjustableOutlinedTextField
|
import com.jetpackduba.gitnuro.ui.components.AdjustableOutlinedTextField
|
||||||
import com.jetpackduba.gitnuro.ui.components.PrimaryButton
|
import com.jetpackduba.gitnuro.ui.components.PrimaryButton
|
||||||
@ -85,12 +79,14 @@ private fun CloneDialogView(
|
|||||||
) {
|
) {
|
||||||
var url by remember(cloneViewModel) { mutableStateOf(cloneViewModel.repositoryUrl.value) }
|
var url by remember(cloneViewModel) { mutableStateOf(cloneViewModel.repositoryUrl.value) }
|
||||||
var directory by remember(cloneViewModel) { mutableStateOf(cloneViewModel.directoryPath.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) }
|
var cloneSubmodules by remember { mutableStateOf(true) }
|
||||||
|
|
||||||
val error by cloneViewModel.error.collectAsState()
|
val error by cloneViewModel.error.collectAsState()
|
||||||
|
|
||||||
val urlFocusRequester = remember { FocusRequester() }
|
val urlFocusRequester = remember { FocusRequester() }
|
||||||
val directoryFocusRequester = remember { FocusRequester() }
|
val directoryFocusRequester = remember { FocusRequester() }
|
||||||
|
val folderFocusRequester = remember { FocusRequester() }
|
||||||
val directoryButtonFocusRequester = remember { FocusRequester() }
|
val directoryButtonFocusRequester = remember { FocusRequester() }
|
||||||
val cloneButtonFocusRequester = remember { FocusRequester() }
|
val cloneButtonFocusRequester = remember { FocusRequester() }
|
||||||
val cancelButtonFocusRequester = remember { FocusRequester() }
|
val cancelButtonFocusRequester = remember { FocusRequester() }
|
||||||
@ -109,7 +105,11 @@ private fun CloneDialogView(
|
|||||||
)
|
)
|
||||||
|
|
||||||
TextInput(
|
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",
|
title = "URL",
|
||||||
value = url,
|
value = url,
|
||||||
focusRequester = urlFocusRequester,
|
focusRequester = urlFocusRequester,
|
||||||
@ -124,11 +124,6 @@ private fun CloneDialogView(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth(),
|
|
||||||
verticalAlignment = Alignment.Bottom,
|
|
||||||
) {
|
|
||||||
TextInput(
|
TextInput(
|
||||||
modifier = Modifier.padding(top = 16.dp),
|
modifier = Modifier.padding(top = 16.dp),
|
||||||
title = "Directory",
|
title = "Directory",
|
||||||
@ -159,7 +154,7 @@ private fun CloneDialogView(
|
|||||||
.focusRequester(directoryButtonFocusRequester)
|
.focusRequester(directoryButtonFocusRequester)
|
||||||
.focusProperties {
|
.focusProperties {
|
||||||
previous = directoryFocusRequester
|
previous = directoryFocusRequester
|
||||||
next = cloneButtonFocusRequester
|
next = folderFocusRequester
|
||||||
}
|
}
|
||||||
.handOnHover()
|
.handOnHover()
|
||||||
.size(40.dp),
|
.size(40.dp),
|
||||||
@ -173,8 +168,21 @@ private fun CloneDialogView(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
@ -204,7 +212,7 @@ private fun CloneDialogView(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimatedVisibility (error.isNotBlank()) {
|
AnimatedVisibility(error.isNotBlank()) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@ -241,7 +249,7 @@ private fun CloneDialogView(
|
|||||||
)
|
)
|
||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
cloneViewModel.clone(directory.text, url.text, cloneSubmodules)
|
cloneViewModel.clone(directory.text, url.text, folder.text, cloneSubmodules)
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.focusRequester(cloneButtonFocusRequester)
|
.focusRequester(cloneButtonFocusRequester)
|
||||||
|
@ -26,6 +26,9 @@ class CloneViewModel @Inject constructor(
|
|||||||
private val _directoryPath = MutableStateFlow(TextFieldValue(""))
|
private val _directoryPath = MutableStateFlow(TextFieldValue(""))
|
||||||
val directoryPath = _directoryPath.asStateFlow()
|
val directoryPath = _directoryPath.asStateFlow()
|
||||||
|
|
||||||
|
private val _folder = MutableStateFlow(TextFieldValue(""))
|
||||||
|
val folder = _folder.asStateFlow()
|
||||||
|
|
||||||
private val _cloneState = MutableStateFlow<CloneState>(CloneState.None)
|
private val _cloneState = MutableStateFlow<CloneState>(CloneState.None)
|
||||||
val cloneState = _cloneState.asStateFlow()
|
val cloneState = _cloneState.asStateFlow()
|
||||||
|
|
||||||
@ -34,7 +37,7 @@ class CloneViewModel @Inject constructor(
|
|||||||
|
|
||||||
private var cloneJob: Job? = null
|
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 {
|
cloneJob = tabState.safeProcessingWithoutGit {
|
||||||
if (directoryPath.isBlank()) {
|
if (directoryPath.isBlank()) {
|
||||||
_error.value = "Invalid empty directory"
|
_error.value = "Invalid empty directory"
|
||||||
@ -46,6 +49,11 @@ class CloneViewModel @Inject constructor(
|
|||||||
return@safeProcessingWithoutGit
|
return@safeProcessingWithoutGit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (folder.isBlank()) {
|
||||||
|
_error.value = "Invalid empty folder name"
|
||||||
|
return@safeProcessingWithoutGit
|
||||||
|
}
|
||||||
|
|
||||||
val urlSplit = url.split("/", "\\").toMutableList()
|
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/
|
// 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()
|
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)
|
val directory = File(directoryPath)
|
||||||
|
|
||||||
if (!directory.exists()) {
|
if (!directory.exists()) {
|
||||||
directory.mkdirs()
|
directory.mkdirs()
|
||||||
}
|
}
|
||||||
|
|
||||||
val repoDir = File(directory, repoName)
|
val repoDir = File(directory, folder)
|
||||||
if (!repoDir.exists()) {
|
if (!repoDir.exists()) {
|
||||||
repoDir.mkdir()
|
repoDir.mkdir()
|
||||||
}
|
}
|
||||||
@ -99,6 +95,18 @@ class CloneViewModel @Inject constructor(
|
|||||||
_error.value = ""
|
_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? {
|
fun openDirectoryPicker(): String? {
|
||||||
return openFilePickerUseCase(PickerType.DIRECTORIES, null)
|
return openFilePickerUseCase(PickerType.DIRECTORIES, null)
|
||||||
}
|
}
|
||||||
@ -110,4 +118,8 @@ class CloneViewModel @Inject constructor(
|
|||||||
fun onRepositoryUrlChanged(repositoryUrl: TextFieldValue) {
|
fun onRepositoryUrlChanged(repositoryUrl: TextFieldValue) {
|
||||||
_repositoryUrl.value = repositoryUrl
|
_repositoryUrl.value = repositoryUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onFolderNameChanged(folderName: TextFieldValue) {
|
||||||
|
_folder.value = folderName
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user