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 import javax.inject.Inject
class BranchesManager @Inject constructor() { 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. * 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 androidx.compose.ui.unit.sp
import app.LoadingRepository import app.LoadingRepository
import app.credentials.CredentialsState import app.credentials.CredentialsState
import app.git.TabViewModel import app.viewmodels.TabViewModel
import app.git.RepositorySelectionStatus import app.viewmodels.RepositorySelectionStatus
import app.ui.dialogs.PasswordDialog import app.ui.dialogs.PasswordDialog
import app.ui.dialogs.UserPasswordDialog import app.ui.dialogs.UserPasswordDialog
import kotlinx.coroutines.delay 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.MAX_SIDE_PANEL_ITEMS_HEIGHT
import app.extensions.isLocal import app.extensions.isLocal
import app.extensions.simpleName import app.extensions.simpleName
import app.git.TabViewModel
import app.ui.components.ScrollableLazyColumn import app.ui.components.ScrollableLazyColumn
import app.ui.components.SideMenuEntry import app.ui.components.SideMenuEntry
import app.ui.components.SideMenuSubentry 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.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import app.extensions.* import app.extensions.*
import app.git.TabViewModel import app.viewmodels.TabViewModel
import app.theme.headerBackground import app.theme.headerBackground
import app.theme.headerText import app.theme.headerText
import app.theme.primaryTextColor 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.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import app.git.DiffEntryType import app.git.DiffEntryType
import app.git.TabViewModel
import app.git.diff.Hunk
import app.git.diff.LineType import app.git.diff.LineType
import app.theme.primaryTextColor import app.theme.primaryTextColor
import app.ui.components.ScrollableLazyColumn import app.ui.components.ScrollableLazyColumn
@ -55,10 +53,11 @@ fun Diff(
Text("Close diff") Text("Close diff")
} }
val scrollState by diffViewModel.lazyListState.collectAsState()
ScrollableLazyColumn( ScrollableLazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize(),
// .padding(16.dp) state = scrollState
) { ) {
itemsIndexed(hunks) { index, hunk -> itemsIndexed(hunks) { index, hunk ->
val hunksSeparation = if (index == 0) val hunksSeparation = if (index == 0)

View File

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

View File

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

View File

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

View File

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

View File

@ -74,7 +74,6 @@ fun UncommitedChanges(
unstaged = listOf<StatusEntry>() // return empty lists if still loading unstaged = listOf<StatusEntry>() // return empty lists if still loading
} }
var commitMessage by remember { mutableStateOf("") } var commitMessage by remember { mutableStateOf("") }
val doCommit = { val doCommit = {
statusViewModel.commit(commitMessage) 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( fun checkIfSelectedEntryShouldBeUpdated(
selectedEntryType: DiffEntryType, selectedEntryType: DiffEntryType,
staged: List<StatusEntry>, staged: List<StatusEntry>,

View File

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

View File

@ -23,7 +23,7 @@ import androidx.compose.ui.unit.dp
import app.AppStateManager import app.AppStateManager
import app.di.AppComponent import app.di.AppComponent
import app.di.DaggerTabComponent import app.di.DaggerTabComponent
import app.git.TabViewModel import app.viewmodels.TabViewModel
import app.theme.tabColorActive import app.theme.tabColorActive
import app.theme.tabColorInactive import app.theme.tabColorInactive
import app.ui.AppTab 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.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import app.git.CloneStatus import app.git.CloneStatus
import app.git.TabViewModel import app.viewmodels.TabViewModel
import app.theme.primaryTextColor import app.theme.primaryTextColor
import java.io.File 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.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import app.extensions.* import app.extensions.*
import app.git.TabViewModel import app.viewmodels.TabViewModel
import app.git.graph.GraphNode import app.git.graph.GraphNode
import app.theme.* import app.theme.*
import app.ui.SelectedItem import app.ui.SelectedItem

View File

@ -1,5 +1,9 @@
package app.viewmodels 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.*
import app.git.diff.Hunk import app.git.diff.Hunk
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -20,9 +24,30 @@ class DiffViewModel @Inject constructor(
private val _diffResult = MutableStateFlow<DiffResult?>(null) private val _diffResult = MutableStateFlow<DiffResult?>(null)
val diffResult: StateFlow<DiffResult?> = _diffResult val diffResult: StateFlow<DiffResult?> = _diffResult
val lazyListState = MutableStateFlow(
LazyListState(
0,
0
)
)
suspend fun updateDiff(git: Git, diffEntryType: DiffEntryType) = withContext(Dispatchers.IO) { suspend fun updateDiff(git: Git, diffEntryType: DiffEntryType) = withContext(Dispatchers.IO) {
val oldDiffEntryType = _diffResult.value?.diffEntryType
_diffResult.value = null _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) val hunks = diffManager.diffFormat(git, diffEntryType)
_diffResult.value = DiffResult(diffEntryType, hunks) _diffResult.value = DiffResult(diffEntryType, hunks)

View File

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