Diff scroll is now preserved when changing the diff entry

The scroll is stored in the view model and only resetted when changing to a different file or state (staged/unstaged version of the same file).
This commit is contained in:
Abdelilah El Aissaoui 2022-01-03 22:59:35 +01:00
parent 9c53ce726e
commit e6619cbd4b
18 changed files with 87 additions and 86 deletions

View File

@ -15,14 +15,6 @@ import org.eclipse.jgit.revwalk.RevCommit
import javax.inject.Inject
class BranchesManager @Inject constructor() {
private val _branches = MutableStateFlow<List<Ref>>(listOf())
val branches: StateFlow<List<Ref>>
get() = _branches
private val _currentBranch = MutableStateFlow<String>("")
val currentBranch: StateFlow<String>
get() = _currentBranch
/**
* Returns the current branch in [Ref]. If the repository is new, the current branch will be null.
*/

View File

@ -21,8 +21,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import app.LoadingRepository
import app.credentials.CredentialsState
import app.git.TabViewModel
import app.git.RepositorySelectionStatus
import app.viewmodels.TabViewModel
import app.viewmodels.RepositorySelectionStatus
import app.ui.dialogs.PasswordDialog
import app.ui.dialogs.UserPasswordDialog
import kotlinx.coroutines.delay

View File

@ -13,7 +13,6 @@ import androidx.compose.ui.unit.dp
import app.MAX_SIDE_PANEL_ITEMS_HEIGHT
import app.extensions.isLocal
import app.extensions.simpleName
import app.git.TabViewModel
import app.ui.components.ScrollableLazyColumn
import app.ui.components.SideMenuEntry
import app.ui.components.SideMenuSubentry

View File

@ -17,7 +17,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import app.extensions.*
import app.git.TabViewModel
import app.viewmodels.TabViewModel
import app.theme.headerBackground
import app.theme.headerText
import app.theme.primaryTextColor

View File

@ -16,8 +16,6 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import app.git.DiffEntryType
import app.git.TabViewModel
import app.git.diff.Hunk
import app.git.diff.LineType
import app.theme.primaryTextColor
import app.ui.components.ScrollableLazyColumn
@ -55,10 +53,11 @@ fun Diff(
Text("Close diff")
}
val scrollState by diffViewModel.lazyListState.collectAsState()
ScrollableLazyColumn(
modifier = Modifier
.fillMaxSize()
// .padding(16.dp)
.fillMaxSize(),
state = scrollState
) {
itemsIndexed(hunks) { index, hunk ->
val hunksSeparation = if (index == 0)

View File

@ -12,7 +12,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import app.MAX_SIDE_PANEL_ITEMS_HEIGHT
import app.extensions.simpleVisibleName
import app.git.TabViewModel
import app.git.RemoteInfo
import app.ui.components.ScrollableLazyColumn
import app.ui.components.SideMenuEntry

View File

@ -1,12 +1,11 @@
package app.ui
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import app.git.DiffEntryType
import app.git.TabViewModel
import app.viewmodels.TabViewModel
import app.ui.dialogs.NewBranchDialog
import app.ui.log.Log
import openRepositoryDialog
@ -20,22 +19,13 @@ import org.jetbrains.compose.splitpane.rememberSplitPaneState
@Composable
fun RepositoryOpenPage(tabViewModel: TabViewModel) {
val repositoryState by tabViewModel.repositoryState.collectAsState()
var diffSelected by remember {
mutableStateOf<DiffEntryType?>(null)
}
LaunchedEffect(diffSelected) {
diffSelected?.let { safeDiffSelected ->
tabViewModel.updatedDiffEntry(safeDiffSelected)
}
}
val diffSelected by tabViewModel.diffSelected.collectAsState()
var showNewBranchDialog by remember { mutableStateOf(false) }
val (selectedItem, setSelectedItem) = remember { mutableStateOf<SelectedItem>(SelectedItem.None) }
LaunchedEffect(selectedItem) {
diffSelected = null
tabViewModel.newDiffSelected = null
}
if (showNewBranchDialog) {
@ -104,26 +94,24 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
modifier = Modifier
.fillMaxSize()
) {
// Crossfade(targetState = diffSelected) { diffEntry ->
when (diffSelected) {
null -> {
Log(
tabViewModel = tabViewModel,
repositoryState = repositoryState,
logViewModel = tabViewModel.logViewModel,
selectedItem = selectedItem,
onItemSelected = {
setSelectedItem(it)
},
)
}
else -> {
Diff(
diffViewModel = tabViewModel.diffViewModel,
onCloseDiffView = { diffSelected = null })
}
when (diffSelected) {
null -> {
Log(
tabViewModel = tabViewModel,
repositoryState = repositoryState,
logViewModel = tabViewModel.logViewModel,
selectedItem = selectedItem,
onItemSelected = {
setSelectedItem(it)
},
)
}
// }
else -> {
Diff(
diffViewModel = tabViewModel.diffViewModel,
onCloseDiffView = { tabViewModel.newDiffSelected = null })
}
}
}
}
@ -138,13 +126,13 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
selectedEntryType = diffSelected,
repositoryState = repositoryState,
onStagedDiffEntrySelected = { diffEntry ->
diffSelected = if (diffEntry != null)
tabViewModel.newDiffSelected = if (diffEntry != null)
DiffEntryType.StagedDiff(diffEntry)
else
null
},
onUnstagedDiffEntrySelected = { diffEntry ->
diffSelected = DiffEntryType.UnstagedDiff(diffEntry)
tabViewModel.newDiffSelected = DiffEntryType.UnstagedDiff(diffEntry)
}
)
} else if (selectedItem is SelectedItem.CommitBasedItem) {
@ -152,7 +140,7 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
gitManager = tabViewModel,
commit = selectedItem.revCommit,
onDiffSelected = { diffEntry ->
diffSelected = DiffEntryType.CommitDiff(diffEntry)
tabViewModel.newDiffSelected = DiffEntryType.CommitDiff(diffEntry)
}
)
}

View File

@ -6,7 +6,7 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import app.git.TabViewModel
import app.viewmodels.TabViewModel
import app.git.StashStatus
import app.ui.components.ScrollableLazyColumn
import app.ui.components.SideMenuEntry

View File

@ -1,5 +1,5 @@
import app.extensions.runCommand
import app.git.TabViewModel
import app.viewmodels.TabViewModel
import javax.swing.JFileChooser

View File

@ -13,7 +13,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import app.MAX_SIDE_PANEL_ITEMS_HEIGHT
import app.extensions.simpleName
import app.git.TabViewModel
import app.ui.components.ScrollableLazyColumn
import app.ui.components.SideMenuEntry
import app.ui.components.SideMenuSubentry

View File

@ -74,7 +74,6 @@ fun UncommitedChanges(
unstaged = listOf<StatusEntry>() // return empty lists if still loading
}
var commitMessage by remember { mutableStateOf("") }
val doCommit = {
statusViewModel.commit(commitMessage)
@ -190,6 +189,7 @@ fun UncommitedChanges(
}
}
// TODO: This logic should be part of the diffViewModel where it gets the latest version of the diffEntry
fun checkIfSelectedEntryShouldBeUpdated(
selectedEntryType: DiffEntryType,
staged: List<StatusEntry>,

View File

@ -20,7 +20,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import app.extensions.dirName
import app.extensions.dirPath
import app.git.TabViewModel
import app.viewmodels.TabViewModel
import app.theme.primaryTextColor
import app.theme.secondaryTextColor
import app.ui.dialogs.CloneDialog

View File

@ -23,7 +23,7 @@ import androidx.compose.ui.unit.dp
import app.AppStateManager
import app.di.AppComponent
import app.di.DaggerTabComponent
import app.git.TabViewModel
import app.viewmodels.TabViewModel
import app.theme.tabColorActive
import app.theme.tabColorInactive
import app.ui.AppTab

View File

@ -12,7 +12,7 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import app.git.CloneStatus
import app.git.TabViewModel
import app.viewmodels.TabViewModel
import app.theme.primaryTextColor
import java.io.File

View File

@ -34,7 +34,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import app.extensions.*
import app.git.TabViewModel
import app.viewmodels.TabViewModel
import app.git.graph.GraphNode
import app.theme.*
import app.ui.SelectedItem

View File

@ -1,5 +1,9 @@
package app.viewmodels
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import app.git.*
import app.git.diff.Hunk
import kotlinx.coroutines.Dispatchers
@ -20,9 +24,30 @@ class DiffViewModel @Inject constructor(
private val _diffResult = MutableStateFlow<DiffResult?>(null)
val diffResult: StateFlow<DiffResult?> = _diffResult
val lazyListState = MutableStateFlow(
LazyListState(
0,
0
)
)
suspend fun updateDiff(git: Git, diffEntryType: DiffEntryType) = withContext(Dispatchers.IO) {
val oldDiffEntryType = _diffResult.value?.diffEntryType
_diffResult.value = null
// If it's a different file or different state (index or workdir), reset the scroll state
if(oldDiffEntryType != null &&
(oldDiffEntryType.diffEntry.oldPath != diffEntryType.diffEntry.oldPath ||
oldDiffEntryType.diffEntry.newPath != diffEntryType.diffEntry.newPath ||
oldDiffEntryType::class != diffEntryType::class)
) {
lazyListState.value = LazyListState(
0,
0
)
}
val hunks = diffManager.diffFormat(git, diffEntryType)
_diffResult.value = DiffResult(diffEntryType, hunks)

View File

@ -26,32 +26,28 @@ class StatusViewModel @Inject constructor(
fun stage(diffEntry: DiffEntry) = tabState.runOperation { git ->
statusManager.stage(git, diffEntry)
loadStatus(git)
return@runOperation RefreshType.NONE
return@runOperation RefreshType.UNCOMMITED_CHANGES
}
fun unstage(diffEntry: DiffEntry) = tabState.runOperation { git ->
statusManager.unstage(git, diffEntry)
loadStatus(git)
return@runOperation RefreshType.NONE
return@runOperation RefreshType.UNCOMMITED_CHANGES
}
fun unstageAll() = tabState.safeProcessing { git ->
statusManager.unstageAll(git)
loadStatus(git)
return@safeProcessing RefreshType.NONE
return@safeProcessing RefreshType.UNCOMMITED_CHANGES
}
fun stageAll() = tabState.safeProcessing { git ->
statusManager.stageAll(git)
loadStatus(git)
return@safeProcessing RefreshType.NONE
return@safeProcessing RefreshType.UNCOMMITED_CHANGES
}

View File

@ -1,12 +1,11 @@
package app.git
package app.viewmodels
import app.AppStateManager
import app.app.ErrorsManager
import app.app.newErrorNow
import app.credentials.CredentialsState
import app.credentials.CredentialsStateManager
import app.git.diff.Hunk
import app.viewmodels.*
import app.git.*
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -38,6 +37,7 @@ class TabViewModel @Inject constructor(
val appStateManager: AppStateManager,
private val fileChangesWatcher: FileChangesWatcher,
) {
val repositoryName: String
get() = safeGit.repository.directory.parentFile.name
@ -57,6 +57,16 @@ class TabViewModel @Inject constructor(
val credentialsState: StateFlow<CredentialsState> = credentialsStateManager.credentialsState
val cloneStatus: StateFlow<CloneStatus> = remoteOperationsManager.cloneStatus
private val _diffSelected = MutableStateFlow<DiffEntryType?>(null)
val diffSelected : StateFlow<DiffEntryType?> = _diffSelected
var newDiffSelected: DiffEntryType?
get() = diffSelected.value
set(value){
_diffSelected.value = value
updateDiffEntry()
}
private val _repositoryState = MutableStateFlow(RepositoryState.SAFE)
val repositoryState: StateFlow<RepositoryState> = _repositoryState
@ -153,12 +163,10 @@ class TabViewModel @Inject constructor(
if (!operationRunning) { // Only update if there isn't any process running
safeProcessing(showError = false) {
println("Changes detected, loading status")
// val hasUncommitedChanges = statusManager.hasUncommitedChanges.value
// statusManager.loadHasUncommitedChanges(safeGit)
// statusManager.loadStatus(safeGit)
statusViewModel.refresh(safeGit)
checkUncommitedChanges()
updateDiffEntry()
}
}
}
@ -174,16 +182,8 @@ class TabViewModel @Inject constructor(
// Update the log only if the uncommitedChanges status has changed
if (uncommitedChangesStateChanged)
loadLog()
}
suspend fun diffFormat(diffEntryType: DiffEntryType): List<Hunk> {
try {
return diffManager.diffFormat(safeGit, diffEntryType)
} catch (ex: Exception) {
ex.printStackTrace()
checkUncommitedChanges()
return listOf()
}
updateDiffEntry()
}
fun pull() = managerScope.launch {
@ -279,8 +279,12 @@ class TabViewModel @Inject constructor(
}
}
fun updatedDiffEntry(diffSelected: DiffEntryType) = tabState.runOperation { git ->
diffViewModel.updateDiff(git , diffSelected)
fun updateDiffEntry() = tabState.runOperation { git ->
val diffSelected = diffSelected.value
if(diffSelected != null) {
diffViewModel.updateDiff(git, diffSelected)
}
return@runOperation RefreshType.NONE
}