diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/extensions/ModifierExtensions.kt b/src/main/kotlin/com/jetpackduba/gitnuro/extensions/ModifierExtensions.kt index 8165701..5732499 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/extensions/ModifierExtensions.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/extensions/ModifierExtensions.kt @@ -26,6 +26,19 @@ fun Modifier.handMouseClickable(onClick: () -> Unit): Modifier { .handOnHover() } +fun Modifier.handMouseClickable( + interactionSource: MutableInteractionSource, + indication: Indication?, + onClick: () -> Unit +): Modifier { + return this + .clickable( + interactionSource = interactionSource, + indication = indication, + ) { onClick() } + .handOnHover() +} + /** * Ignore keyboard events of that components. * Specially useful for clickable components that may get focused and become clickable when pressing ENTER. diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt index 84be131..323b725 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt @@ -59,6 +59,7 @@ fun UncommitedChanges( var commitMessage by remember(statusViewModel) { mutableStateOf(statusViewModel.savedCommitMessage.message) } val stagedListState by statusViewModel.stagedLazyListState.collectAsState() val unstagedListState by statusViewModel.unstagedLazyListState.collectAsState() + val isAmend by statusViewModel.isAmend.collectAsState() val stageStatus = stageStatusState.value val staged: List @@ -75,16 +76,16 @@ fun UncommitedChanges( isLoading = true } - val doCommit = { amend: Boolean -> - statusViewModel.commit(commitMessage, amend) + val doCommit = { + statusViewModel.commit(commitMessage) onStagedDiffEntrySelected(null) commitMessage = "" } val canCommit = commitMessage.isNotEmpty() && staged.isNotEmpty() - val canAmend = (commitMessage.isNotEmpty() || staged.isNotEmpty()) && statusViewModel.hasPreviousCommits + val canAmend = commitMessage.isNotEmpty() && statusViewModel.hasPreviousCommits - LaunchedEffect(Unit) { + LaunchedEffect(statusViewModel) { statusViewModel.commitMessageChangesFlow.collect { newCommitMessage -> commitMessage = newCommitMessage } @@ -179,7 +180,7 @@ fun UncommitedChanges( .weight(weight = 1f, fill = true) .onPreviewKeyEvent { keyEvent -> if (keyEvent.matchesBinding(KeybindingOption.TEXT_ACCEPT) && canCommit) { - doCommit(false) + doCommit() true } else false @@ -215,7 +216,7 @@ fun UncommitedChanges( statusViewModel.resetRepoState() statusViewModel.updateCommitMessage("") }, - onMerge = { doCommit(false) } + onMerge = { doCommit() } ) repositoryState.isRebasing -> RebasingButtons( @@ -236,7 +237,7 @@ fun UncommitedChanges( statusViewModel.updateCommitMessage("") }, onCommit = { - doCommit(false) + doCommit() } ) @@ -248,14 +249,18 @@ fun UncommitedChanges( statusViewModel.updateCommitMessage("") }, onCommit = { - doCommit(false) + doCommit() } ) else -> UncommitedChangesButtons( canCommit = canCommit, canAmend = canAmend, - onCommit = { amend -> doCommit(amend) }, + isAmend = isAmend, + onAmendChecked = { isAmend -> + statusViewModel.amend(isAmend) + }, + onCommit = { doCommit() }, ) } } @@ -267,62 +272,98 @@ fun UncommitedChanges( fun UncommitedChangesButtons( canCommit: Boolean, canAmend: Boolean, - onCommit: (Boolean) -> Unit + isAmend: Boolean, + onAmendChecked: (Boolean) -> Unit, + onCommit: () -> Unit ) { var showDropDownMenu by remember { mutableStateOf(false) } - Row( - modifier = Modifier - .padding(top = 2.dp) - ) { - ConfirmationButton( - text = "Commit", - modifier = Modifier - .weight(1f) - .height(40.dp), - onClick = { onCommit(false) }, - enabled = canCommit, - shape = MaterialTheme.shapes.small.copy(topEnd = CornerSize(0.dp), bottomEnd = CornerSize(0.dp)) - ) - Spacer( - modifier = Modifier - .width(1.dp) - .height(40.dp), - ) + val buttonText = if (isAmend) + "Amend" + else + "Commit" - Box( - modifier = Modifier - .height(40.dp) - .clip(MaterialTheme.shapes.small.copy(topStart = CornerSize(0.dp), bottomStart = CornerSize(0.dp))) - .background(MaterialTheme.colors.primary) - .handMouseClickable { showDropDownMenu = true } + Column { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.handMouseClickable( + interactionSource = remember { MutableInteractionSource() }, + indication = null, + ) { + onAmendChecked(!isAmend) + } ) { - Icon( - Icons.Default.ArrowDropDown, - contentDescription = null, - tint = Color.White, + Checkbox( + checked = isAmend, + onCheckedChange = { + onAmendChecked(!isAmend) + }, modifier = Modifier - .padding(horizontal = 8.dp) - .align(Alignment.Center), + .padding(all = 8.dp) + .size(12.dp) ) - DropdownMenu( - onDismissRequest = { - showDropDownMenu = false - }, - content = { - DropDownContent( - enabled = canAmend, - dropDownContentData = DropDownContentData( - label = "Amend previous commit", - icon = null, - onClick = { onCommit(true) } - ), - onDismiss = { showDropDownMenu = false } - ) - }, - expanded = showDropDownMenu, + + Text( + "Amend previous commit", + style = MaterialTheme.typography.caption, + color = MaterialTheme.colors.onBackground, ) } + Row( + modifier = Modifier + .padding(top = 2.dp) + ) { + ConfirmationButton( + text = buttonText, + modifier = Modifier + .weight(1f) + .height(36.dp), + onClick = { + onCommit() + }, + enabled = canCommit || (canAmend && isAmend), + shape = MaterialTheme.shapes.small.copy(topEnd = CornerSize(0.dp), bottomEnd = CornerSize(0.dp)) + ) + Spacer( + modifier = Modifier + .width(1.dp) + .height(36.dp), + ) + + Box( + modifier = Modifier + .height(36.dp) + .clip(MaterialTheme.shapes.small.copy(topStart = CornerSize(0.dp), bottomStart = CornerSize(0.dp))) + .background(MaterialTheme.colors.primary) + .handMouseClickable { showDropDownMenu = true } + ) { + Icon( + Icons.Default.ArrowDropDown, + contentDescription = null, + tint = Color.White, + modifier = Modifier + .padding(horizontal = 8.dp) + .align(Alignment.Center), + ) + DropdownMenu( + onDismissRequest = { + showDropDownMenu = false + }, + content = { + /*DropDownContent( + enabled = canAmend, + dropDownContentData = DropDownContentData( + label = "Amend previous commit", + icon = null, + onClick = onCommit + ), + onDismiss = { showDropDownMenu = false } + )*/ + }, + expanded = showDropDownMenu, + ) + } + } } } diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/StatusViewModel.kt b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/StatusViewModel.kt index 549d65a..94f367e 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/StatusViewModel.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/StatusViewModel.kt @@ -66,6 +66,9 @@ class StatusViewModel @Inject constructor( private val _commitMessageChangesFlow = MutableSharedFlow() val commitMessageChangesFlow: SharedFlow = _commitMessageChangesFlow + private val _isAmend = MutableStateFlow(false) + val isAmend: StateFlow = _isAmend + init { tabScope.launch { tabState.refreshFlowFiltered( @@ -191,10 +194,19 @@ class StatusViewModel @Inject constructor( private suspend fun loadHasUncommitedChanges(git: Git) = withContext(Dispatchers.IO) { lastUncommitedChangesState = checkHasUncommitedChangedUseCase(git) } + fun amend(isAmend: Boolean) { + _isAmend.value = isAmend - fun commit(message: String, amend: Boolean) = tabState.safeProcessing( + if (isAmend && savedCommitMessage.message.isEmpty()) { + takeMessageFromPreviousCommit() + } + } + + fun commit(message: String) = tabState.safeProcessing( refreshType = RefreshType.ALL_DATA, ) { git -> + val amend = isAmend.value + val commitMessage = if (amend && message.isBlank()) { getLastCommitMessageUseCase(git) } else @@ -202,6 +214,7 @@ class StatusViewModel @Inject constructor( doCommitUseCase(git, commitMessage, amend) updateCommitMessage("") + _isAmend.value = false } suspend fun refresh(git: Git) = withContext(Dispatchers.IO) { @@ -263,6 +276,14 @@ class StatusViewModel @Inject constructor( savedCommitMessage = savedCommitMessage.copy(message = message) persistMessage() } + + private fun takeMessageFromPreviousCommit() = tabState.runOperation( + refreshType = RefreshType.NONE, + ) { git -> + savedCommitMessage = savedCommitMessage.copy(message = getLastCommitMessageUseCase(git)) + persistMessage() + _commitMessageChangesFlow.emit(savedCommitMessage.message) + } } sealed class StageStatus {