Added "File history" feature
This commit is contained in:
parent
9c1133a292
commit
969233ec99
@ -32,6 +32,7 @@ fun CommitChanges(
|
||||
onDiffSelected: (DiffEntry) -> Unit,
|
||||
diffSelected: DiffEntryType?,
|
||||
onBlame: (String) -> Unit,
|
||||
onHistory: (String) -> Unit,
|
||||
) {
|
||||
LaunchedEffect(selectedItem) {
|
||||
commitChangesViewModel.loadChanges(selectedItem.revCommit)
|
||||
@ -49,7 +50,8 @@ fun CommitChanges(
|
||||
commit = commitChangesStatus.commit,
|
||||
changes = commitChangesStatus.changes,
|
||||
onDiffSelected = onDiffSelected,
|
||||
onBlame = onBlame
|
||||
onBlame = onBlame,
|
||||
onHistory = onHistory,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -62,6 +64,7 @@ fun CommitChangesView(
|
||||
onDiffSelected: (DiffEntry) -> Unit,
|
||||
diffSelected: DiffEntryType?,
|
||||
onBlame: (String) -> Unit,
|
||||
onHistory: (String) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@ -119,6 +122,7 @@ fun CommitChangesView(
|
||||
diffEntries = changes,
|
||||
onDiffSelected = onDiffSelected,
|
||||
onBlame = onBlame,
|
||||
onHistory = onHistory,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -170,7 +174,6 @@ fun Author(commit: RevCommit) {
|
||||
fontSize = 13.sp,
|
||||
tooltipTitle = authorIdent.`when`.toSystemDateTimeString()
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
Text(
|
||||
@ -190,6 +193,7 @@ fun CommitLogChanges(
|
||||
onDiffSelected: (DiffEntry) -> Unit,
|
||||
diffSelected: DiffEntryType?,
|
||||
onBlame: (String) -> Unit,
|
||||
onHistory: (String) -> Unit,
|
||||
) {
|
||||
ScrollableLazyColumn(
|
||||
modifier = Modifier
|
||||
@ -200,7 +204,8 @@ fun CommitLogChanges(
|
||||
items = {
|
||||
commitedChangesEntriesContextMenuItems(
|
||||
diffEntry,
|
||||
onBlame = { onBlame(diffEntry.filePath) }
|
||||
onBlame = { onBlame(diffEntry.filePath) },
|
||||
onHistory = { onHistory(diffEntry.filePath) },
|
||||
)
|
||||
}
|
||||
) {
|
||||
@ -219,7 +224,6 @@ fun CommitLogChanges(
|
||||
) {
|
||||
Spacer(modifier = Modifier.weight(2f))
|
||||
|
||||
|
||||
Row {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
|
@ -5,6 +5,7 @@ package app.ui
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.selection.DisableSelection
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
@ -79,12 +80,23 @@ fun Diff(
|
||||
)
|
||||
|
||||
if (diffResult is DiffResult.Text) {
|
||||
TextDiff(diffEntryType, diffViewModel, diffResult)
|
||||
val scrollState by diffViewModel.lazyListState.collectAsState()
|
||||
|
||||
TextDiff(
|
||||
diffEntryType = diffEntryType,
|
||||
scrollState = scrollState,
|
||||
diffResult = diffResult,
|
||||
onUnstageHunk = { entry, hunk ->
|
||||
diffViewModel.unstageHunk(entry, hunk)
|
||||
},
|
||||
) { entry, hunk ->
|
||||
diffViewModel.stageHunk(entry, hunk)
|
||||
}
|
||||
} else if (diffResult is DiffResult.NonText) {
|
||||
NonTextDiff(diffResult)
|
||||
}
|
||||
}
|
||||
ViewDiffResult.Loading -> {
|
||||
ViewDiffResult.Loading, ViewDiffResult.None -> {
|
||||
LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
}
|
||||
@ -186,25 +198,29 @@ fun BinaryDiff() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TextDiff(diffEntryType: DiffEntryType, diffViewModel: DiffViewModel, diffResult: DiffResult.Text) {
|
||||
fun TextDiff(
|
||||
diffEntryType: DiffEntryType,
|
||||
scrollState: LazyListState,
|
||||
diffResult: DiffResult.Text,
|
||||
onUnstageHunk: (DiffEntry, Hunk) -> Unit,
|
||||
onStageHunk: (DiffEntry, Hunk) -> Unit,
|
||||
) {
|
||||
val hunks = diffResult.hunks
|
||||
|
||||
val scrollState by diffViewModel.lazyListState.collectAsState()
|
||||
SelectionContainer {
|
||||
ScrollableLazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
state = scrollState
|
||||
) {
|
||||
|
||||
for (hunk in hunks) {
|
||||
item {
|
||||
DisableSelection {
|
||||
HunkHeader(
|
||||
hunk = hunk,
|
||||
diffViewModel = diffViewModel,
|
||||
diffEntryType = diffEntryType,
|
||||
diffEntry =diffResult.diffEntry,
|
||||
onUnstageHunk = { onUnstageHunk(diffResult.diffEntry, hunk) },
|
||||
onStageHunk = { onStageHunk(diffResult.diffEntry, hunk) },
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -227,8 +243,8 @@ fun TextDiff(diffEntryType: DiffEntryType, diffViewModel: DiffViewModel, diffRes
|
||||
fun HunkHeader(
|
||||
hunk: Hunk,
|
||||
diffEntryType: DiffEntryType,
|
||||
diffViewModel: DiffViewModel,
|
||||
diffEntry: DiffEntry,
|
||||
onUnstageHunk: () -> Unit,
|
||||
onStageHunk: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@ -266,9 +282,9 @@ fun HunkHeader(
|
||||
backgroundButton = color,
|
||||
onClick = {
|
||||
if (diffEntryType is DiffEntryType.StagedDiff) {
|
||||
diffViewModel.unstageHunk(diffEntry, hunk)
|
||||
onUnstageHunk()
|
||||
} else {
|
||||
diffViewModel.stageHunk(diffEntry, hunk)
|
||||
onStageHunk()
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -346,7 +362,10 @@ fun DiffHeader(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DiffLine(highestLineNumberLength: Int, line: Line) {
|
||||
fun DiffLine(
|
||||
highestLineNumberLength: Int,
|
||||
line: Line,
|
||||
) {
|
||||
val backgroundColor = when (line.lineType) {
|
||||
LineType.ADDED -> {
|
||||
Color(0x77a9d49b)
|
||||
|
222
src/main/kotlin/app/ui/FileHistory.kt
Normal file
222
src/main/kotlin/app/ui/FileHistory.kt
Normal file
@ -0,0 +1,222 @@
|
||||
@file:OptIn(ExperimentalComposeUiApi::class, ExperimentalSplitPaneApi::class)
|
||||
|
||||
package app.ui
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.input.pointer.PointerIconDefaults
|
||||
import androidx.compose.ui.input.pointer.pointerHoverIcon
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import app.extensions.handMouseClickable
|
||||
import app.extensions.toSmartSystemString
|
||||
import app.extensions.toSystemDateTimeString
|
||||
import app.git.diff.DiffResult
|
||||
import app.theme.primaryTextColor
|
||||
import app.theme.secondaryTextColor
|
||||
import app.ui.components.AvatarImage
|
||||
import app.ui.components.ScrollableLazyColumn
|
||||
import app.ui.components.TooltipText
|
||||
import app.viewmodels.HistoryState
|
||||
import app.viewmodels.HistoryViewModel
|
||||
import app.viewmodels.ViewDiffResult
|
||||
import org.eclipse.jgit.revwalk.RevCommit
|
||||
import org.jetbrains.compose.splitpane.ExperimentalSplitPaneApi
|
||||
|
||||
@Composable
|
||||
fun FileHistory(
|
||||
historyViewModel: HistoryViewModel,
|
||||
onClose: () -> Unit
|
||||
) {
|
||||
val historyState by historyViewModel.historyState.collectAsState()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Header(filePath = historyState.filePath, onClose = onClose)
|
||||
|
||||
HistoryContent(
|
||||
historyViewModel,
|
||||
historyState,
|
||||
onCommitSelected = { historyViewModel.selectCommit(it) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Header(
|
||||
filePath: String,
|
||||
onClose: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(50.dp)
|
||||
.padding(start = 8.dp, end = 8.dp, top = 8.dp)
|
||||
.background(MaterialTheme.colors.surface),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = filePath,
|
||||
color = MaterialTheme.colors.primaryTextColor,
|
||||
fontSize = 13.sp,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
IconButton(
|
||||
onClick = onClose,
|
||||
modifier = Modifier
|
||||
.pointerHoverIcon(PointerIconDefaults.Hand)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource("close.svg"),
|
||||
contentDescription = "Close history",
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colors.primaryTextColor),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
private fun HistoryContent(
|
||||
historyViewModel: HistoryViewModel,
|
||||
historyState: HistoryState,
|
||||
onCommitSelected: (RevCommit) -> Unit,
|
||||
) {
|
||||
val textScrollState by historyViewModel.lazyListState.collectAsState()
|
||||
val viewDiffResult by historyViewModel.viewDiffResult.collectAsState()
|
||||
|
||||
when (historyState) {
|
||||
is HistoryState.Loaded -> HistoryContentLoaded(
|
||||
historyState = historyState,
|
||||
viewDiffResult = viewDiffResult,
|
||||
scrollState = textScrollState,
|
||||
onCommitSelected = onCommitSelected,
|
||||
)
|
||||
is HistoryState.Loading -> Box { }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HistoryContentLoaded(
|
||||
historyState: HistoryState.Loaded,
|
||||
viewDiffResult: ViewDiffResult?,
|
||||
scrollState: LazyListState,
|
||||
onCommitSelected: (RevCommit) -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
ScrollableLazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.width(300.dp)
|
||||
.background(MaterialTheme.colors.surface)
|
||||
) {
|
||||
items(historyState.commits) { commit ->
|
||||
HistoryCommit(commit, onCommitSelected = { onCommitSelected(commit) })
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
if (
|
||||
viewDiffResult != null &&
|
||||
viewDiffResult is ViewDiffResult.Loaded
|
||||
) {
|
||||
val diffResult = viewDiffResult.diffResult
|
||||
if (diffResult is DiffResult.Text) {
|
||||
TextDiff(
|
||||
diffEntryType = viewDiffResult.diffEntryType,
|
||||
scrollState = scrollState,
|
||||
diffResult = diffResult,
|
||||
onUnstageHunk = { _, _ -> },
|
||||
onStageHunk = { _, _ -> }
|
||||
)
|
||||
} else {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
.background(MaterialTheme.colors.background)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
.background(MaterialTheme.colors.background)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HistoryCommit(commit: RevCommit, onCommitSelected: () -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.handMouseClickable { onCommitSelected() }
|
||||
.padding(vertical = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
AvatarImage(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.size(40.dp),
|
||||
personIdent = commit.authorIdent,
|
||||
)
|
||||
|
||||
Column {
|
||||
Text(
|
||||
text = commit.shortMessage,
|
||||
maxLines = 1,
|
||||
fontSize = 14.sp,
|
||||
color = MaterialTheme.colors.primaryTextColor,
|
||||
)
|
||||
|
||||
Row {
|
||||
Text(
|
||||
text = commit.name.take(7),
|
||||
maxLines = 1,
|
||||
fontSize = 12.sp,
|
||||
color = MaterialTheme.colors.secondaryTextColor,
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
val date = remember(commit.authorIdent) {
|
||||
commit.authorIdent.`when`.toSmartSystemString()
|
||||
}
|
||||
|
||||
TooltipText(
|
||||
text = date,
|
||||
color = MaterialTheme.colors.secondaryTextColor,
|
||||
maxLines = 1,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
fontSize = 12.sp,
|
||||
tooltipTitle = date
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
@file:OptIn(ExperimentalSplitPaneApi::class)
|
||||
|
||||
package app.ui
|
||||
|
||||
import androidx.compose.foundation.border
|
||||
@ -31,6 +33,7 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
|
||||
val diffSelected by tabViewModel.diffSelected.collectAsState()
|
||||
val selectedItem by tabViewModel.selectedItem.collectAsState()
|
||||
val blameState by tabViewModel.blameState.collectAsState()
|
||||
val showHistory by tabViewModel.showHistory.collectAsState()
|
||||
|
||||
var showNewBranchDialog by remember { mutableStateOf(false) }
|
||||
|
||||
@ -65,13 +68,12 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
|
||||
onCreateBranch = { showNewBranchDialog = true }
|
||||
)
|
||||
|
||||
RepoContent(tabViewModel, diffSelected, selectedItem, repositoryState, blameState)
|
||||
RepoContent(tabViewModel, diffSelected, selectedItem, repositoryState, blameState, showHistory)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSplitPaneApi::class)
|
||||
@Composable
|
||||
fun RepoContent(
|
||||
tabViewModel: TabViewModel,
|
||||
@ -79,6 +81,39 @@ fun RepoContent(
|
||||
selectedItem: SelectedItem,
|
||||
repositoryState: RepositoryState,
|
||||
blameState: BlameState,
|
||||
showHistory: Boolean,
|
||||
) {
|
||||
if(showHistory) {
|
||||
val historyViewModel = tabViewModel.historyViewModel
|
||||
|
||||
if(historyViewModel != null) {
|
||||
FileHistory(
|
||||
historyViewModel = historyViewModel,
|
||||
onClose = {
|
||||
tabViewModel.closeHistory()
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
MainContentView(
|
||||
tabViewModel,
|
||||
diffSelected,
|
||||
selectedItem,
|
||||
repositoryState,
|
||||
blameState,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainContentView(
|
||||
tabViewModel: TabViewModel,
|
||||
diffSelected: DiffEntryType?,
|
||||
selectedItem: SelectedItem,
|
||||
repositoryState: RepositoryState,
|
||||
blameState: BlameState
|
||||
) {
|
||||
Row {
|
||||
HorizontalSplitPane {
|
||||
@ -187,7 +222,8 @@ fun RepoContent(
|
||||
else
|
||||
tabViewModel.newDiffSelected = DiffEntryType.UnsafeUnstagedDiff(diffEntry)
|
||||
},
|
||||
onBlameFile = { tabViewModel.blameFile(it) }
|
||||
onBlameFile = { tabViewModel.blameFile(it) },
|
||||
onHistoryFile = { tabViewModel.fileHistory(it) }
|
||||
)
|
||||
} else if (safeSelectedItem is SelectedItem.CommitBasedItem) {
|
||||
CommitChanges(
|
||||
@ -198,7 +234,8 @@ fun RepoContent(
|
||||
tabViewModel.minimizeBlame()
|
||||
tabViewModel.newDiffSelected = DiffEntryType.CommitDiff(diffEntry)
|
||||
},
|
||||
onBlame = { tabViewModel.blameFile(it) }
|
||||
onBlame = { tabViewModel.blameFile(it) },
|
||||
onHistory = { tabViewModel.fileHistory(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ fun UncommitedChanges(
|
||||
onStagedDiffEntrySelected: (StatusEntry?) -> Unit,
|
||||
onUnstagedDiffEntrySelected: (StatusEntry) -> Unit,
|
||||
onBlameFile: (String) -> Unit,
|
||||
onHistoryFile: (String) -> Unit,
|
||||
) {
|
||||
val stageStatusState = statusViewModel.stageStatus.collectAsState()
|
||||
var commitMessage by remember { mutableStateOf(statusViewModel.savedCommitMessage) }
|
||||
@ -106,6 +107,7 @@ fun UncommitedChanges(
|
||||
entryType = EntryType.STAGED,
|
||||
onBlame = { onBlameFile(statusEntry.filePath) },
|
||||
onReset = { statusViewModel.resetStaged(statusEntry) },
|
||||
onHistory = { onHistoryFile(statusEntry.filePath) },
|
||||
)
|
||||
},
|
||||
onAllAction = {
|
||||
@ -132,10 +134,11 @@ fun UncommitedChanges(
|
||||
statusEntry = statusEntry,
|
||||
entryType = EntryType.UNSTAGED,
|
||||
onBlame = { onBlameFile(statusEntry.filePath) },
|
||||
onHistory = { onHistoryFile(statusEntry.filePath) },
|
||||
onReset = { statusViewModel.resetUnstaged(statusEntry) },
|
||||
onDelete = {
|
||||
statusViewModel.deleteFile(statusEntry)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
onAllAction = {
|
||||
|
@ -9,6 +9,7 @@ import org.eclipse.jgit.diff.DiffEntry
|
||||
fun commitedChangesEntriesContextMenuItems(
|
||||
diffEntry: DiffEntry,
|
||||
onBlame: () -> Unit,
|
||||
onHistory: () -> Unit,
|
||||
): List<ContextMenuItem> {
|
||||
return mutableListOf<ContextMenuItem>().apply {
|
||||
if (diffEntry.changeType != DiffEntry.ChangeType.ADD ||
|
||||
@ -19,6 +20,12 @@ fun commitedChangesEntriesContextMenuItems(
|
||||
onClick = onBlame,
|
||||
)
|
||||
)
|
||||
add(
|
||||
ContextMenuItem(
|
||||
label = "File history",
|
||||
onClick = onHistory,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ fun statusEntriesContextMenuItems(
|
||||
onReset: () -> Unit,
|
||||
onDelete: () -> Unit = {},
|
||||
onBlame: () -> Unit,
|
||||
onHistory: () -> Unit,
|
||||
): List<ContextMenuItem> {
|
||||
return mutableListOf<ContextMenuItem>().apply {
|
||||
if (statusEntry.statusType != StatusType.ADDED) {
|
||||
@ -29,6 +30,13 @@ fun statusEntriesContextMenuItems(
|
||||
onClick = onBlame,
|
||||
)
|
||||
)
|
||||
|
||||
add(
|
||||
ContextMenuItem(
|
||||
label = "File history",
|
||||
onClick = onHistory,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -779,7 +779,7 @@ fun CommitMessage(
|
||||
)
|
||||
|
||||
Text(
|
||||
text = commit.committerIdent.`when`.toSmartSystemString(),
|
||||
text = commit.authorIdent.`when`.toSmartSystemString(),
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
fontSize = 12.sp,
|
||||
color = MaterialTheme.colors.secondaryTextColor,
|
||||
|
@ -3,7 +3,6 @@ package app.viewmodels
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import app.exceptions.MissingDiffEntryException
|
||||
import app.git.*
|
||||
import app.git.diff.DiffResult
|
||||
import app.git.diff.Hunk
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@ -87,8 +86,3 @@ class DiffViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
|
||||
sealed interface ViewDiffResult {
|
||||
object Loading: ViewDiffResult
|
||||
object DiffNotFound: ViewDiffResult
|
||||
data class Loaded(val diffEntryType: DiffEntryType, val diffResult: DiffResult): ViewDiffResult
|
||||
}
|
||||
|
82
src/main/kotlin/app/viewmodels/HistoryViewModel.kt
Normal file
82
src/main/kotlin/app/viewmodels/HistoryViewModel.kt
Normal file
@ -0,0 +1,82 @@
|
||||
package app.viewmodels
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import app.exceptions.MissingDiffEntryException
|
||||
import app.extensions.filePath
|
||||
import app.git.DiffEntryType
|
||||
import app.git.DiffManager
|
||||
import app.git.RefreshType
|
||||
import app.git.TabState
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import org.eclipse.jgit.revwalk.RevCommit
|
||||
import javax.inject.Inject
|
||||
|
||||
class HistoryViewModel @Inject constructor(
|
||||
private val tabState: TabState,
|
||||
private val diffManager: DiffManager,
|
||||
) {
|
||||
private val _historyState = MutableStateFlow<HistoryState>(HistoryState.Loading(""))
|
||||
val historyState: StateFlow<HistoryState> = _historyState
|
||||
|
||||
private val _viewDiffResult = MutableStateFlow<ViewDiffResult>(ViewDiffResult.None)
|
||||
val viewDiffResult: StateFlow<ViewDiffResult> = _viewDiffResult
|
||||
var filePath: String = ""
|
||||
|
||||
val lazyListState = MutableStateFlow(
|
||||
LazyListState(
|
||||
0,
|
||||
0
|
||||
)
|
||||
)
|
||||
|
||||
fun fileHistory(filePath: String) = tabState.safeProcessing(
|
||||
refreshType = RefreshType.NONE,
|
||||
) { git ->
|
||||
this.filePath = filePath
|
||||
_historyState.value = HistoryState.Loading(filePath)
|
||||
|
||||
val log = git.log()
|
||||
.addPath(filePath)
|
||||
.call()
|
||||
.toList()
|
||||
|
||||
_historyState.value = HistoryState.Loaded(filePath, log)
|
||||
}
|
||||
|
||||
fun selectCommit(commit: RevCommit) = tabState.runOperation(
|
||||
refreshType = RefreshType.NONE,
|
||||
showError = true,
|
||||
) { git ->
|
||||
|
||||
try {
|
||||
|
||||
val diffEntries = diffManager.commitDiffEntries(git, commit)
|
||||
val diffEntry = diffEntries.firstOrNull {entry ->
|
||||
entry.filePath == this.filePath
|
||||
}
|
||||
|
||||
if(diffEntry == null) {
|
||||
_viewDiffResult.value = ViewDiffResult.DiffNotFound
|
||||
return@runOperation
|
||||
}
|
||||
val diffEntryType = DiffEntryType.CommitDiff(diffEntry)
|
||||
|
||||
val diffFormat = diffManager.diffFormat(git, diffEntryType)
|
||||
|
||||
_viewDiffResult.value = ViewDiffResult.Loaded(diffEntryType, diffFormat)
|
||||
} catch (ex: Exception) {
|
||||
if(ex is MissingDiffEntryException) {
|
||||
tabState.refreshData(refreshType = RefreshType.UNCOMMITED_CHANGES)
|
||||
_viewDiffResult.value = ViewDiffResult.DiffNotFound
|
||||
} else
|
||||
ex.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class HistoryState(val filePath: String) {
|
||||
class Loading(filePath: String) : HistoryState(filePath)
|
||||
class Loaded(filePath: String, val commits: List<RevCommit>) : HistoryState(filePath)
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ class TabViewModel @Inject constructor(
|
||||
val commitChangesViewModel: CommitChangesViewModel,
|
||||
val cloneViewModel: CloneViewModel,
|
||||
private val rebaseInteractiveViewModelProvider: Provider<RebaseInteractiveViewModel>,
|
||||
private val historyViewModelProvider: Provider<HistoryViewModel>,
|
||||
private val repositoryManager: RepositoryManager,
|
||||
private val tabState: TabState,
|
||||
val appStateManager: AppStateManager,
|
||||
@ -79,6 +80,12 @@ class TabViewModel @Inject constructor(
|
||||
private val _blameState = MutableStateFlow<BlameState>(BlameState.None)
|
||||
val blameState: StateFlow<BlameState> = _blameState
|
||||
|
||||
private val _showHistory = MutableStateFlow(false)
|
||||
val showHistory: StateFlow<Boolean> = _showHistory
|
||||
|
||||
var historyViewModel: HistoryViewModel? = null
|
||||
private set
|
||||
|
||||
val showError = MutableStateFlow(false)
|
||||
|
||||
init {
|
||||
@ -361,6 +368,17 @@ class TabViewModel @Inject constructor(
|
||||
fun selectCommit(commit: RevCommit) {
|
||||
tabState.newSelectedItem(SelectedItem.Commit(commit))
|
||||
}
|
||||
|
||||
fun fileHistory(filePath: String) {
|
||||
historyViewModel = historyViewModelProvider.get()
|
||||
historyViewModel?.fileHistory(filePath)
|
||||
_showHistory.value = true
|
||||
}
|
||||
|
||||
fun closeHistory() {
|
||||
_showHistory.value = false
|
||||
historyViewModel = null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -370,7 +388,6 @@ sealed class RepositorySelectionStatus {
|
||||
data class Open(val repository: Repository) : RepositorySelectionStatus()
|
||||
}
|
||||
|
||||
|
||||
sealed interface BlameState {
|
||||
data class Loading(val filePath: String) : BlameState
|
||||
|
||||
|
11
src/main/kotlin/app/viewmodels/ViewDiffResult.kt
Normal file
11
src/main/kotlin/app/viewmodels/ViewDiffResult.kt
Normal file
@ -0,0 +1,11 @@
|
||||
package app.viewmodels
|
||||
|
||||
import app.git.DiffEntryType
|
||||
import app.git.diff.DiffResult
|
||||
|
||||
sealed interface ViewDiffResult {
|
||||
object None: ViewDiffResult
|
||||
object Loading: ViewDiffResult
|
||||
object DiffNotFound: ViewDiffResult
|
||||
data class Loaded(val diffEntryType: DiffEntryType, val diffResult: DiffResult): ViewDiffResult
|
||||
}
|
Loading…
Reference in New Issue
Block a user