diff --git a/src/main/kotlin/app/ui/CommitChanges.kt b/src/main/kotlin/app/ui/CommitChanges.kt index d3c58cd..0124679 100644 --- a/src/main/kotlin/app/ui/CommitChanges.kt +++ b/src/main/kotlin/app/ui/CommitChanges.kt @@ -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 diff --git a/src/main/kotlin/app/ui/Diff.kt b/src/main/kotlin/app/ui/Diff.kt index aa04ecc..4af3d70 100644 --- a/src/main/kotlin/app/ui/Diff.kt +++ b/src/main/kotlin/app/ui/Diff.kt @@ -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) diff --git a/src/main/kotlin/app/ui/FileHistory.kt b/src/main/kotlin/app/ui/FileHistory.kt new file mode 100644 index 0000000..8274813 --- /dev/null +++ b/src/main/kotlin/app/ui/FileHistory.kt @@ -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 + ) + } + } + } +} diff --git a/src/main/kotlin/app/ui/RepositoryOpen.kt b/src/main/kotlin/app/ui/RepositoryOpen.kt index 8d348c0..7b4279f 100644 --- a/src/main/kotlin/app/ui/RepositoryOpen.kt +++ b/src/main/kotlin/app/ui/RepositoryOpen.kt @@ -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) }, ) } } diff --git a/src/main/kotlin/app/ui/UncommitedChanges.kt b/src/main/kotlin/app/ui/UncommitedChanges.kt index 59136f6..4ecdad5 100644 --- a/src/main/kotlin/app/ui/UncommitedChanges.kt +++ b/src/main/kotlin/app/ui/UncommitedChanges.kt @@ -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 = { diff --git a/src/main/kotlin/app/ui/context_menu/CommitedChangesEntriesContextMenu.kt b/src/main/kotlin/app/ui/context_menu/CommitedChangesEntriesContextMenu.kt index cc882df..d2be1c3 100644 --- a/src/main/kotlin/app/ui/context_menu/CommitedChangesEntriesContextMenu.kt +++ b/src/main/kotlin/app/ui/context_menu/CommitedChangesEntriesContextMenu.kt @@ -9,6 +9,7 @@ import org.eclipse.jgit.diff.DiffEntry fun commitedChangesEntriesContextMenuItems( diffEntry: DiffEntry, onBlame: () -> Unit, + onHistory: () -> Unit, ): List { return mutableListOf().apply { if (diffEntry.changeType != DiffEntry.ChangeType.ADD || @@ -19,6 +20,12 @@ fun commitedChangesEntriesContextMenuItems( onClick = onBlame, ) ) + add( + ContextMenuItem( + label = "File history", + onClick = onHistory, + ) + ) } } } \ No newline at end of file diff --git a/src/main/kotlin/app/ui/context_menu/StatusEntriesContextMenu.kt b/src/main/kotlin/app/ui/context_menu/StatusEntriesContextMenu.kt index 358f93d..6a0592c 100644 --- a/src/main/kotlin/app/ui/context_menu/StatusEntriesContextMenu.kt +++ b/src/main/kotlin/app/ui/context_menu/StatusEntriesContextMenu.kt @@ -12,6 +12,7 @@ fun statusEntriesContextMenuItems( onReset: () -> Unit, onDelete: () -> Unit = {}, onBlame: () -> Unit, + onHistory: () -> Unit, ): List { return mutableListOf().apply { if (statusEntry.statusType != StatusType.ADDED) { @@ -29,6 +30,13 @@ fun statusEntriesContextMenuItems( onClick = onBlame, ) ) + + add( + ContextMenuItem( + label = "File history", + onClick = onHistory, + ) + ) } } diff --git a/src/main/kotlin/app/ui/log/Log.kt b/src/main/kotlin/app/ui/log/Log.kt index f09064c..653c282 100644 --- a/src/main/kotlin/app/ui/log/Log.kt +++ b/src/main/kotlin/app/ui/log/Log.kt @@ -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, diff --git a/src/main/kotlin/app/viewmodels/DiffViewModel.kt b/src/main/kotlin/app/viewmodels/DiffViewModel.kt index 0a60721..04ef0fd 100644 --- a/src/main/kotlin/app/viewmodels/DiffViewModel.kt +++ b/src/main/kotlin/app/viewmodels/DiffViewModel.kt @@ -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 -} diff --git a/src/main/kotlin/app/viewmodels/HistoryViewModel.kt b/src/main/kotlin/app/viewmodels/HistoryViewModel.kt new file mode 100644 index 0000000..97eb9a7 --- /dev/null +++ b/src/main/kotlin/app/viewmodels/HistoryViewModel.kt @@ -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.Loading("")) + val historyState: StateFlow = _historyState + + private val _viewDiffResult = MutableStateFlow(ViewDiffResult.None) + val viewDiffResult: StateFlow = _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) : HistoryState(filePath) +} + diff --git a/src/main/kotlin/app/viewmodels/TabViewModel.kt b/src/main/kotlin/app/viewmodels/TabViewModel.kt index d45a39b..53d4cde 100644 --- a/src/main/kotlin/app/viewmodels/TabViewModel.kt +++ b/src/main/kotlin/app/viewmodels/TabViewModel.kt @@ -41,6 +41,7 @@ class TabViewModel @Inject constructor( val commitChangesViewModel: CommitChangesViewModel, val cloneViewModel: CloneViewModel, private val rebaseInteractiveViewModelProvider: Provider, + private val historyViewModelProvider: Provider, private val repositoryManager: RepositoryManager, private val tabState: TabState, val appStateManager: AppStateManager, @@ -79,6 +80,12 @@ class TabViewModel @Inject constructor( private val _blameState = MutableStateFlow(BlameState.None) val blameState: StateFlow = _blameState + private val _showHistory = MutableStateFlow(false) + val showHistory: StateFlow = _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 diff --git a/src/main/kotlin/app/viewmodels/ViewDiffResult.kt b/src/main/kotlin/app/viewmodels/ViewDiffResult.kt new file mode 100644 index 0000000..8264265 --- /dev/null +++ b/src/main/kotlin/app/viewmodels/ViewDiffResult.kt @@ -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 +} \ No newline at end of file