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:
Abdelilah El Aissaoui 2022-05-27 19:50:30 +02:00
parent cb3fe17fee
commit eca68aaf07
4 changed files with 125 additions and 27 deletions

View File

@ -1,10 +1,10 @@
@file:Suppress("UNUSED_PARAMETER")
package app.ui package app.ui
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.IconButton import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material.Text
@ -18,13 +18,16 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import app.extensions.lineAt import app.extensions.lineAt
import app.theme.primaryTextColor import app.theme.primaryTextColor
import app.ui.components.PrimaryButton
import app.ui.components.ScrollableLazyColumn import app.ui.components.ScrollableLazyColumn
import org.eclipse.jgit.blame.BlameResult import org.eclipse.jgit.blame.BlameResult
import org.eclipse.jgit.revwalk.RevCommit
@Composable @Composable
fun Blame( fun Blame(
filePath: String, filePath: String,
blameResult: BlameResult, blameResult: BlameResult,
onSelectCommit: (RevCommit) -> Unit,
onClose: () -> Unit, onClose: () -> Unit,
) { ) {
Column { Column {
@ -45,7 +48,11 @@ fun Blame(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Column( 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, verticalArrangement = Arrangement.Center,
) { ) {
Text( 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 @Composable
private fun Header( private fun Header(
filePath: String, filePath: String,
onClose: () -> Unit, onClose: () -> Unit,
) { ) {
Row( 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, verticalAlignment = Alignment.CenterVertically,
) { ) {
Text( Text(
@ -94,12 +156,13 @@ private fun Header(
) )
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
IconButton( IconButton(
onClick = onClose onClick = onClose
) { ) {
Image( Image(
painter = painterResource("close.svg"), painter = painterResource("close.svg"),
contentDescription = "Close diff", contentDescription = "Close blame",
colorFilter = ColorFilter.tint(MaterialTheme.colors.primaryTextColor), colorFilter = ColorFilter.tint(MaterialTheme.colors.primaryTextColor),
) )
} }

View File

@ -54,7 +54,6 @@ fun Diff(
Column( Column(
modifier = Modifier modifier = Modifier
.padding(8.dp)
.background(MaterialTheme.colors.background) .background(MaterialTheme.colors.background)
.fillMaxSize() .fillMaxSize()
) { ) {
@ -228,7 +227,7 @@ fun HunkHeader(
Row( Row(
modifier = Modifier modifier = Modifier
.background(MaterialTheme.colors.surface) .background(MaterialTheme.colors.surface)
.padding(vertical = 4.dp) .padding(horizontal = 8.dp, vertical = 4.dp)
.fillMaxWidth(), .fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
@ -282,6 +281,8 @@ fun DiffHeader(
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(50.dp)
.padding(start = 8.dp, end = 8.dp, top = 8.dp)
.background(MaterialTheme.colors.surface), .background(MaterialTheme.colors.surface),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
@ -351,6 +352,7 @@ fun DiffLine(highestLineNumberLength: Int, line: Line) {
} }
Row( Row(
modifier = Modifier modifier = Modifier
.padding(horizontal = 8.dp)
.background(backgroundColor) .background(backgroundColor)
.height(IntrinsicSize.Min) .height(IntrinsicSize.Min)
) { ) {

View File

@ -118,13 +118,16 @@ fun RepoContent(
shape = RoundedCornerShape(4.dp) shape = RoundedCornerShape(4.dp)
) )
) { ) {
if (blameState is BlameState.Loaded) { if (blameState is BlameState.Loaded && !blameState.isMinimized) {
Blame( Blame(
filePath = blameState.filePath, filePath = blameState.filePath,
blameResult = blameState.blameResult, blameResult = blameState.blameResult,
onClose = { tabViewModel.resetBlameState() } onClose = { tabViewModel.resetBlameState() },
onSelectCommit = { tabViewModel.selectCommit(it) }
) )
} else { } else {
Column {
Box(modifier = Modifier.weight(1f, true)) {
when (diffSelected) { when (diffSelected) {
null -> { null -> {
Log( Log(
@ -140,6 +143,16 @@ fun RepoContent(
} }
} }
} }
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, selectedEntryType = diffSelected,
repositoryState = repositoryState, repositoryState = repositoryState,
onStagedDiffEntrySelected = { diffEntry -> onStagedDiffEntrySelected = { diffEntry ->
// TODO: Instead of resetting the state, create a new one where the blame tabViewModel.minimizeBlame()
// 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.newDiffSelected = if (diffEntry != null) { tabViewModel.newDiffSelected = if (diffEntry != null) {
if (repositoryState == RepositoryState.SAFE) if (repositoryState == RepositoryState.SAFE)
@ -170,7 +180,7 @@ fun RepoContent(
} }
}, },
onUnstagedDiffEntrySelected = { diffEntry -> onUnstagedDiffEntrySelected = { diffEntry ->
tabViewModel.resetBlameState() tabViewModel.minimizeBlame()
if (repositoryState == RepositoryState.SAFE) if (repositoryState == RepositoryState.SAFE)
tabViewModel.newDiffSelected = DiffEntryType.SafeUnstagedDiff(diffEntry) tabViewModel.newDiffSelected = DiffEntryType.SafeUnstagedDiff(diffEntry)
@ -185,7 +195,7 @@ fun RepoContent(
selectedItem = safeSelectedItem, selectedItem = safeSelectedItem,
diffSelected = diffSelected, diffSelected = diffSelected,
onDiffSelected = { diffEntry -> onDiffSelected = { diffEntry ->
tabViewModel.resetBlameState() tabViewModel.minimizeBlame()
tabViewModel.newDiffSelected = DiffEntryType.CommitDiff(diffEntry) tabViewModel.newDiffSelected = DiffEntryType.CommitDiff(diffEntry)
}, },
onBlame = { tabViewModel.blameFile(it) } onBlame = { tabViewModel.blameFile(it) }

View File

@ -17,6 +17,7 @@ import org.eclipse.jgit.api.Git
import org.eclipse.jgit.blame.BlameResult import org.eclipse.jgit.blame.BlameResult
import org.eclipse.jgit.lib.Repository import org.eclipse.jgit.lib.Repository
import org.eclipse.jgit.lib.RepositoryState import org.eclipse.jgit.lib.RepositoryState
import org.eclipse.jgit.revwalk.RevCommit
import java.io.File import java.io.File
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Provider import javax.inject.Provider
@ -340,6 +341,26 @@ class TabViewModel @Inject constructor(
fun resetBlameState() { fun resetBlameState() {
_blameState.value = BlameState.None _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 { sealed interface BlameState {
data class Loading(val filePath: String) : 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 object None : BlameState
} }