Refactored remotes in side panel
This commit is contained in:
parent
22088a3fbd
commit
81836f44a5
@ -22,6 +22,7 @@ object AppIcons {
|
|||||||
const val DOWNLOAD = "download.svg"
|
const val DOWNLOAD = "download.svg"
|
||||||
const val DRAG = "drag.svg"
|
const val DRAG = "drag.svg"
|
||||||
const val DROPDOWN = "dropdown.svg"
|
const val DROPDOWN = "dropdown.svg"
|
||||||
|
const val EDIT = "edit.svg"
|
||||||
const val ERROR = "error.svg"
|
const val ERROR = "error.svg"
|
||||||
const val EXPAND_MORE = "expand_more.svg"
|
const val EXPAND_MORE = "expand_more.svg"
|
||||||
const val FETCH = "fetch.svg"
|
const val FETCH = "fetch.svg"
|
||||||
|
@ -14,7 +14,7 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
private const val TAG = "FetchAllBranchesUseCase"
|
private const val TAG = "FetchAllBranchesUseCase"
|
||||||
|
|
||||||
class FetchAllBranchesUseCase @Inject constructor(
|
class FetchAllRemotesUseCase @Inject constructor(
|
||||||
private val handleTransportUseCase: HandleTransportUseCase,
|
private val handleTransportUseCase: HandleTransportUseCase,
|
||||||
) {
|
) {
|
||||||
suspend operator fun invoke(git: Git) = withContext(Dispatchers.IO) {
|
suspend operator fun invoke(git: Git) = withContext(Dispatchers.IO) {
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.jetpackduba.gitnuro.models
|
||||||
|
|
||||||
|
import org.eclipse.jgit.transport.RemoteConfig
|
||||||
|
|
||||||
|
data class RemoteWrapper constructor(
|
||||||
|
val remoteName: String,
|
||||||
|
val fetchUri: String,
|
||||||
|
val pushUri: String,
|
||||||
|
val isNew: Boolean,
|
||||||
|
)
|
||||||
|
|
||||||
|
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(),
|
||||||
|
pushUri = pushUri?.toString().orEmpty(),
|
||||||
|
isNew = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newRemoteWrapper(): RemoteWrapper {
|
||||||
|
return RemoteWrapper(
|
||||||
|
remoteName = "",
|
||||||
|
fetchUri = "",
|
||||||
|
pushUri = "",
|
||||||
|
isNew = true,
|
||||||
|
)
|
||||||
|
}
|
@ -17,12 +17,18 @@ import com.jetpackduba.gitnuro.extensions.handOnHover
|
|||||||
import com.jetpackduba.gitnuro.extensions.isLocal
|
import com.jetpackduba.gitnuro.extensions.isLocal
|
||||||
import com.jetpackduba.gitnuro.extensions.isValid
|
import com.jetpackduba.gitnuro.extensions.isValid
|
||||||
import com.jetpackduba.gitnuro.extensions.simpleName
|
import com.jetpackduba.gitnuro.extensions.simpleName
|
||||||
|
import com.jetpackduba.gitnuro.models.RemoteWrapper
|
||||||
|
import com.jetpackduba.gitnuro.models.newRemoteWrapper
|
||||||
|
import com.jetpackduba.gitnuro.models.toRemoteWrapper
|
||||||
import com.jetpackduba.gitnuro.theme.onBackgroundSecondary
|
import com.jetpackduba.gitnuro.theme.onBackgroundSecondary
|
||||||
import com.jetpackduba.gitnuro.ui.components.*
|
import com.jetpackduba.gitnuro.ui.components.AdjustableOutlinedTextField
|
||||||
|
import com.jetpackduba.gitnuro.ui.components.ScrollableLazyColumn
|
||||||
|
import com.jetpackduba.gitnuro.ui.components.SideMenuHeader
|
||||||
|
import com.jetpackduba.gitnuro.ui.components.SideMenuSubentry
|
||||||
import com.jetpackduba.gitnuro.ui.components.tooltip.DelayedTooltip
|
import com.jetpackduba.gitnuro.ui.components.tooltip.DelayedTooltip
|
||||||
import com.jetpackduba.gitnuro.ui.context_menu.*
|
import com.jetpackduba.gitnuro.ui.context_menu.*
|
||||||
|
import com.jetpackduba.gitnuro.ui.dialogs.AddEditRemoteDialog
|
||||||
import com.jetpackduba.gitnuro.ui.dialogs.AddSubmodulesDialog
|
import com.jetpackduba.gitnuro.ui.dialogs.AddSubmodulesDialog
|
||||||
import com.jetpackduba.gitnuro.ui.dialogs.EditRemotesDialog
|
|
||||||
import com.jetpackduba.gitnuro.ui.dialogs.SetDefaultUpstreamBranchDialog
|
import com.jetpackduba.gitnuro.ui.dialogs.SetDefaultUpstreamBranchDialog
|
||||||
import com.jetpackduba.gitnuro.viewmodels.ChangeDefaultUpstreamBranchViewModel
|
import com.jetpackduba.gitnuro.viewmodels.ChangeDefaultUpstreamBranchViewModel
|
||||||
import com.jetpackduba.gitnuro.viewmodels.sidepanel.*
|
import com.jetpackduba.gitnuro.viewmodels.sidepanel.*
|
||||||
@ -50,7 +56,7 @@ fun SidePanel(
|
|||||||
val stashesState by stashesViewModel.stashesState.collectAsState()
|
val stashesState by stashesViewModel.stashesState.collectAsState()
|
||||||
val submodulesState by submodulesViewModel.submodules.collectAsState()
|
val submodulesState by submodulesViewModel.submodules.collectAsState()
|
||||||
|
|
||||||
var showEditRemotesDialog by remember { mutableStateOf(false) }
|
val (showAddEditRemote, setShowAddEditRemote) = remember { mutableStateOf<RemoteWrapper?>(null) }
|
||||||
val (branchToChangeUpstream, setBranchToChangeUpstream) = remember { mutableStateOf<Ref?>(null) }
|
val (branchToChangeUpstream, setBranchToChangeUpstream) = remember { mutableStateOf<Ref?>(null) }
|
||||||
var showEditSubmodulesDialog by remember { mutableStateOf(false) }
|
var showEditSubmodulesDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
@ -80,7 +86,7 @@ fun SidePanel(
|
|||||||
remotes(
|
remotes(
|
||||||
remotesState = remotesState,
|
remotesState = remotesState,
|
||||||
remotesViewModel = remotesViewModel,
|
remotesViewModel = remotesViewModel,
|
||||||
onShowEditRemotesDialog = { showEditRemotesDialog = true },
|
onShowAddEditRemoteDialog = { setShowAddEditRemote(it) },
|
||||||
)
|
)
|
||||||
|
|
||||||
tags(
|
tags(
|
||||||
@ -103,11 +109,12 @@ fun SidePanel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showEditRemotesDialog) {
|
if (showAddEditRemote != null) {
|
||||||
EditRemotesDialog(
|
AddEditRemoteDialog(
|
||||||
remotesViewModel = remotesViewModel,
|
remotesViewModel = remotesViewModel,
|
||||||
|
remoteWrapper = showAddEditRemote,
|
||||||
onDismiss = {
|
onDismiss = {
|
||||||
showEditRemotesDialog = false
|
setShowAddEditRemote(null)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -218,7 +225,7 @@ fun LazyListScope.localBranches(
|
|||||||
fun LazyListScope.remotes(
|
fun LazyListScope.remotes(
|
||||||
remotesState: RemotesState,
|
remotesState: RemotesState,
|
||||||
remotesViewModel: RemotesViewModel,
|
remotesViewModel: RemotesViewModel,
|
||||||
onShowEditRemotesDialog: () -> Unit,
|
onShowAddEditRemoteDialog: (RemoteWrapper) -> Unit,
|
||||||
) {
|
) {
|
||||||
val isExpanded = remotesState.isExpanded
|
val isExpanded = remotesState.isExpanded
|
||||||
val remotes = remotesState.remotes
|
val remotes = remotesState.remotes
|
||||||
@ -230,14 +237,14 @@ fun LazyListScope.remotes(
|
|||||||
itemsCount = remotes.count(),
|
itemsCount = remotes.count(),
|
||||||
hoverIcon = {
|
hoverIcon = {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onShowEditRemotesDialog,
|
onClick = { onShowAddEditRemoteDialog(newRemoteWrapper()) },
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(end = 16.dp)
|
.padding(end = 16.dp)
|
||||||
.size(16.dp)
|
.size(16.dp)
|
||||||
.handOnHover(),
|
.handOnHover(),
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(AppIcons.SETTINGS),
|
painter = painterResource(AppIcons.ADD),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
@ -255,7 +262,12 @@ fun LazyListScope.remotes(
|
|||||||
item {
|
item {
|
||||||
Remote(
|
Remote(
|
||||||
remote = remote,
|
remote = remote,
|
||||||
onRemoteClicked = { remotesViewModel.onRemoteClicked(remote) }
|
onEditRemote = {
|
||||||
|
val wrapper = remote.remoteInfo.remoteConfig.toRemoteWrapper()
|
||||||
|
onShowAddEditRemoteDialog(wrapper)
|
||||||
|
},
|
||||||
|
onDeleteRemote = { remotesViewModel.deleteRemote(remote.remoteInfo.remoteConfig.name) },
|
||||||
|
onRemoteClicked = { remotesViewModel.onRemoteClicked(remote) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,7 +473,17 @@ private fun Branch(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun Remote(
|
private fun Remote(
|
||||||
remote: RemoteView,
|
remote: RemoteView,
|
||||||
|
onEditRemote: () -> Unit,
|
||||||
|
onDeleteRemote: () -> Unit,
|
||||||
onRemoteClicked: () -> Unit,
|
onRemoteClicked: () -> Unit,
|
||||||
|
) {
|
||||||
|
ContextMenu(
|
||||||
|
items = {
|
||||||
|
remoteContextMenu(
|
||||||
|
onEdit = onEditRemote,
|
||||||
|
onDelete = onDeleteRemote,
|
||||||
|
)
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
SideMenuSubentry(
|
SideMenuSubentry(
|
||||||
text = remote.remoteInfo.remoteConfig.name,
|
text = remote.remoteInfo.remoteConfig.name,
|
||||||
@ -470,6 +492,7 @@ private fun Remote(
|
|||||||
isSelected = false,
|
isSelected = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -1,10 +1,20 @@
|
|||||||
package com.jetpackduba.gitnuro.ui.context_menu
|
package com.jetpackduba.gitnuro.ui.context_menu
|
||||||
|
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import com.jetpackduba.gitnuro.AppIcons
|
||||||
|
|
||||||
fun remoteContextMenu(
|
fun remoteContextMenu(
|
||||||
onEditRemotes: () -> Unit,
|
onEdit: () -> Unit,
|
||||||
|
onDelete: () -> Unit,
|
||||||
): List<ContextMenuElement> = listOf(
|
): List<ContextMenuElement> = listOf(
|
||||||
ContextMenuElement.ContextTextEntry(
|
ContextMenuElement.ContextTextEntry(
|
||||||
label = "Edit remotes",
|
label = "Edit",
|
||||||
onClick = onEditRemotes
|
icon = { painterResource(AppIcons.EDIT) },
|
||||||
|
onClick = onEdit
|
||||||
|
),
|
||||||
|
ContextMenuElement.ContextTextEntry(
|
||||||
|
label = "Delete",
|
||||||
|
icon = { painterResource(AppIcons.DELETE) },
|
||||||
|
onClick = onDelete
|
||||||
),
|
),
|
||||||
)
|
)
|
@ -0,0 +1,159 @@
|
|||||||
|
package com.jetpackduba.gitnuro.ui.dialogs
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
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.unit.dp
|
||||||
|
import com.jetpackduba.gitnuro.extensions.handOnHover
|
||||||
|
import com.jetpackduba.gitnuro.models.RemoteWrapper
|
||||||
|
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 kotlinx.coroutines.flow.collectLatest
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AddEditRemoteDialog(
|
||||||
|
remotesViewModel: RemotesViewModel,
|
||||||
|
remoteWrapper: RemoteWrapper,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
) {
|
||||||
|
var remote: RemoteWrapper by remember(remoteWrapper) { mutableStateOf(remoteWrapper) }
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
remotesViewModel.remoteUpdated.collectLatest { onDismiss() }
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialDialog(
|
||||||
|
paddingVertical = 8.dp,
|
||||||
|
paddingHorizontal = 16.dp,
|
||||||
|
background = MaterialTheme.colors.surface,
|
||||||
|
onCloseRequested = onDismiss
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(600.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = if (remote.isNew) "New remote" else "Edit remote \"${remote.remoteName}\"",
|
||||||
|
color = MaterialTheme.colors.onBackground,
|
||||||
|
style = MaterialTheme.typography.h3,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(start = 8.dp, top = 8.dp, bottom = 8.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(bottom = 8.dp)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 8.dp)
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
if (remote.isNew) {
|
||||||
|
Text(
|
||||||
|
text = "Remote name",
|
||||||
|
color = MaterialTheme.colors.onBackground,
|
||||||
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
|
)
|
||||||
|
|
||||||
|
AdjustableOutlinedTextField(
|
||||||
|
value = remote.remoteName,
|
||||||
|
onValueChange = { newValue ->
|
||||||
|
remote = remote.copy(remoteName = newValue)
|
||||||
|
},
|
||||||
|
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 = remote.fetchUri,
|
||||||
|
onValueChange = { newValue ->
|
||||||
|
remote = remote.copy(fetchUri = newValue)
|
||||||
|
},
|
||||||
|
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 = remote.pushUri,
|
||||||
|
onValueChange = { newValue ->
|
||||||
|
remote = remote.copy(pushUri = newValue)
|
||||||
|
},
|
||||||
|
singleLine = true,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 8.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = 32.dp)
|
||||||
|
.align(Alignment.End),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
|
PrimaryButton(
|
||||||
|
text = "Cancel",
|
||||||
|
modifier = Modifier.padding(end = 8.dp),
|
||||||
|
onClick = onDismiss,
|
||||||
|
backgroundColor = Color.Transparent,
|
||||||
|
backgroundDisabled = Color.Transparent,
|
||||||
|
textColor = MaterialTheme.colors.onBackground,
|
||||||
|
disabledTextColor = MaterialTheme.colors.onBackground.copy(alpha = 0.5f),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
PrimaryButton(
|
||||||
|
modifier = Modifier,
|
||||||
|
onClick = {
|
||||||
|
if (remote.isNew)
|
||||||
|
remotesViewModel.addRemote(remote)
|
||||||
|
else
|
||||||
|
remotesViewModel.updateRemote(remote)
|
||||||
|
},
|
||||||
|
text = "Save changes",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,371 +0,0 @@
|
|||||||
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(),
|
|
||||||
)
|
|
||||||
}
|
|
@ -3,7 +3,7 @@ package com.jetpackduba.gitnuro.viewmodels
|
|||||||
import com.jetpackduba.gitnuro.TaskType
|
import com.jetpackduba.gitnuro.TaskType
|
||||||
import com.jetpackduba.gitnuro.git.RefreshType
|
import com.jetpackduba.gitnuro.git.RefreshType
|
||||||
import com.jetpackduba.gitnuro.git.TabState
|
import com.jetpackduba.gitnuro.git.TabState
|
||||||
import com.jetpackduba.gitnuro.git.remote_operations.FetchAllBranchesUseCase
|
import com.jetpackduba.gitnuro.git.remote_operations.FetchAllRemotesUseCase
|
||||||
import com.jetpackduba.gitnuro.git.remote_operations.PullBranchUseCase
|
import com.jetpackduba.gitnuro.git.remote_operations.PullBranchUseCase
|
||||||
import com.jetpackduba.gitnuro.git.remote_operations.PullType
|
import com.jetpackduba.gitnuro.git.remote_operations.PullType
|
||||||
import com.jetpackduba.gitnuro.git.remote_operations.PushBranchUseCase
|
import com.jetpackduba.gitnuro.git.remote_operations.PushBranchUseCase
|
||||||
@ -19,7 +19,7 @@ class MenuViewModel @Inject constructor(
|
|||||||
private val tabState: TabState,
|
private val tabState: TabState,
|
||||||
private val pullBranchUseCase: PullBranchUseCase,
|
private val pullBranchUseCase: PullBranchUseCase,
|
||||||
private val pushBranchUseCase: PushBranchUseCase,
|
private val pushBranchUseCase: PushBranchUseCase,
|
||||||
private val fetchAllBranchesUseCase: FetchAllBranchesUseCase,
|
private val fetchAllRemotesUseCase: FetchAllRemotesUseCase,
|
||||||
private val popLastStashUseCase: PopLastStashUseCase,
|
private val popLastStashUseCase: PopLastStashUseCase,
|
||||||
private val stashChangesUseCase: StashChangesUseCase,
|
private val stashChangesUseCase: StashChangesUseCase,
|
||||||
private val stageUntrackedFileUseCase: StageUntrackedFileUseCase,
|
private val stageUntrackedFileUseCase: StageUntrackedFileUseCase,
|
||||||
@ -50,7 +50,7 @@ class MenuViewModel @Inject constructor(
|
|||||||
taskType = TaskType.FETCH,
|
taskType = TaskType.FETCH,
|
||||||
positiveFeedbackText = "Fetch all completed",
|
positiveFeedbackText = "Fetch all completed",
|
||||||
) { git ->
|
) { git ->
|
||||||
fetchAllBranchesUseCase(git)
|
fetchAllRemotesUseCase(git)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun push(force: Boolean = false, pushTags: Boolean = false) = tabState.safeProcessing(
|
fun push(force: Boolean = false, pushTags: Boolean = false) = tabState.safeProcessing(
|
||||||
|
@ -6,15 +6,11 @@ import com.jetpackduba.gitnuro.extensions.lowercaseContains
|
|||||||
import com.jetpackduba.gitnuro.extensions.simpleName
|
import com.jetpackduba.gitnuro.extensions.simpleName
|
||||||
import com.jetpackduba.gitnuro.git.RefreshType
|
import com.jetpackduba.gitnuro.git.RefreshType
|
||||||
import com.jetpackduba.gitnuro.git.TabState
|
import com.jetpackduba.gitnuro.git.TabState
|
||||||
import com.jetpackduba.gitnuro.git.branches.CheckoutRefUseCase
|
|
||||||
import com.jetpackduba.gitnuro.git.branches.DeleteLocallyRemoteBranchesUseCase
|
import com.jetpackduba.gitnuro.git.branches.DeleteLocallyRemoteBranchesUseCase
|
||||||
import com.jetpackduba.gitnuro.git.branches.GetCurrentBranchUseCase
|
import com.jetpackduba.gitnuro.git.branches.GetCurrentBranchUseCase
|
||||||
import com.jetpackduba.gitnuro.git.branches.GetRemoteBranchesUseCase
|
import com.jetpackduba.gitnuro.git.branches.GetRemoteBranchesUseCase
|
||||||
import com.jetpackduba.gitnuro.git.remote_operations.DeleteRemoteBranchUseCase
|
|
||||||
import com.jetpackduba.gitnuro.git.remote_operations.PullFromSpecificBranchUseCase
|
|
||||||
import com.jetpackduba.gitnuro.git.remote_operations.PushToSpecificBranchUseCase
|
|
||||||
import com.jetpackduba.gitnuro.git.remotes.*
|
import com.jetpackduba.gitnuro.git.remotes.*
|
||||||
import com.jetpackduba.gitnuro.ui.dialogs.RemoteWrapper
|
import com.jetpackduba.gitnuro.models.RemoteWrapper
|
||||||
import com.jetpackduba.gitnuro.viewmodels.ISharedRemotesViewModel
|
import com.jetpackduba.gitnuro.viewmodels.ISharedRemotesViewModel
|
||||||
import com.jetpackduba.gitnuro.viewmodels.SharedRemotesViewModel
|
import com.jetpackduba.gitnuro.viewmodels.SharedRemotesViewModel
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
@ -45,6 +41,9 @@ class RemotesViewModel @AssistedInject constructor(
|
|||||||
private val remotes = MutableStateFlow<List<RemoteView>>(listOf())
|
private val remotes = MutableStateFlow<List<RemoteView>>(listOf())
|
||||||
private val currentBranch = MutableStateFlow<Ref?>(null)
|
private val currentBranch = MutableStateFlow<Ref?>(null)
|
||||||
|
|
||||||
|
private val _remoteUpdated = MutableSharedFlow<Unit>()
|
||||||
|
val remoteUpdated = _remoteUpdated.asSharedFlow()
|
||||||
|
|
||||||
val remoteState: StateFlow<RemotesState> =
|
val remoteState: StateFlow<RemotesState> =
|
||||||
combine(remotes, isExpanded, filter, currentBranch) { remotes, isExpanded, filter, currentBranch ->
|
combine(remotes, isExpanded, filter, currentBranch) { remotes, isExpanded, filter, currentBranch ->
|
||||||
val remotesFiltered = remotes.map { remote ->
|
val remotesFiltered = remotes.map { remote ->
|
||||||
@ -115,8 +114,8 @@ class RemotesViewModel @AssistedInject constructor(
|
|||||||
tabState.newSelectedRef(ref, ref.objectId)
|
tabState.newSelectedRef(ref, ref.objectId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteRemote(remoteName: String, isNew: Boolean) = tabState.safeProcessing(
|
fun deleteRemote(remoteName: String) = tabState.safeProcessing(
|
||||||
refreshType = if (isNew) RefreshType.REMOTES else RefreshType.ALL_DATA,
|
refreshType = RefreshType.ALL_DATA,
|
||||||
taskType = TaskType.DELETE_REMOTE,
|
taskType = TaskType.DELETE_REMOTE,
|
||||||
positiveFeedbackText = "Remote $remoteName deleted",
|
positiveFeedbackText = "Remote $remoteName deleted",
|
||||||
) { git ->
|
) { git ->
|
||||||
@ -176,6 +175,8 @@ class RemotesViewModel @AssistedInject constructor(
|
|||||||
uri = selectedRemoteConfig.pushUri,
|
uri = selectedRemoteConfig.pushUri,
|
||||||
uriType = RemoteSetUrlCommand.UriType.PUSH
|
uriType = RemoteSetUrlCommand.UriType.PUSH
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_remoteUpdated.emit(Unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
src/main/resources/edit.svg
Normal file
1
src/main/resources/edit.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M200-200h57l391-391-57-57-391 391v57Zm-80 80v-170l528-527q12-11 26.5-17t30.5-6q16 0 31 6t26 18l55 56q12 11 17.5 26t5.5 30q0 16-5.5 30.5T817-647L290-120H120Zm640-584-56-56 56 56Zm-141 85-28-29 57 57-29-28Z"/></svg>
|
After Width: | Height: | Size: 329 B |
Loading…
Reference in New Issue
Block a user