Multiple blame improvements
- Clicking on a diff now minimizes the blame - Clicking on a commit of the blame will select this commit in the log and show the commit changes. - Unified design of Diff and Blame
This commit is contained in:
parent
cb3fe17fee
commit
eca68aaf07
4 changed files with 125 additions and 27 deletions
|
@ -1,10 +1,10 @@
|
|||
@file:Suppress("UNUSED_PARAMETER")
|
||||
|
||||
package app.ui
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
|
@ -18,13 +18,16 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import app.extensions.lineAt
|
||||
import app.theme.primaryTextColor
|
||||
import app.ui.components.PrimaryButton
|
||||
import app.ui.components.ScrollableLazyColumn
|
||||
import org.eclipse.jgit.blame.BlameResult
|
||||
import org.eclipse.jgit.revwalk.RevCommit
|
||||
|
||||
@Composable
|
||||
fun Blame(
|
||||
filePath: String,
|
||||
blameResult: BlameResult,
|
||||
onSelectCommit: (RevCommit) -> Unit,
|
||||
onClose: () -> Unit,
|
||||
) {
|
||||
Column {
|
||||
|
@ -45,7 +48,11 @@ fun Blame(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.width(200.dp).fillMaxHeight().background(MaterialTheme.colors.surface),
|
||||
modifier = Modifier
|
||||
.width(200.dp)
|
||||
.fillMaxHeight()
|
||||
.background(MaterialTheme.colors.surface)
|
||||
.clickable { onSelectCommit(commit) },
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Text(
|
||||
|
@ -77,13 +84,68 @@ fun Blame(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MinimizedBlame(
|
||||
filePath: String,
|
||||
onExpand: () -> Unit,
|
||||
onClose: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(50.dp)
|
||||
.background(MaterialTheme.colors.surface),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Minimized file blame",
|
||||
color = MaterialTheme.colors.primaryTextColor,
|
||||
maxLines = 1,
|
||||
fontSize = 10.sp,
|
||||
)
|
||||
Text(
|
||||
text = filePath,
|
||||
color = MaterialTheme.colors.primaryTextColor,
|
||||
maxLines = 1,
|
||||
fontSize = 12.sp,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
PrimaryButton(
|
||||
onClick = onExpand,
|
||||
text = "Show",
|
||||
)
|
||||
|
||||
IconButton(
|
||||
onClick = onClose,
|
||||
modifier = Modifier.padding(horizontal = 16.dp)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource("close.svg"),
|
||||
contentDescription = "Close blame",
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colors.primaryTextColor),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Header(
|
||||
filePath: String,
|
||||
onClose: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().background(MaterialTheme.colors.surface),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(50.dp)
|
||||
.padding(start = 8.dp, end = 8.dp, top = 8.dp)
|
||||
.background(MaterialTheme.colors.surface),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
|
@ -94,12 +156,13 @@ private fun Header(
|
|||
)
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
IconButton(
|
||||
onClick = onClose
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource("close.svg"),
|
||||
contentDescription = "Close diff",
|
||||
contentDescription = "Close blame",
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colors.primaryTextColor),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -54,7 +54,6 @@ fun Diff(
|
|||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(8.dp)
|
||||
.background(MaterialTheme.colors.background)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
|
@ -228,7 +227,7 @@ fun HunkHeader(
|
|||
Row(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colors.surface)
|
||||
.padding(vertical = 4.dp)
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
@ -282,6 +281,8 @@ fun DiffHeader(
|
|||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(50.dp)
|
||||
.padding(start = 8.dp, end = 8.dp, top = 8.dp)
|
||||
.background(MaterialTheme.colors.surface),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
|
@ -351,6 +352,7 @@ fun DiffLine(highestLineNumberLength: Int, line: Line) {
|
|||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 8.dp)
|
||||
.background(backgroundColor)
|
||||
.height(IntrinsicSize.Min)
|
||||
) {
|
||||
|
|
|
@ -118,25 +118,38 @@ fun RepoContent(
|
|||
shape = RoundedCornerShape(4.dp)
|
||||
)
|
||||
) {
|
||||
if (blameState is BlameState.Loaded) {
|
||||
if (blameState is BlameState.Loaded && !blameState.isMinimized) {
|
||||
Blame(
|
||||
filePath = blameState.filePath,
|
||||
blameResult = blameState.blameResult,
|
||||
onClose = { tabViewModel.resetBlameState() }
|
||||
onClose = { tabViewModel.resetBlameState() },
|
||||
onSelectCommit = { tabViewModel.selectCommit(it) }
|
||||
)
|
||||
} else {
|
||||
when (diffSelected) {
|
||||
null -> {
|
||||
Log(
|
||||
logViewModel = tabViewModel.logViewModel,
|
||||
selectedItem = selectedItem,
|
||||
repositoryState = repositoryState,
|
||||
)
|
||||
Column {
|
||||
Box(modifier = Modifier.weight(1f, true)) {
|
||||
when (diffSelected) {
|
||||
null -> {
|
||||
Log(
|
||||
logViewModel = tabViewModel.logViewModel,
|
||||
selectedItem = selectedItem,
|
||||
repositoryState = repositoryState,
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
Diff(
|
||||
diffViewModel = tabViewModel.diffViewModel,
|
||||
onCloseDiffView = { tabViewModel.newDiffSelected = null })
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Diff(
|
||||
diffViewModel = tabViewModel.diffViewModel,
|
||||
onCloseDiffView = { tabViewModel.newDiffSelected = null })
|
||||
|
||||
if (blameState is BlameState.Loaded) { // BlameState.isMinimized is true here
|
||||
MinimizedBlame(
|
||||
filePath = blameState.filePath,
|
||||
onExpand = { tabViewModel.expandBlame() },
|
||||
onClose = { tabViewModel.resetBlameState() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -155,10 +168,7 @@ fun RepoContent(
|
|||
selectedEntryType = diffSelected,
|
||||
repositoryState = repositoryState,
|
||||
onStagedDiffEntrySelected = { diffEntry ->
|
||||
// TODO: Instead of resetting the state, create a new one where the blame
|
||||
// is "on hold". In this state we can show a bar at the bottom so the user
|
||||
// can click on it and return to the blame
|
||||
tabViewModel.resetBlameState()
|
||||
tabViewModel.minimizeBlame()
|
||||
|
||||
tabViewModel.newDiffSelected = if (diffEntry != null) {
|
||||
if (repositoryState == RepositoryState.SAFE)
|
||||
|
@ -170,7 +180,7 @@ fun RepoContent(
|
|||
}
|
||||
},
|
||||
onUnstagedDiffEntrySelected = { diffEntry ->
|
||||
tabViewModel.resetBlameState()
|
||||
tabViewModel.minimizeBlame()
|
||||
|
||||
if (repositoryState == RepositoryState.SAFE)
|
||||
tabViewModel.newDiffSelected = DiffEntryType.SafeUnstagedDiff(diffEntry)
|
||||
|
@ -185,7 +195,7 @@ fun RepoContent(
|
|||
selectedItem = safeSelectedItem,
|
||||
diffSelected = diffSelected,
|
||||
onDiffSelected = { diffEntry ->
|
||||
tabViewModel.resetBlameState()
|
||||
tabViewModel.minimizeBlame()
|
||||
tabViewModel.newDiffSelected = DiffEntryType.CommitDiff(diffEntry)
|
||||
},
|
||||
onBlame = { tabViewModel.blameFile(it) }
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.eclipse.jgit.api.Git
|
|||
import org.eclipse.jgit.blame.BlameResult
|
||||
import org.eclipse.jgit.lib.Repository
|
||||
import org.eclipse.jgit.lib.RepositoryState
|
||||
import org.eclipse.jgit.revwalk.RevCommit
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
@ -340,6 +341,26 @@ class TabViewModel @Inject constructor(
|
|||
fun resetBlameState() {
|
||||
_blameState.value = BlameState.None
|
||||
}
|
||||
|
||||
fun expandBlame() {
|
||||
val blameState = _blameState.value
|
||||
|
||||
if(blameState is BlameState.Loaded && blameState.isMinimized) {
|
||||
_blameState.value = blameState.copy(isMinimized = false)
|
||||
}
|
||||
}
|
||||
|
||||
fun minimizeBlame() {
|
||||
val blameState = _blameState.value
|
||||
|
||||
if(blameState is BlameState.Loaded && !blameState.isMinimized) {
|
||||
_blameState.value = blameState.copy(isMinimized = true)
|
||||
}
|
||||
}
|
||||
|
||||
fun selectCommit(commit: RevCommit) {
|
||||
tabState.newSelectedItem(SelectedItem.Commit(commit))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -352,6 +373,8 @@ sealed class RepositorySelectionStatus {
|
|||
|
||||
sealed interface BlameState {
|
||||
data class Loading(val filePath: String) : BlameState
|
||||
data class Loaded(val filePath: String, val blameResult: BlameResult) : BlameState
|
||||
|
||||
data class Loaded(val filePath: String, val blameResult: BlameResult, val isMinimized: Boolean = false) : BlameState
|
||||
|
||||
object None : BlameState
|
||||
}
|
Loading…
Reference in a new issue