Implemented context menu for stash operations
Moved selected item to TabState, so every ViewModel can update the current selected tab state without having to use callbacks to the RepoOpened component. This allows to set currently selected item to "None" when droping a stash that has been selected
This commit is contained in:
parent
fff18b7fef
commit
02313fe632
@ -3,6 +3,7 @@ package app.git
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class StashManager @Inject constructor() {
|
class StashManager @Inject constructor() {
|
||||||
@ -22,9 +23,29 @@ class StashManager @Inject constructor() {
|
|||||||
.call()
|
.call()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun popStash(git: Git, stash: RevCommit) = withContext(Dispatchers.IO) {
|
||||||
|
applyStash(git, stash)
|
||||||
|
deleteStash(git, stash)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getStashList(git: Git) = withContext(Dispatchers.IO) {
|
suspend fun getStashList(git: Git) = withContext(Dispatchers.IO) {
|
||||||
return@withContext git
|
return@withContext git
|
||||||
.stashList()
|
.stashList()
|
||||||
.call()
|
.call()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun applyStash(git: Git, stashInfo: RevCommit) = withContext(Dispatchers.IO) {
|
||||||
|
git.stashApply()
|
||||||
|
.setStashRef(stashInfo.name)
|
||||||
|
.call()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun deleteStash(git: Git, stashInfo: RevCommit) = withContext(Dispatchers.IO) {
|
||||||
|
val stashList = getStashList(git)
|
||||||
|
val indexOfStashToDelete = stashList.indexOf(stashInfo)
|
||||||
|
|
||||||
|
git.stashDrop()
|
||||||
|
.setStashRef(indexOfStashToDelete)
|
||||||
|
.call()
|
||||||
|
}
|
||||||
}
|
}
|
@ -281,10 +281,8 @@ class StatusManager @Inject constructor(
|
|||||||
suspend fun getStatusSummary(git: Git, currentBranch: Ref?, repositoryState: RepositoryState): StatusSummary {
|
suspend fun getStatusSummary(git: Git, currentBranch: Ref?, repositoryState: RepositoryState): StatusSummary {
|
||||||
val staged = getStaged(git, currentBranch, repositoryState)
|
val staged = getStaged(git, currentBranch, repositoryState)
|
||||||
val allChanges = staged.toMutableList()
|
val allChanges = staged.toMutableList()
|
||||||
println("Staged: $staged")
|
|
||||||
|
|
||||||
val unstaged = getUnstaged(git, repositoryState)
|
val unstaged = getUnstaged(git, repositoryState)
|
||||||
println("Unstaged: $unstaged")
|
|
||||||
|
|
||||||
allChanges.addAll(unstaged)
|
allChanges.addAll(unstaged)
|
||||||
val groupedChanges = allChanges.groupBy {
|
val groupedChanges = allChanges.groupBy {
|
||||||
|
@ -3,6 +3,7 @@ package app.git
|
|||||||
import app.ErrorsManager
|
import app.ErrorsManager
|
||||||
import app.di.TabScope
|
import app.di.TabScope
|
||||||
import app.newErrorNow
|
import app.newErrorNow
|
||||||
|
import app.ui.SelectedItem
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
@ -14,6 +15,8 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.lib.ObjectId
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.coroutines.cancellation.CancellationException
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
|
|
||||||
@ -21,12 +24,14 @@ import kotlin.coroutines.cancellation.CancellationException
|
|||||||
class TabState @Inject constructor(
|
class TabState @Inject constructor(
|
||||||
val errorsManager: ErrorsManager,
|
val errorsManager: ErrorsManager,
|
||||||
) {
|
) {
|
||||||
|
private val _selectedItem = MutableStateFlow<SelectedItem>(SelectedItem.None)
|
||||||
|
val selectedItem: StateFlow<SelectedItem> = _selectedItem
|
||||||
|
|
||||||
var git: Git? = null
|
var git: Git? = null
|
||||||
val safeGit: Git
|
val safeGit: Git
|
||||||
get() {
|
get() {
|
||||||
val git = this.git
|
val git = this.git
|
||||||
if (git == null) {
|
if (git == null) {
|
||||||
// _repositorySelectionStatus.value = RepositorySelectionStatus.None
|
|
||||||
throw CancellationException("Null git object")
|
throw CancellationException("Null git object")
|
||||||
} else
|
} else
|
||||||
return git
|
return git
|
||||||
@ -128,11 +133,44 @@ class TabState @Inject constructor(
|
|||||||
_refreshData.emit(refreshType)
|
_refreshData.emit(refreshType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun newSelectedStash(stash: RevCommit) {
|
||||||
|
newSelectedItem(SelectedItem.Stash(stash))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun noneSelected() {
|
||||||
|
newSelectedItem(SelectedItem.None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newSelectedRef(objectId: ObjectId?) = runOperation(
|
||||||
|
refreshType = RefreshType.NONE,
|
||||||
|
) { git ->
|
||||||
|
if (objectId == null) {
|
||||||
|
newSelectedItem(SelectedItem.None)
|
||||||
|
} else {
|
||||||
|
val commit = findCommit(git, objectId)
|
||||||
|
newSelectedItem(SelectedItem.Ref(commit))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findCommit(git: Git, objectId: ObjectId): RevCommit {
|
||||||
|
return git.repository.parseCommit(objectId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newSelectedItem(selectedItem: SelectedItem) {
|
||||||
|
_selectedItem.value = selectedItem
|
||||||
|
println(selectedItem)
|
||||||
|
// if (selectedItem is SelectedItem.CommitBasedItem) {
|
||||||
|
// commitChangesViewModel.loadChanges(selectedItem.revCommit)
|
||||||
|
// }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class RefreshType {
|
enum class RefreshType {
|
||||||
NONE,
|
NONE,
|
||||||
ALL_DATA,
|
ALL_DATA,
|
||||||
ONLY_LOG,
|
ONLY_LOG,
|
||||||
|
STASHES,
|
||||||
UNCOMMITED_CHANGES,
|
UNCOMMITED_CHANGES,
|
||||||
|
UNCOMMITED_CHANGES_AND_LOG,
|
||||||
}
|
}
|
@ -22,7 +22,6 @@ import org.eclipse.jgit.lib.Ref
|
|||||||
@Composable
|
@Composable
|
||||||
fun Branches(
|
fun Branches(
|
||||||
branchesViewModel: BranchesViewModel,
|
branchesViewModel: BranchesViewModel,
|
||||||
onBranchClicked: (Ref) -> Unit,
|
|
||||||
) {
|
) {
|
||||||
val branches by branchesViewModel.branches.collectAsState()
|
val branches by branchesViewModel.branches.collectAsState()
|
||||||
val currentBranch by branchesViewModel.currentBranch.collectAsState()
|
val currentBranch by branchesViewModel.currentBranch.collectAsState()
|
||||||
@ -37,7 +36,7 @@ fun Branches(
|
|||||||
BranchLineEntry(
|
BranchLineEntry(
|
||||||
branch = branch,
|
branch = branch,
|
||||||
isCurrentBranch = currentBranch == branch.name,
|
isCurrentBranch = currentBranch == branch.name,
|
||||||
onBranchClicked = { onBranchClicked(branch) },
|
onBranchClicked = { branchesViewModel.selectBranch(branch) },
|
||||||
onCheckoutBranch = { branchesViewModel.checkoutRef(branch) },
|
onCheckoutBranch = { branchesViewModel.checkoutRef(branch) },
|
||||||
onMergeBranch = { setMergeBranch(branch) },
|
onMergeBranch = { setMergeBranch(branch) },
|
||||||
onDeleteBranch = { branchesViewModel.deleteBranch(branch) },
|
onDeleteBranch = { branchesViewModel.deleteBranch(branch) },
|
||||||
|
@ -8,10 +8,7 @@ import androidx.compose.foundation.rememberScrollState
|
|||||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
@ -34,8 +31,13 @@ import org.eclipse.jgit.revwalk.RevCommit
|
|||||||
@Composable
|
@Composable
|
||||||
fun CommitChanges(
|
fun CommitChanges(
|
||||||
commitChangesViewModel: CommitChangesViewModel,
|
commitChangesViewModel: CommitChangesViewModel,
|
||||||
onDiffSelected: (DiffEntry) -> Unit
|
onDiffSelected: (DiffEntry) -> Unit,
|
||||||
|
selectedItem: SelectedItem.CommitBasedItem
|
||||||
) {
|
) {
|
||||||
|
LaunchedEffect(selectedItem) {
|
||||||
|
commitChangesViewModel.loadChanges(selectedItem.revCommit)
|
||||||
|
}
|
||||||
|
|
||||||
val commitChangesStatusState = commitChangesViewModel.commitChangesStatus.collectAsState()
|
val commitChangesStatusState = commitChangesViewModel.commitChangesStatus.collectAsState()
|
||||||
|
|
||||||
when (val commitChangesStatus = commitChangesStatusState.value) {
|
when (val commitChangesStatus = commitChangesStatusState.value) {
|
||||||
|
@ -21,7 +21,6 @@ import org.eclipse.jgit.lib.Ref
|
|||||||
@Composable
|
@Composable
|
||||||
fun Remotes(
|
fun Remotes(
|
||||||
remotesViewModel: RemotesViewModel,
|
remotesViewModel: RemotesViewModel,
|
||||||
onBranchClicked: (Ref) -> Unit,
|
|
||||||
) {
|
) {
|
||||||
val remotes by remotesViewModel.remotes.collectAsState()
|
val remotes by remotesViewModel.remotes.collectAsState()
|
||||||
|
|
||||||
@ -43,7 +42,7 @@ fun Remotes(
|
|||||||
itemContent = { remoteInfo ->
|
itemContent = { remoteInfo ->
|
||||||
RemoteRow(
|
RemoteRow(
|
||||||
remote = remoteInfo,
|
remote = remoteInfo,
|
||||||
onBranchClicked = { branch -> onBranchClicked(branch) },
|
onBranchClicked = { branch -> remotesViewModel.selectBranch(branch) },
|
||||||
onDeleteBranch = { branch -> remotesViewModel.deleteRemoteBranch(branch) },
|
onDeleteBranch = { branch -> remotesViewModel.deleteRemoteBranch(branch) },
|
||||||
onRemoteClicked = { remotesViewModel.onRemoteClicked(remoteInfo) }
|
onRemoteClicked = { remotesViewModel.onRemoteClicked(remoteInfo) }
|
||||||
)
|
)
|
||||||
|
@ -51,7 +51,7 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
HorizontalSplitPane() {
|
HorizontalSplitPane {
|
||||||
first(minSize = 200.dp) {
|
first(minSize = 200.dp) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -61,27 +61,15 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
|
|||||||
) {
|
) {
|
||||||
Branches(
|
Branches(
|
||||||
branchesViewModel = tabViewModel.branchesViewModel,
|
branchesViewModel = tabViewModel.branchesViewModel,
|
||||||
onBranchClicked = {
|
|
||||||
tabViewModel.newSelectedRef(it.objectId)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
Remotes(
|
Remotes(
|
||||||
remotesViewModel = tabViewModel.remotesViewModel,
|
remotesViewModel = tabViewModel.remotesViewModel,
|
||||||
onBranchClicked = {
|
|
||||||
tabViewModel.newSelectedRef(it.objectId)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
Tags(
|
Tags(
|
||||||
tagsViewModel = tabViewModel.tagsViewModel,
|
tagsViewModel = tabViewModel.tagsViewModel,
|
||||||
onTagClicked = {
|
|
||||||
tabViewModel.newSelectedRef(it.objectId)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
Stashes(
|
Stashes(
|
||||||
stashesViewModel = tabViewModel.stashesViewModel,
|
stashesViewModel = tabViewModel.stashesViewModel,
|
||||||
onStashSelected = { stash ->
|
|
||||||
tabViewModel.newSelectedStash(stash)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,9 +92,6 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
|
|||||||
Log(
|
Log(
|
||||||
logViewModel = tabViewModel.logViewModel,
|
logViewModel = tabViewModel.logViewModel,
|
||||||
selectedItem = selectedItem,
|
selectedItem = selectedItem,
|
||||||
onItemSelected = {
|
|
||||||
tabViewModel.newSelectedItem(it)
|
|
||||||
},
|
|
||||||
repositoryState = repositoryState,
|
repositoryState = repositoryState,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -143,6 +128,7 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
|
|||||||
} else if (safeSelectedItem is SelectedItem.CommitBasedItem) {
|
} else if (safeSelectedItem is SelectedItem.CommitBasedItem) {
|
||||||
CommitChanges(
|
CommitChanges(
|
||||||
commitChangesViewModel = tabViewModel.commitChangesViewModel,
|
commitChangesViewModel = tabViewModel.commitChangesViewModel,
|
||||||
|
selectedItem = safeSelectedItem,
|
||||||
onDiffSelected = { diffEntry ->
|
onDiffSelected = { diffEntry ->
|
||||||
tabViewModel.newDiffSelected = DiffEntryType.CommitDiff(diffEntry)
|
tabViewModel.newDiffSelected = DiffEntryType.CommitDiff(diffEntry)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
|
@file:OptIn(ExperimentalFoundationApi::class)
|
||||||
|
|
||||||
package app.ui
|
package app.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.ContextMenuArea
|
||||||
|
import androidx.compose.foundation.ContextMenuItem
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import app.ui.components.SideMenuPanel
|
import app.ui.components.SideMenuPanel
|
||||||
import app.ui.components.SideMenuSubentry
|
import app.ui.components.SideMenuSubentry
|
||||||
|
import app.ui.context_menu.stashesContextMenuItems
|
||||||
import app.viewmodels.StashStatus
|
import app.viewmodels.StashStatus
|
||||||
import app.viewmodels.StashesViewModel
|
import app.viewmodels.StashesViewModel
|
||||||
import org.eclipse.jgit.revwalk.RevCommit
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
@ -12,7 +18,6 @@ import org.eclipse.jgit.revwalk.RevCommit
|
|||||||
@Composable
|
@Composable
|
||||||
fun Stashes(
|
fun Stashes(
|
||||||
stashesViewModel: StashesViewModel,
|
stashesViewModel: StashesViewModel,
|
||||||
onStashSelected: (commit: RevCommit) -> Unit,
|
|
||||||
) {
|
) {
|
||||||
val stashStatusState = stashesViewModel.stashStatus.collectAsState()
|
val stashStatusState = stashesViewModel.stashStatus.collectAsState()
|
||||||
val stashStatus = stashStatusState.value
|
val stashStatus = stashStatusState.value
|
||||||
@ -22,15 +27,23 @@ fun Stashes(
|
|||||||
else
|
else
|
||||||
listOf()
|
listOf()
|
||||||
|
|
||||||
|
|
||||||
SideMenuPanel(
|
SideMenuPanel(
|
||||||
title = "Stashes",
|
title = "Stashes",
|
||||||
icon = painterResource("stash.svg"),
|
icon = painterResource("stash.svg"),
|
||||||
items = stashList,
|
items = stashList,
|
||||||
itemContent = { stashInfo ->
|
itemContent = { stash ->
|
||||||
StashRow(
|
StashRow(
|
||||||
stash = stashInfo,
|
stash = stash,
|
||||||
onClick = { onStashSelected(stashInfo) }
|
onClick = { stashesViewModel.selectTab(stash) },
|
||||||
|
contextItems = stashesContextMenuItems(
|
||||||
|
onApply = { stashesViewModel.applyStash(stash) },
|
||||||
|
onPop = {
|
||||||
|
stashesViewModel.popStash(stash)
|
||||||
|
},
|
||||||
|
onDelete = {
|
||||||
|
stashesViewModel.deleteStash(stash)
|
||||||
|
},
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -38,10 +51,18 @@ fun Stashes(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun StashRow(stash: RevCommit, onClick: () -> Unit) {
|
private fun StashRow(
|
||||||
SideMenuSubentry(
|
stash: RevCommit,
|
||||||
text = stash.shortMessage,
|
onClick: () -> Unit,
|
||||||
iconResourcePath = "stash.svg",
|
contextItems: List<ContextMenuItem>,
|
||||||
onClick = onClick,
|
) {
|
||||||
)
|
ContextMenuArea(
|
||||||
|
items = { contextItems }
|
||||||
|
) {
|
||||||
|
SideMenuSubentry(
|
||||||
|
text = stash.shortMessage,
|
||||||
|
iconResourcePath = "stash.svg",
|
||||||
|
onClick = onClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
@ -15,7 +15,6 @@ import org.eclipse.jgit.lib.Ref
|
|||||||
@Composable
|
@Composable
|
||||||
fun Tags(
|
fun Tags(
|
||||||
tagsViewModel: TagsViewModel,
|
tagsViewModel: TagsViewModel,
|
||||||
onTagClicked: (Ref) -> Unit,
|
|
||||||
) {
|
) {
|
||||||
val tagsState = tagsViewModel.tags.collectAsState()
|
val tagsState = tagsViewModel.tags.collectAsState()
|
||||||
val tags = tagsState.value
|
val tags = tagsState.value
|
||||||
@ -27,7 +26,7 @@ fun Tags(
|
|||||||
itemContent = { tag ->
|
itemContent = { tag ->
|
||||||
TagRow(
|
TagRow(
|
||||||
tag = tag,
|
tag = tag,
|
||||||
onTagClicked = { onTagClicked(tag) },
|
onTagClicked = { tagsViewModel.selectTag(tag) },
|
||||||
onCheckoutTag = { tagsViewModel.checkoutRef(tag) },
|
onCheckoutTag = { tagsViewModel.checkoutRef(tag) },
|
||||||
onDeleteTag = { tagsViewModel.deleteTag(tag) }
|
onDeleteTag = { tagsViewModel.deleteTag(tag) }
|
||||||
)
|
)
|
||||||
|
26
src/main/kotlin/app/ui/context_menu/StashesContextMenu.kt
Normal file
26
src/main/kotlin/app/ui/context_menu/StashesContextMenu.kt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package app.ui.context_menu
|
||||||
|
|
||||||
|
import androidx.compose.foundation.ContextMenuItem
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
fun stashesContextMenuItems(
|
||||||
|
onApply: () -> Unit,
|
||||||
|
onPop: () -> Unit,
|
||||||
|
onDelete: () -> Unit,
|
||||||
|
): List<ContextMenuItem> {
|
||||||
|
return mutableListOf(
|
||||||
|
ContextMenuItem(
|
||||||
|
label = "Apply stash",
|
||||||
|
onClick = onApply
|
||||||
|
),
|
||||||
|
ContextMenuItem(
|
||||||
|
label = "Pop stash",
|
||||||
|
onClick = onPop
|
||||||
|
),
|
||||||
|
ContextMenuItem(
|
||||||
|
label = "Drop stash",
|
||||||
|
onClick = onDelete
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
@ -72,7 +72,6 @@ private const val CANVAS_MIN_WIDTH = 100
|
|||||||
fun Log(
|
fun Log(
|
||||||
logViewModel: LogViewModel,
|
logViewModel: LogViewModel,
|
||||||
selectedItem: SelectedItem,
|
selectedItem: SelectedItem,
|
||||||
onItemSelected: (SelectedItem) -> Unit,
|
|
||||||
repositoryState: RepositoryState,
|
repositoryState: RepositoryState,
|
||||||
) {
|
) {
|
||||||
val logStatusState = logViewModel.logStatus.collectAsState()
|
val logStatusState = logViewModel.logStatus.collectAsState()
|
||||||
@ -114,7 +113,6 @@ fun Log(
|
|||||||
.background(MaterialTheme.colors.background)
|
.background(MaterialTheme.colors.background)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
// val hasUncommitedChanges by tabViewModel.hasUncommitedChanges.collectAsState()
|
|
||||||
val weightMod = remember { mutableStateOf(0f) }
|
val weightMod = remember { mutableStateOf(0f) }
|
||||||
var graphWidth = (CANVAS_MIN_WIDTH + weightMod.value).dp
|
var graphWidth = (CANVAS_MIN_WIDTH + weightMod.value).dp
|
||||||
|
|
||||||
@ -131,7 +129,6 @@ fun Log(
|
|||||||
.background(MaterialTheme.colors.background)
|
.background(MaterialTheme.colors.background)
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
//TODO: Shouldn't this be an item of the graph?
|
|
||||||
if (hasUncommitedChanges)
|
if (hasUncommitedChanges)
|
||||||
item {
|
item {
|
||||||
UncommitedChangesLine(
|
UncommitedChangesLine(
|
||||||
@ -142,7 +139,7 @@ fun Log(
|
|||||||
weightMod = weightMod,
|
weightMod = weightMod,
|
||||||
repositoryState = repositoryState,
|
repositoryState = repositoryState,
|
||||||
onUncommitedChangesSelected = {
|
onUncommitedChangesSelected = {
|
||||||
onItemSelected(SelectedItem.UncommitedChanges)
|
logViewModel.selectLogLine(SelectedItem.UncommitedChanges)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -160,7 +157,7 @@ fun Log(
|
|||||||
onMergeBranch = { ref -> showLogDialog.value = LogDialog.MergeBranch(ref) },
|
onMergeBranch = { ref -> showLogDialog.value = LogDialog.MergeBranch(ref) },
|
||||||
onRebaseBranch = { ref -> showLogDialog.value = LogDialog.RebaseBranch(ref) },
|
onRebaseBranch = { ref -> showLogDialog.value = LogDialog.RebaseBranch(ref) },
|
||||||
onRevCommitSelected = {
|
onRevCommitSelected = {
|
||||||
onItemSelected(SelectedItem.Commit(graphNode))
|
logViewModel.selectLogLine(SelectedItem.Commit(graphNode))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -71,4 +71,8 @@ class BranchesViewModel @Inject constructor(
|
|||||||
) { git ->
|
) { git ->
|
||||||
rebaseManager.rebaseBranch(git, ref)
|
rebaseManager.rebaseBranch(git, ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun selectBranch(ref: Ref) {
|
||||||
|
tabState.newSelectedRef(ref.objectId)
|
||||||
|
}
|
||||||
}
|
}
|
@ -2,6 +2,7 @@ package app.viewmodels
|
|||||||
|
|
||||||
import app.git.*
|
import app.git.*
|
||||||
import app.git.graph.GraphCommitList
|
import app.git.graph.GraphCommitList
|
||||||
|
import app.ui.SelectedItem
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
@ -143,6 +144,10 @@ class LogViewModel @Inject constructor(
|
|||||||
) { git ->
|
) { git ->
|
||||||
rebaseManager.rebaseBranch(git, ref)
|
rebaseManager.rebaseBranch(git, ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun selectLogLine(selectedItem: SelectedItem) {
|
||||||
|
tabState.newSelectedItem(selectedItem)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class LogStatus {
|
sealed class LogStatus {
|
||||||
|
@ -34,13 +34,13 @@ class MenuViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun stash() = tabState.safeProcessing(
|
fun stash() = tabState.safeProcessing(
|
||||||
refreshType = RefreshType.UNCOMMITED_CHANGES,
|
refreshType = RefreshType.UNCOMMITED_CHANGES_AND_LOG,
|
||||||
) { git ->
|
) { git ->
|
||||||
stashManager.stash(git)
|
stashManager.stash(git)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun popStash() = tabState.safeProcessing(
|
fun popStash() = tabState.safeProcessing(
|
||||||
refreshType = RefreshType.UNCOMMITED_CHANGES,
|
refreshType = RefreshType.UNCOMMITED_CHANGES_AND_LOG,
|
||||||
) { git ->
|
) { git ->
|
||||||
stashManager.popStash(git)
|
stashManager.popStash(git)
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,10 @@ class RemotesViewModel @Inject constructor(
|
|||||||
|
|
||||||
_remotes.value = newRemotesList
|
_remotes.value = newRemotesList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun selectBranch(ref: Ref) {
|
||||||
|
tabState.newSelectedRef(ref.objectId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class RemoteView(val remoteInfo: RemoteInfo, val isExpanded: Boolean)
|
data class RemoteView(val remoteInfo: RemoteInfo, val isExpanded: Boolean)
|
@ -1,6 +1,9 @@
|
|||||||
package app.viewmodels
|
package app.viewmodels
|
||||||
|
|
||||||
|
import app.git.RefreshType
|
||||||
import app.git.StashManager
|
import app.git.StashManager
|
||||||
|
import app.git.TabState
|
||||||
|
import app.ui.SelectedItem
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
@ -9,6 +12,7 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
class StashesViewModel @Inject constructor(
|
class StashesViewModel @Inject constructor(
|
||||||
private val stashManager: StashManager,
|
private val stashManager: StashManager,
|
||||||
|
private val tabState: TabState,
|
||||||
) {
|
) {
|
||||||
private val _stashStatus = MutableStateFlow<StashStatus>(StashStatus.Loaded(listOf()))
|
private val _stashStatus = MutableStateFlow<StashStatus>(StashStatus.Loaded(listOf()))
|
||||||
val stashStatus: StateFlow<StashStatus>
|
val stashStatus: StateFlow<StashStatus>
|
||||||
@ -23,6 +27,41 @@ class StashesViewModel @Inject constructor(
|
|||||||
suspend fun refresh(git: Git) {
|
suspend fun refresh(git: Git) {
|
||||||
loadStashes(git)
|
loadStashes(git)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun applyStash(stashInfo: RevCommit) = tabState.safeProcessing(
|
||||||
|
refreshType = RefreshType.UNCOMMITED_CHANGES_AND_LOG,
|
||||||
|
) { git ->
|
||||||
|
stashManager.applyStash(git, stashInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun popStash(stash: RevCommit) = tabState.safeProcessing(
|
||||||
|
refreshType = RefreshType.UNCOMMITED_CHANGES_AND_LOG,
|
||||||
|
) { git ->
|
||||||
|
stashManager.popStash(git, stash)
|
||||||
|
|
||||||
|
stashDropped(stash)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteStash(stash: RevCommit) = tabState.safeProcessing(
|
||||||
|
refreshType = RefreshType.STASHES,
|
||||||
|
) { git ->
|
||||||
|
stashManager.deleteStash(git, stash)
|
||||||
|
stashDropped(stash)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun selectTab(stash: RevCommit) {
|
||||||
|
tabState.newSelectedStash(stash)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stashDropped(stash: RevCommit) {
|
||||||
|
val selectedValue = tabState.selectedItem.value
|
||||||
|
if (
|
||||||
|
selectedValue is SelectedItem.Stash &&
|
||||||
|
selectedValue.revCommit.name == stash.name
|
||||||
|
) {
|
||||||
|
tabState.noneSelected()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -108,6 +108,22 @@ class StatusViewModel @Inject constructor(
|
|||||||
loadHasUncommitedChanges(git)
|
loadHasUncommitedChanges(git)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if there are uncommited changes and returns if the state has changed (
|
||||||
|
*/
|
||||||
|
suspend fun updateHasUncommitedChanges(git: Git): Boolean {
|
||||||
|
val hadUncommitedChanges = this.lastUncommitedChangesState
|
||||||
|
|
||||||
|
loadStatus(git)
|
||||||
|
loadHasUncommitedChanges(git)
|
||||||
|
|
||||||
|
val hasNowUncommitedChanges = this.lastUncommitedChangesState
|
||||||
|
hasPreviousCommits = logManager.hasPreviousCommits(git)
|
||||||
|
|
||||||
|
// Return true to update the log only if the uncommitedChanges status has changed
|
||||||
|
return (hasNowUncommitedChanges != hadUncommitedChanges)
|
||||||
|
}
|
||||||
|
|
||||||
fun continueRebase() = tabState.safeProcessing(
|
fun continueRebase() = tabState.safeProcessing(
|
||||||
refreshType = RefreshType.ALL_DATA,
|
refreshType = RefreshType.ALL_DATA,
|
||||||
) { git ->
|
) { git ->
|
||||||
|
@ -41,8 +41,7 @@ class TabViewModel @Inject constructor(
|
|||||||
private val fileChangesWatcher: FileChangesWatcher,
|
private val fileChangesWatcher: FileChangesWatcher,
|
||||||
) {
|
) {
|
||||||
val errorsManager: ErrorsManager = tabState.errorsManager
|
val errorsManager: ErrorsManager = tabState.errorsManager
|
||||||
private val _selectedItem = MutableStateFlow<SelectedItem>(SelectedItem.None)
|
val selectedItem: StateFlow<SelectedItem> = tabState.selectedItem
|
||||||
val selectedItem: StateFlow<SelectedItem> = _selectedItem
|
|
||||||
|
|
||||||
private val credentialsStateManager = CredentialsStateManager
|
private val credentialsStateManager = CredentialsStateManager
|
||||||
|
|
||||||
@ -75,12 +74,20 @@ class TabViewModel @Inject constructor(
|
|||||||
RefreshType.NONE -> println("Not refreshing...")
|
RefreshType.NONE -> println("Not refreshing...")
|
||||||
RefreshType.ALL_DATA -> refreshRepositoryInfo()
|
RefreshType.ALL_DATA -> refreshRepositoryInfo()
|
||||||
RefreshType.ONLY_LOG -> refreshLog()
|
RefreshType.ONLY_LOG -> refreshLog()
|
||||||
|
RefreshType.STASHES -> refreshStashes()
|
||||||
RefreshType.UNCOMMITED_CHANGES -> checkUncommitedChanges()
|
RefreshType.UNCOMMITED_CHANGES -> checkUncommitedChanges()
|
||||||
|
RefreshType.UNCOMMITED_CHANGES_AND_LOG -> checkUncommitedChanges(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun refreshStashes() = tabState.runOperation(
|
||||||
|
refreshType = RefreshType.NONE
|
||||||
|
) { git ->
|
||||||
|
stashesViewModel.refresh(git)
|
||||||
|
}
|
||||||
|
|
||||||
private fun refreshLog() = tabState.runOperation(
|
private fun refreshLog() = tabState.runOperation(
|
||||||
refreshType = RefreshType.NONE,
|
refreshType = RefreshType.NONE,
|
||||||
) { git ->
|
) { git ->
|
||||||
@ -147,11 +154,18 @@ class TabViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun checkUncommitedChanges() = tabState.runOperation(
|
private suspend fun checkUncommitedChanges(fullUpdateLog: Boolean = false) = tabState.runOperation(
|
||||||
refreshType = RefreshType.NONE,
|
refreshType = RefreshType.NONE,
|
||||||
) { git ->
|
) { git ->
|
||||||
statusViewModel.refresh(git)
|
val uncommitedChangesStateChanged = statusViewModel.updateHasUncommitedChanges(git)
|
||||||
logViewModel.refreshUncommitedChanges(git)
|
|
||||||
|
println("Has uncommitedChangesStateChanged $uncommitedChangesStateChanged")
|
||||||
|
|
||||||
|
// Update the log only if the uncommitedChanges status has changed or requested
|
||||||
|
if (uncommitedChangesStateChanged || fullUpdateLog)
|
||||||
|
logViewModel.refresh(git)
|
||||||
|
else
|
||||||
|
logViewModel.refreshUncommitedChanges(git)
|
||||||
|
|
||||||
updateDiffEntry()
|
updateDiffEntry()
|
||||||
|
|
||||||
@ -195,10 +209,6 @@ class TabViewModel @Inject constructor(
|
|||||||
remoteOperationsManager.clone(directory, url)
|
remoteOperationsManager.clone(directory, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findCommit(git: Git, objectId: ObjectId): RevCommit {
|
|
||||||
return git.repository.parseCommit(objectId)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateDiffEntry() {
|
private fun updateDiffEntry() {
|
||||||
val diffSelected = diffSelected.value
|
val diffSelected = diffSelected.value
|
||||||
|
|
||||||
@ -206,29 +216,6 @@ class TabViewModel @Inject constructor(
|
|||||||
diffViewModel.updateDiff(diffSelected)
|
diffViewModel.updateDiff(diffSelected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun newSelectedRef(objectId: ObjectId?) = tabState.runOperation(
|
|
||||||
refreshType = RefreshType.NONE,
|
|
||||||
) { git ->
|
|
||||||
if (objectId == null) {
|
|
||||||
newSelectedItem(SelectedItem.None)
|
|
||||||
} else {
|
|
||||||
val commit = findCommit(git, objectId)
|
|
||||||
newSelectedItem(SelectedItem.Ref(commit))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun newSelectedStash(stash: RevCommit) {
|
|
||||||
newSelectedItem(SelectedItem.Stash(stash))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun newSelectedItem(selectedItem: SelectedItem) {
|
|
||||||
_selectedItem.value = selectedItem
|
|
||||||
|
|
||||||
if (selectedItem is SelectedItem.CommitBasedItem) {
|
|
||||||
commitChangesViewModel.loadChanges(selectedItem.revCommit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,6 +39,10 @@ class TagsViewModel @Inject constructor(
|
|||||||
tagsManager.deleteTag(git, tag)
|
tagsManager.deleteTag(git, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun selectTag(tag: Ref) {
|
||||||
|
tabState.newSelectedRef(tag.objectId)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun refresh(git: Git) {
|
suspend fun refresh(git: Git) {
|
||||||
loadTags(git)
|
loadTags(git)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user