diff --git a/src/main/kotlin/app/git/StashManager.kt b/src/main/kotlin/app/git/StashManager.kt index c3b4fba..1fc906f 100644 --- a/src/main/kotlin/app/git/StashManager.kt +++ b/src/main/kotlin/app/git/StashManager.kt @@ -7,10 +7,14 @@ import org.eclipse.jgit.revwalk.RevCommit import javax.inject.Inject class StashManager @Inject constructor() { - suspend fun stash(git: Git) = withContext(Dispatchers.IO) { + suspend fun stash(git: Git, message: String?) = withContext(Dispatchers.IO) { git .stashCreate() .setIncludeUntracked(true) + .apply { + if (message != null) + setWorkingDirectoryMessage(message) + } .call() } diff --git a/src/main/kotlin/app/ui/Menu.kt b/src/main/kotlin/app/ui/Menu.kt index e7d694e..7192c2a 100644 --- a/src/main/kotlin/app/ui/Menu.kt +++ b/src/main/kotlin/app/ui/Menu.kt @@ -32,6 +32,7 @@ fun Menu( menuViewModel: MenuViewModel, onRepositoryOpen: () -> Unit, onCreateBranch: () -> Unit, + onStashWithMessage: () -> Unit, ) { var showAdditionalOptionsDropDownMenu by remember { mutableStateOf(false) } @@ -93,11 +94,14 @@ fun Menu( Spacer(modifier = Modifier.width(24.dp)) - MenuButton( + ExtendedMenuButton( modifier = Modifier.padding(end = 8.dp), title = "Stash", icon = painterResource("stash.svg"), onClick = { menuViewModel.stash() }, + extendedListItems = stashContextMenuItems( + onStashWithMessage = onStashWithMessage + ) ) MenuButton( diff --git a/src/main/kotlin/app/ui/RepositoryOpen.kt b/src/main/kotlin/app/ui/RepositoryOpen.kt index 9be8a8e..6ef2874 100644 --- a/src/main/kotlin/app/ui/RepositoryOpen.kt +++ b/src/main/kotlin/app/ui/RepositoryOpen.kt @@ -20,6 +20,7 @@ import app.theme.borderColor import app.theme.primaryTextColor import app.ui.dialogs.NewBranchDialog import app.ui.dialogs.RebaseInteractive +import app.ui.dialogs.StashWithMessageDialog import app.ui.log.Log import app.viewmodels.BlameState import app.viewmodels.TabViewModel @@ -43,6 +44,7 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) { val showHistory by tabViewModel.showHistory.collectAsState() var showNewBranchDialog by remember { mutableStateOf(false) } + var showStashWithMessageDialog by remember { mutableStateOf(false) } if (showNewBranchDialog) { NewBranchDialog( @@ -54,6 +56,16 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) { showNewBranchDialog = false } ) + } else if (showStashWithMessageDialog) { + StashWithMessageDialog( + onReject = { + showStashWithMessageDialog = false + }, + onAccept = { stashMessage -> + tabViewModel.menuViewModel.stashWithMessage(stashMessage) + showStashWithMessageDialog = false + } + ) } Column { @@ -72,7 +84,8 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) { onRepositoryOpen = { openRepositoryDialog(tabViewModel = tabViewModel) }, - onCreateBranch = { showNewBranchDialog = true } + onCreateBranch = { showNewBranchDialog = true }, + onStashWithMessage = { showStashWithMessageDialog = true }, ) RepoContent(tabViewModel, diffSelected, selectedItem, repositoryState, blameState, showHistory) @@ -90,10 +103,10 @@ fun RepoContent( blameState: BlameState, showHistory: Boolean, ) { - if(showHistory) { + if (showHistory) { val historyViewModel = tabViewModel.historyViewModel - if(historyViewModel != null) { + if (historyViewModel != null) { FileHistory( historyViewModel = historyViewModel, onClose = { diff --git a/src/main/kotlin/app/ui/context_menu/StashContextMenu.kt b/src/main/kotlin/app/ui/context_menu/StashContextMenu.kt new file mode 100644 index 0000000..37653a0 --- /dev/null +++ b/src/main/kotlin/app/ui/context_menu/StashContextMenu.kt @@ -0,0 +1,15 @@ +package app.ui.context_menu + +import androidx.compose.foundation.ExperimentalFoundationApi + +@OptIn(ExperimentalFoundationApi::class) +fun stashContextMenuItems( + onStashWithMessage: () -> Unit, +): List { + return mutableListOf( + DropDownContentData( + label = "Stash with message", + onClick = onStashWithMessage, + ), + ) +} diff --git a/src/main/kotlin/app/ui/dialogs/StashWithMessageDialog.kt b/src/main/kotlin/app/ui/dialogs/StashWithMessageDialog.kt new file mode 100644 index 0000000..ef63820 --- /dev/null +++ b/src/main/kotlin/app/ui/dialogs/StashWithMessageDialog.kt @@ -0,0 +1,94 @@ +package app.ui.dialogs + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusOrder +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.onPreviewKeyEvent +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import app.theme.outlinedTextFieldColors +import app.theme.primaryTextColor +import app.theme.textButtonColors +import app.ui.components.PrimaryButton + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun StashWithMessageDialog( + onReject: () -> Unit, + onAccept: (stashMessage: String) -> Unit +) { + var textField by remember { mutableStateOf("") } + val textFieldFocusRequester = remember { FocusRequester() } + val buttonFieldFocusRequester = remember { FocusRequester() } + + MaterialDialog(onCloseRequested = onReject) { + Column( + modifier = Modifier + .background(MaterialTheme.colors.background), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + OutlinedTextField( + modifier = Modifier + .focusOrder(textFieldFocusRequester) { + this.next = buttonFieldFocusRequester + } + .width(300.dp) + .onPreviewKeyEvent { + if (it.key == Key.Enter) { + onAccept(textField) + true + } else { + false + } + }, + value = textField, + label = { Text("New stash message", fontSize = 14.sp) }, + textStyle = TextStyle(fontSize = 14.sp, color = MaterialTheme.colors.primaryTextColor), + colors = outlinedTextFieldColors(), + onValueChange = { + textField = it + }, + ) + Row( + modifier = Modifier + .padding(top = 16.dp) + .align(Alignment.End) + ) { + TextButton( + modifier = Modifier.padding(end = 8.dp), + colors = textButtonColors(), + onClick = { + onReject() + } + ) { + Text("Cancel") + } + PrimaryButton( + modifier = Modifier.focusOrder(buttonFieldFocusRequester) { + this.previous = textFieldFocusRequester + this.next = textFieldFocusRequester + }, + enabled = textField.isNotEmpty(), + onClick = { + onAccept(textField) + }, + text = "Stash" + ) + } + } + } + + LaunchedEffect(Unit) { + textFieldFocusRequester.requestFocus() + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/viewmodels/MenuViewModel.kt b/src/main/kotlin/app/viewmodels/MenuViewModel.kt index c21fdfe..f788752 100644 --- a/src/main/kotlin/app/viewmodels/MenuViewModel.kt +++ b/src/main/kotlin/app/viewmodels/MenuViewModel.kt @@ -35,7 +35,14 @@ class MenuViewModel @Inject constructor( refreshType = RefreshType.UNCOMMITED_CHANGES_AND_LOG, ) { git -> statusManager.stageUntrackedFiles(git) - stashManager.stash(git) + stashManager.stash(git, null) + } + + fun stashWithMessage(message: String) = tabState.safeProcessing( + refreshType = RefreshType.UNCOMMITED_CHANGES_AND_LOG, + ) { git -> + statusManager.stageUntrackedFiles(git) + stashManager.stash(git, message) } fun popStash() = tabState.safeProcessing(