Gitnuro/src/main/kotlin/com/jetpackduba/gitnuro/ui/dialogs/EditRemotesDialog.kt
2023-03-08 01:41:10 +01:00

371 lines
16 KiB
Kotlin

package com.jetpackduba.gitnuro.ui.dialogs
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Clear
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.jetpackduba.gitnuro.AppIcons
import com.jetpackduba.gitnuro.extensions.handMouseClickable
import com.jetpackduba.gitnuro.extensions.handOnHover
import com.jetpackduba.gitnuro.theme.backgroundSelected
import com.jetpackduba.gitnuro.theme.onBackgroundSecondary
import com.jetpackduba.gitnuro.theme.outlinedTextFieldColors
import com.jetpackduba.gitnuro.ui.components.AdjustableOutlinedTextField
import com.jetpackduba.gitnuro.ui.components.PrimaryButton
import com.jetpackduba.gitnuro.viewmodels.sidepanel.RemotesViewModel
import org.eclipse.jgit.transport.RemoteConfig
@Composable
fun EditRemotesDialog(
remotesViewModel: RemotesViewModel,
onDismiss: () -> Unit,
) {
var remotesEditorData by remember {
mutableStateOf(
RemotesEditorData(
emptyList(),
null,
)
)
}
val remotesState by remotesViewModel.remoteState.collectAsState()
val remotes = remotesState.remotes
var remoteChanged by remember { mutableStateOf(false) }
val selectedRemote = remotesEditorData.selectedRemote
LaunchedEffect(remotes) {
val newRemotesWrappers = remotes.map {
val remoteConfig = it.remoteInfo.remoteConfig
remoteConfig.toRemoteWrapper()
}
val safeSelectedRemote = remotesEditorData.selectedRemote
var newSelectedRemote: RemoteWrapper? = null
if (safeSelectedRemote != null) {
newSelectedRemote = newRemotesWrappers.firstOrNull { it.remoteName == safeSelectedRemote.remoteName }
}
remoteChanged = newSelectedRemote?.haveUrisChanged ?: false
remotesEditorData = remotesEditorData.copy(
listRemotes = newRemotesWrappers,
selectedRemote = newSelectedRemote,
)
}
MaterialDialog(
paddingVertical = 8.dp,
paddingHorizontal = 16.dp,
background = MaterialTheme.colors.surface,
onCloseRequested = onDismiss
) {
Column(
modifier = Modifier
.size(width = 900.dp, height = 600.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Row(
modifier = Modifier,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = "Remotes",
color = MaterialTheme.colors.onBackground,
style = MaterialTheme.typography.h3,
modifier = Modifier.padding(vertical = 8.dp),
)
Spacer(modifier = Modifier.weight(1f))
IconButton(
onClick = onDismiss,
modifier = Modifier
.handOnHover()
) {
Icon(
imageVector = Icons.Default.Clear,
contentDescription = null,
tint = MaterialTheme.colors.onBackground,
)
}
}
Row(
modifier = Modifier
.padding(bottom = 8.dp)
) {
Column(
modifier = Modifier
.width(200.dp)
.fillMaxHeight()
.background(MaterialTheme.colors.background),
) {
if (remotesEditorData.listRemotes.isNotEmpty()) {
LazyColumn(
modifier = Modifier.weight(1f)
) {
items(remotesEditorData.listRemotes) { remote ->
val background = if (remote == selectedRemote) {
MaterialTheme.colors.backgroundSelected
} else
MaterialTheme.colors.background
Text(
text = remote.remoteName,
color = MaterialTheme.colors.onBackground,
modifier = Modifier
.fillMaxWidth()
.handMouseClickable {
remotesEditorData = remotesEditorData.copy(selectedRemote = remote)
}
.background(background)
.padding(horizontal = 16.dp, vertical = 8.dp)
)
}
}
} else {
Box(
modifier = Modifier
.weight(1f)
.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
Text(
text = "No available remotes",
style = MaterialTheme.typography.body2.copy(color = MaterialTheme.colors.onBackgroundSecondary)
)
}
}
Row(
modifier = Modifier
.fillMaxWidth()
) {
IconButton(
modifier = Modifier
.size(36.dp)
.handOnHover(),
onClick = {
val remotesWithNew = remotesEditorData.listRemotes.toMutableList()
val newRemote = RemoteWrapper(
remoteName = "new_remote",
fetchUri = "",
originalFetchUri = "",
pushUri = "",
originalPushUri = "",
isNew = true
)
remotesWithNew.add(newRemote)
remotesEditorData = remotesEditorData.copy(
listRemotes = remotesWithNew,
selectedRemote = newRemote
)
}
) {
Icon(
painter = painterResource(AppIcons.ADD),
contentDescription = null,
tint = MaterialTheme.colors.onBackground,
)
}
IconButton(
modifier = Modifier
.size(36.dp)
.handOnHover(),
enabled = selectedRemote != null,
onClick = {
if (selectedRemote != null)
remotesViewModel.deleteRemote(selectedRemote.remoteName, selectedRemote.isNew)
}
) {
Icon(
painter = painterResource(AppIcons.REMOVE),
contentDescription = null,
tint = if (selectedRemote != null)
MaterialTheme.colors.onBackground
else
MaterialTheme.colors.onBackgroundSecondary,
)
}
}
}
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
if (selectedRemote != null) {
Column {
if (selectedRemote.isNew) {
Text(
text = "New remote name",
color = MaterialTheme.colors.onBackground,
modifier = Modifier.padding(top = 8.dp),
)
AdjustableOutlinedTextField(
value = selectedRemote.remoteName,
onValueChange = { newValue ->
val newSelectedRemoteConfig = selectedRemote.copy(remoteName = newValue)
val listRemotes = remotesEditorData.listRemotes.toMutableList()
val newRemoteToBeReplacedIndex = listRemotes.indexOfFirst { it.isNew }
listRemotes[newRemoteToBeReplacedIndex] = newSelectedRemoteConfig
remotesEditorData = remotesEditorData.copy(
listRemotes = listRemotes,
selectedRemote = newSelectedRemoteConfig
)
},
singleLine = true,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
)
}
Text(
text = "Fetch URL",
color = MaterialTheme.colors.onBackground,
modifier = Modifier.padding(top = 8.dp),
)
AdjustableOutlinedTextField(
value = selectedRemote.fetchUri,
onValueChange = { newValue ->
val newSelectedRemoteConfig = selectedRemote.copy(fetchUri = newValue)
remotesEditorData = remotesEditorData.copy(selectedRemote = newSelectedRemoteConfig)
remoteChanged = newSelectedRemoteConfig.haveUrisChanged
},
singleLine = true,
colors = outlinedTextFieldColors(),
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
)
Text(
text = "Push URL",
color = MaterialTheme.colors.onBackground,
modifier = Modifier.padding(top = 8.dp),
)
AdjustableOutlinedTextField(
value = selectedRemote.pushUri,
onValueChange = { newValue ->
val newSelectedRemoteConfig = selectedRemote.copy(pushUri = newValue)
remotesEditorData = remotesEditorData.copy(selectedRemote = newSelectedRemoteConfig)
remoteChanged = newSelectedRemoteConfig.haveUrisChanged
},
singleLine = true,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
)
Spacer(modifier = Modifier.weight(1f))
Row(
modifier = Modifier
.padding(top = 16.dp)
.align(Alignment.End),
verticalAlignment = Alignment.CenterVertically,
) {
Spacer(modifier = Modifier.weight(1f))
if (!selectedRemote.isNew) {
PrimaryButton(
text = "Discard changes",
modifier = Modifier.padding(end = 8.dp),
onClick = {
remotesEditorData = remotesEditorData.copy(
selectedRemote = selectedRemote.copy(
fetchUri = selectedRemote.originalFetchUri,
pushUri = selectedRemote.originalPushUri,
)
)
remoteChanged = false
},
backgroundColor = Color.Transparent,
backgroundDisabled = Color.Transparent,
enabled = remoteChanged,
textColor = MaterialTheme.colors.onBackground,
disabledTextColor = MaterialTheme.colors.onBackground.copy(alpha = 0.5f),
)
}
val text = if (selectedRemote.isNew)
"Add new remote"
else
"Save ${selectedRemote.remoteName} changes"
PrimaryButton(
modifier = Modifier,
enabled = remoteChanged,
onClick = {
if (selectedRemote.isNew)
remotesViewModel.addRemote(selectedRemote)
else
remotesViewModel.updateRemote(selectedRemote)
},
text = text,
)
}
}
}
}
}
}
}
}
data class RemoteWrapper(
val remoteName: String,
val fetchUri: String,
val originalFetchUri: String,
val pushUri: String,
val originalPushUri: String,
val isNew: Boolean = false,
) {
val haveUrisChanged: Boolean = isNew ||
fetchUri != originalFetchUri ||
pushUri != originalPushUri
}
data class RemotesEditorData(
val listRemotes: List<RemoteWrapper>,
val selectedRemote: RemoteWrapper?,
)
fun RemoteConfig.toRemoteWrapper(): RemoteWrapper {
val fetchUri = this.urIs.firstOrNull()
val pushUri = this.pushURIs.firstOrNull()
?: this.urIs.firstOrNull() // If push URI == null, take fetch URI
return RemoteWrapper(
remoteName = this.name,
fetchUri = fetchUri?.toString().orEmpty(),
originalFetchUri = fetchUri?.toString().orEmpty(),
pushUri = pushUri?.toString().orEmpty(),
originalPushUri = pushUri?.toString().orEmpty(),
)
}