Added keybindings to pull, push and create branch
This commit is contained in:
parent
0dad158275
commit
761ea59986
@ -42,6 +42,21 @@ enum class KeybindingOption {
|
|||||||
* Used to go down in lists
|
* Used to go down in lists
|
||||||
*/
|
*/
|
||||||
DOWN,
|
DOWN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to pull in current repository
|
||||||
|
*/
|
||||||
|
PULL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to push in current repository
|
||||||
|
*/
|
||||||
|
PUSH,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to show branch creation dialog
|
||||||
|
*/
|
||||||
|
BRANCH_CREATE,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -66,6 +81,15 @@ private fun baseKeybindings() = mapOf(
|
|||||||
KeybindingOption.DOWN to listOf(
|
KeybindingOption.DOWN to listOf(
|
||||||
Keybinding(key = Key.DirectionDown),
|
Keybinding(key = Key.DirectionDown),
|
||||||
),
|
),
|
||||||
|
KeybindingOption.PULL to listOf(
|
||||||
|
Keybinding(key = Key.U, control = true),
|
||||||
|
),
|
||||||
|
KeybindingOption.PUSH to listOf(
|
||||||
|
Keybinding(key = Key.P, control = true),
|
||||||
|
),
|
||||||
|
KeybindingOption.BRANCH_CREATE to listOf(
|
||||||
|
Keybinding(key = Key.B, control = true),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun linuxKeybindings(): Map<KeybindingOption, List<Keybinding>> = baseKeybindings()
|
private fun linuxKeybindings(): Map<KeybindingOption, List<Keybinding>> = baseKeybindings()
|
||||||
|
@ -11,12 +11,14 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.input.key.onKeyEvent
|
import androidx.compose.ui.input.key.onKeyEvent
|
||||||
|
import androidx.compose.ui.input.key.onPreviewKeyEvent
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.jetpackduba.gitnuro.AppConstants
|
import com.jetpackduba.gitnuro.AppConstants
|
||||||
import com.jetpackduba.gitnuro.extensions.handMouseClickable
|
import com.jetpackduba.gitnuro.extensions.handMouseClickable
|
||||||
import com.jetpackduba.gitnuro.git.DiffType
|
import com.jetpackduba.gitnuro.git.DiffType
|
||||||
import com.jetpackduba.gitnuro.git.rebase.RebaseInteractiveState
|
import com.jetpackduba.gitnuro.git.rebase.RebaseInteractiveState
|
||||||
|
import com.jetpackduba.gitnuro.git.remote_operations.PullType
|
||||||
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
|
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
|
||||||
import com.jetpackduba.gitnuro.keybindings.matchesBinding
|
import com.jetpackduba.gitnuro.keybindings.matchesBinding
|
||||||
import com.jetpackduba.gitnuro.models.AuthorInfoSimple
|
import com.jetpackduba.gitnuro.models.AuthorInfoSimple
|
||||||
@ -106,7 +108,31 @@ fun RepositoryOpenPage(
|
|||||||
LaunchedEffect(selectedItem) {
|
LaunchedEffect(selectedItem) {
|
||||||
focusRequester.requestFocus()
|
focusRequester.requestFocus()
|
||||||
}
|
}
|
||||||
Column {
|
Column (
|
||||||
|
modifier = Modifier.onPreviewKeyEvent {
|
||||||
|
println("Key event $it")
|
||||||
|
when {
|
||||||
|
it.matchesBinding(KeybindingOption.PULL) -> {
|
||||||
|
tabViewModel.pull(PullType.DEFAULT)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
it.matchesBinding(KeybindingOption.PUSH) -> {
|
||||||
|
tabViewModel.push()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
it.matchesBinding(KeybindingOption.BRANCH_CREATE) -> {
|
||||||
|
if (!showNewBranchDialog) {
|
||||||
|
showNewBranchDialog = true
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
) {
|
||||||
Row(modifier = Modifier.weight(1f)) {
|
Row(modifier = Modifier.weight(1f)) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -33,6 +33,8 @@ import androidx.compose.ui.unit.dp
|
|||||||
import com.jetpackduba.gitnuro.AppConstants
|
import com.jetpackduba.gitnuro.AppConstants
|
||||||
import com.jetpackduba.gitnuro.AppIcons
|
import com.jetpackduba.gitnuro.AppIcons
|
||||||
import com.jetpackduba.gitnuro.extensions.*
|
import com.jetpackduba.gitnuro.extensions.*
|
||||||
|
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
|
||||||
|
import com.jetpackduba.gitnuro.keybindings.matchesBinding
|
||||||
import com.jetpackduba.gitnuro.theme.AppTheme
|
import com.jetpackduba.gitnuro.theme.AppTheme
|
||||||
import com.jetpackduba.gitnuro.theme.backgroundSelected
|
import com.jetpackduba.gitnuro.theme.backgroundSelected
|
||||||
import com.jetpackduba.gitnuro.theme.onBackgroundSecondary
|
import com.jetpackduba.gitnuro.theme.onBackgroundSecondary
|
||||||
@ -343,8 +345,9 @@ fun RecentRepositoriesList(
|
|||||||
if (it.type != KeyEventType.KeyDown) {
|
if (it.type != KeyEventType.KeyDown) {
|
||||||
return@onPreviewKeyEvent false
|
return@onPreviewKeyEvent false
|
||||||
}
|
}
|
||||||
when (it.key) {
|
|
||||||
Key.DirectionDown -> {
|
when {
|
||||||
|
it.matchesBinding(KeybindingOption.DOWN) -> {
|
||||||
if (focusedItemIndex < filteredRepositories.lastIndex) {
|
if (focusedItemIndex < filteredRepositories.lastIndex) {
|
||||||
focusedItemIndex += 1
|
focusedItemIndex += 1
|
||||||
scope.launch { listState.animateScrollToItem(focusedItemIndex) }
|
scope.launch { listState.animateScrollToItem(focusedItemIndex) }
|
||||||
@ -352,7 +355,7 @@ fun RecentRepositoriesList(
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
Key.DirectionUp -> {
|
it.matchesBinding(KeybindingOption.UP) -> {
|
||||||
if (focusedItemIndex > 0) {
|
if (focusedItemIndex > 0) {
|
||||||
focusedItemIndex -= 1
|
focusedItemIndex -= 1
|
||||||
scope.launch { listState.animateScrollToItem(focusedItemIndex) }
|
scope.launch { listState.animateScrollToItem(focusedItemIndex) }
|
||||||
@ -360,7 +363,7 @@ fun RecentRepositoriesList(
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
Key.Enter -> {
|
it.matchesBinding(KeybindingOption.SIMPLE_ACCEPT) -> {
|
||||||
val repo = filteredRepositories.getOrNull(focusedItemIndex)
|
val repo = filteredRepositories.getOrNull(focusedItemIndex)
|
||||||
if (repo != null && isSearchFocused) {
|
if (repo != null && isSearchFocused) {
|
||||||
onOpenKnownRepository(repo)
|
onOpenKnownRepository(repo)
|
||||||
|
@ -209,10 +209,6 @@ fun Diff(
|
|||||||
|
|
||||||
ViewDiffResult.None -> throw NotImplementedError("None should be a possible state in the diff")
|
ViewDiffResult.None -> throw NotImplementedError("None should be a possible state in the diff")
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
focusRequester.requestFocus()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,104 @@
|
|||||||
|
package com.jetpackduba.gitnuro.viewmodels
|
||||||
|
|
||||||
|
import com.jetpackduba.gitnuro.TaskType
|
||||||
|
import com.jetpackduba.gitnuro.git.RefreshType
|
||||||
|
import com.jetpackduba.gitnuro.git.TabState
|
||||||
|
import com.jetpackduba.gitnuro.git.remote_operations.FetchAllRemotesUseCase
|
||||||
|
import com.jetpackduba.gitnuro.git.remote_operations.PullBranchUseCase
|
||||||
|
import com.jetpackduba.gitnuro.git.remote_operations.PullType
|
||||||
|
import com.jetpackduba.gitnuro.git.remote_operations.PushBranchUseCase
|
||||||
|
import com.jetpackduba.gitnuro.git.stash.PopLastStashUseCase
|
||||||
|
import com.jetpackduba.gitnuro.git.stash.StashChangesUseCase
|
||||||
|
import com.jetpackduba.gitnuro.managers.AppStateManager
|
||||||
|
import com.jetpackduba.gitnuro.models.errorNotification
|
||||||
|
import com.jetpackduba.gitnuro.models.positiveNotification
|
||||||
|
import com.jetpackduba.gitnuro.repositories.AppSettingsRepository
|
||||||
|
import com.jetpackduba.gitnuro.terminal.OpenRepositoryInTerminalUseCase
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
interface IGlobalMenuActionsViewModel {
|
||||||
|
fun pull(pullType: PullType): Job
|
||||||
|
fun fetchAll(): Job
|
||||||
|
fun push(force: Boolean = false, pushTags: Boolean = false): Job
|
||||||
|
fun stash(): Job
|
||||||
|
fun popStash(): Job
|
||||||
|
fun openTerminal(): Job
|
||||||
|
}
|
||||||
|
|
||||||
|
class GlobalMenuActionsViewModel @Inject constructor(
|
||||||
|
private val tabState: TabState,
|
||||||
|
private val pullBranchUseCase: PullBranchUseCase,
|
||||||
|
private val pushBranchUseCase: PushBranchUseCase,
|
||||||
|
private val fetchAllRemotesUseCase: FetchAllRemotesUseCase,
|
||||||
|
private val popLastStashUseCase: PopLastStashUseCase,
|
||||||
|
private val stashChangesUseCase: StashChangesUseCase,
|
||||||
|
private val openRepositoryInTerminalUseCase: OpenRepositoryInTerminalUseCase,
|
||||||
|
settings: AppSettingsRepository,
|
||||||
|
appStateManager: AppStateManager,
|
||||||
|
) : IGlobalMenuActionsViewModel {
|
||||||
|
override fun pull(pullType: PullType) = tabState.safeProcessing(
|
||||||
|
refreshType = RefreshType.ALL_DATA,
|
||||||
|
title = "Pulling",
|
||||||
|
subtitle = "Pulling changes from the remote branch to the current branch",
|
||||||
|
refreshEvenIfCrashes = true,
|
||||||
|
taskType = TaskType.PULL,
|
||||||
|
) { git ->
|
||||||
|
pullBranchUseCase(git, pullType)
|
||||||
|
|
||||||
|
positiveNotification("Pull completed")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fetchAll() = tabState.safeProcessing(
|
||||||
|
refreshType = RefreshType.ALL_DATA,
|
||||||
|
title = "Fetching",
|
||||||
|
subtitle = "Updating references from the remote repositories...",
|
||||||
|
isCancellable = false,
|
||||||
|
refreshEvenIfCrashes = true,
|
||||||
|
taskType = TaskType.FETCH,
|
||||||
|
) { git ->
|
||||||
|
fetchAllRemotesUseCase(git)
|
||||||
|
|
||||||
|
positiveNotification("Fetch all completed")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun push(force: Boolean, pushTags: Boolean) = tabState.safeProcessing(
|
||||||
|
refreshType = RefreshType.ALL_DATA,
|
||||||
|
title = "Push",
|
||||||
|
subtitle = "Pushing current branch to the remote repository",
|
||||||
|
isCancellable = false,
|
||||||
|
refreshEvenIfCrashes = true,
|
||||||
|
taskType = TaskType.PUSH,
|
||||||
|
) { git ->
|
||||||
|
pushBranchUseCase(git, force, pushTags)
|
||||||
|
|
||||||
|
positiveNotification("Push completed")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stash() = tabState.safeProcessing(
|
||||||
|
refreshType = RefreshType.UNCOMMITTED_CHANGES_AND_LOG,
|
||||||
|
taskType = TaskType.STASH,
|
||||||
|
) { git ->
|
||||||
|
if (stashChangesUseCase(git, null)) {
|
||||||
|
positiveNotification("Changes stashed")
|
||||||
|
} else {
|
||||||
|
errorNotification("There are no changes to stash")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popStash() = tabState.safeProcessing(
|
||||||
|
refreshType = RefreshType.UNCOMMITTED_CHANGES_AND_LOG,
|
||||||
|
refreshEvenIfCrashes = true,
|
||||||
|
taskType = TaskType.POP_STASH,
|
||||||
|
) { git ->
|
||||||
|
popLastStashUseCase(git)
|
||||||
|
|
||||||
|
positiveNotification("Stash popped")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun openTerminal() = tabState.runOperation(
|
||||||
|
refreshType = RefreshType.NONE
|
||||||
|
) { git ->
|
||||||
|
openRepositoryInTerminalUseCase(git.repository.workTree.absolutePath)
|
||||||
|
}
|
||||||
|
}
|
@ -20,80 +20,11 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
class MenuViewModel @Inject constructor(
|
class MenuViewModel @Inject constructor(
|
||||||
private val tabState: TabState,
|
private val tabState: TabState,
|
||||||
private val pullBranchUseCase: PullBranchUseCase,
|
private val globalMenuActionsViewModel: GlobalMenuActionsViewModel,
|
||||||
private val pushBranchUseCase: PushBranchUseCase,
|
|
||||||
private val fetchAllRemotesUseCase: FetchAllRemotesUseCase,
|
|
||||||
private val popLastStashUseCase: PopLastStashUseCase,
|
|
||||||
private val stashChangesUseCase: StashChangesUseCase,
|
|
||||||
private val openRepositoryInTerminalUseCase: OpenRepositoryInTerminalUseCase,
|
|
||||||
settings: AppSettingsRepository,
|
settings: AppSettingsRepository,
|
||||||
appStateManager: AppStateManager,
|
appStateManager: AppStateManager,
|
||||||
) {
|
): IGlobalMenuActionsViewModel by globalMenuActionsViewModel {
|
||||||
val isPullWithRebaseDefault = settings.pullRebaseFlow
|
val isPullWithRebaseDefault = settings.pullRebaseFlow
|
||||||
val lastLoadedTabs = appStateManager.latestOpenedRepositoriesPaths
|
val lastLoadedTabs = appStateManager.latestOpenedRepositoriesPaths
|
||||||
|
|
||||||
fun pull(pullType: PullType) = tabState.safeProcessing(
|
|
||||||
refreshType = RefreshType.ALL_DATA,
|
|
||||||
title = "Pulling",
|
|
||||||
subtitle = "Pulling changes from the remote branch to the current branch",
|
|
||||||
refreshEvenIfCrashes = true,
|
|
||||||
taskType = TaskType.PULL,
|
|
||||||
) { git ->
|
|
||||||
pullBranchUseCase(git, pullType)
|
|
||||||
|
|
||||||
positiveNotification("Pull completed")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun fetchAll() = tabState.safeProcessing(
|
|
||||||
refreshType = RefreshType.ALL_DATA,
|
|
||||||
title = "Fetching",
|
|
||||||
subtitle = "Updating references from the remote repositories...",
|
|
||||||
isCancellable = false,
|
|
||||||
refreshEvenIfCrashes = true,
|
|
||||||
taskType = TaskType.FETCH,
|
|
||||||
) { git ->
|
|
||||||
fetchAllRemotesUseCase(git)
|
|
||||||
|
|
||||||
positiveNotification("Fetch all completed")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun push(force: Boolean = false, pushTags: Boolean = false) = tabState.safeProcessing(
|
|
||||||
refreshType = RefreshType.ALL_DATA,
|
|
||||||
title = "Push",
|
|
||||||
subtitle = "Pushing current branch to the remote repository",
|
|
||||||
isCancellable = false,
|
|
||||||
refreshEvenIfCrashes = true,
|
|
||||||
taskType = TaskType.PUSH,
|
|
||||||
) { git ->
|
|
||||||
pushBranchUseCase(git, force, pushTags)
|
|
||||||
|
|
||||||
positiveNotification("Push completed")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stash() = tabState.safeProcessing(
|
|
||||||
refreshType = RefreshType.UNCOMMITTED_CHANGES_AND_LOG,
|
|
||||||
taskType = TaskType.STASH,
|
|
||||||
) { git ->
|
|
||||||
if (stashChangesUseCase(git, null)) {
|
|
||||||
positiveNotification("Changes stashed")
|
|
||||||
} else {
|
|
||||||
errorNotification("There are no changes to stash")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun popStash() = tabState.safeProcessing(
|
|
||||||
refreshType = RefreshType.UNCOMMITTED_CHANGES_AND_LOG,
|
|
||||||
refreshEvenIfCrashes = true,
|
|
||||||
taskType = TaskType.POP_STASH,
|
|
||||||
) { git ->
|
|
||||||
popLastStashUseCase(git)
|
|
||||||
|
|
||||||
positiveNotification("Stash popped")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun openTerminal() = tabState.runOperation(
|
|
||||||
refreshType = RefreshType.NONE
|
|
||||||
) { git ->
|
|
||||||
openRepositoryInTerminalUseCase(git.repository.workTree.absolutePath)
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -22,7 +22,6 @@ import com.jetpackduba.gitnuro.managers.newErrorNow
|
|||||||
import com.jetpackduba.gitnuro.models.AuthorInfoSimple
|
import com.jetpackduba.gitnuro.models.AuthorInfoSimple
|
||||||
import com.jetpackduba.gitnuro.models.errorNotification
|
import com.jetpackduba.gitnuro.models.errorNotification
|
||||||
import com.jetpackduba.gitnuro.models.positiveNotification
|
import com.jetpackduba.gitnuro.models.positiveNotification
|
||||||
import com.jetpackduba.gitnuro.models.warningNotification
|
|
||||||
import com.jetpackduba.gitnuro.system.OpenFilePickerUseCase
|
import com.jetpackduba.gitnuro.system.OpenFilePickerUseCase
|
||||||
import com.jetpackduba.gitnuro.system.OpenUrlInBrowserUseCase
|
import com.jetpackduba.gitnuro.system.OpenUrlInBrowserUseCase
|
||||||
import com.jetpackduba.gitnuro.system.PickerType
|
import com.jetpackduba.gitnuro.system.PickerType
|
||||||
@ -77,7 +76,9 @@ class TabViewModel @Inject constructor(
|
|||||||
private val tabScope: CoroutineScope,
|
private val tabScope: CoroutineScope,
|
||||||
private val verticalSplitPaneConfig: VerticalSplitPaneConfig,
|
private val verticalSplitPaneConfig: VerticalSplitPaneConfig,
|
||||||
val tabViewModelsProvider: TabViewModelsProvider,
|
val tabViewModelsProvider: TabViewModelsProvider,
|
||||||
) : IVerticalSplitPaneConfig by verticalSplitPaneConfig {
|
private val globalMenuActionsViewModel: GlobalMenuActionsViewModel,
|
||||||
|
) : IVerticalSplitPaneConfig by verticalSplitPaneConfig,
|
||||||
|
IGlobalMenuActionsViewModel by globalMenuActionsViewModel {
|
||||||
var initialPath: String? = null // Stores the path that should be opened when the tab is selected
|
var initialPath: String? = null // Stores the path that should be opened when the tab is selected
|
||||||
val errorsManager: ErrorsManager = tabState.errorsManager
|
val errorsManager: ErrorsManager = tabState.errorsManager
|
||||||
val selectedItem: StateFlow<SelectedItem> = tabState.selectedItem
|
val selectedItem: StateFlow<SelectedItem> = tabState.selectedItem
|
||||||
|
Loading…
Reference in New Issue
Block a user