Improved events notifications. Removed GIt dir changes detection temporarily

This commit is contained in:
Abdelilah El Aissaoui 2022-10-16 16:00:42 +02:00
parent 453df1b6d4
commit f293e9428c
13 changed files with 41 additions and 114 deletions

View File

@ -23,7 +23,6 @@ class FileChangesWatcher @Inject constructor() {
val keys = mutableMapOf<WatchKey, Path>() val keys = mutableMapOf<WatchKey, Path>()
suspend fun watchDirectoryPath(pathStr: String, ignoredDirsPath: List<String>) = withContext(Dispatchers.IO) { suspend fun watchDirectoryPath(pathStr: String, ignoredDirsPath: List<String>) = withContext(Dispatchers.IO) {
println(ignoredDirsPath)
val watchService = FileSystems.getDefault().newWatchService() val watchService = FileSystems.getDefault().newWatchService()
val path = Paths.get(pathStr) val path = Paths.get(pathStr)
@ -41,7 +40,7 @@ class FileChangesWatcher @Inject constructor() {
override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult { override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult {
val isIgnoredDirectory = ignoredDirsPath.any { "$pathStr/$it" == dir.toString() } val isIgnoredDirectory = ignoredDirsPath.any { "$pathStr/$it" == dir.toString() }
return if (!isIgnoredDirectory) { return if (!isIgnoredDirectory && !isGitDir(dir, pathStr)) {
val watchKey = dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY) val watchKey = dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY)
keys[watchKey] = dir keys[watchKey] = dir
FileVisitResult.CONTINUE FileVisitResult.CONTINUE
@ -57,8 +56,6 @@ class FileChangesWatcher @Inject constructor() {
val dir = keys[key] ?: return@withContext val dir = keys[key] ?: return@withContext
val hasGitDirectoryChanged = dir.startsWith("$pathStr$systemSeparator.git$systemSeparator")
if (events.count() == 1) { if (events.count() == 1) {
val fileChanged = events.first().context().toString() val fileChanged = events.first().context().toString()
val fullPathOfFileChanged = "$pathStr$systemSeparator.git$systemSeparator$fileChanged" val fullPathOfFileChanged = "$pathStr$systemSeparator.git$systemSeparator$fileChanged"
@ -70,9 +67,7 @@ class FileChangesWatcher @Inject constructor() {
} }
} }
printLog(TAG, "Has git dir changed: $hasGitDirectoryChanged") _changesNotifier.emit(false)
_changesNotifier.emit(hasGitDirectoryChanged)
// Check if new directories have been added to add them to the watchService // Check if new directories have been added to add them to the watchService
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
@ -99,6 +94,10 @@ class FileChangesWatcher @Inject constructor() {
} }
} }
private fun isGitDir(dir: Path, pathStr: String): Boolean {
return dir.startsWith("$pathStr$systemSeparator.git$systemSeparator")
}
private fun isGitMessageFile(repoPath: String, fullPathOfFileChanged: String): Boolean { private fun isGitMessageFile(repoPath: String, fullPathOfFileChanged: String): Boolean {
val gitDir = "$repoPath$systemSeparator.git$systemSeparator" val gitDir = "$repoPath$systemSeparator.git$systemSeparator"
return fullPathOfFileChanged == "${gitDir}COMMIT_EDITMSG" || return fullPathOfFileChanged == "${gitDir}COMMIT_EDITMSG" ||

View File

@ -26,14 +26,14 @@ class TabState @Inject constructor(
private val _taskEvent = MutableSharedFlow<TaskEvent>() private val _taskEvent = MutableSharedFlow<TaskEvent>()
val taskEvent: SharedFlow<TaskEvent> = _taskEvent val taskEvent: SharedFlow<TaskEvent> = _taskEvent
var git: Git? = null private var unsafeGit: Git? = null
private val safeGit: Git val git: Git
get() { get() {
val git = this.git val unsafeGit = this.unsafeGit
if (git == null) { if (unsafeGit == null) {
throw CancellationException("Null git object") throw CancellationException("Repository not available")
} else } else
return git return unsafeGit
} }
private val _refreshData = MutableSharedFlow<RefreshType>() private val _refreshData = MutableSharedFlow<RefreshType>()
@ -48,6 +48,10 @@ class TabState @Inject constructor(
private val _processing = MutableStateFlow(false) private val _processing = MutableStateFlow(false)
val processing: StateFlow<Boolean> = _processing val processing: StateFlow<Boolean> = _processing
fun initGit(git: Git) {
this.unsafeGit = git
}
fun safeProcessing( fun safeProcessing(
showError: Boolean = true, showError: Boolean = true,
refreshType: RefreshType, refreshType: RefreshType,
@ -65,7 +69,7 @@ class TabState @Inject constructor(
_processing.value = true _processing.value = true
} }
) { ) {
callback(safeGit) callback(git)
} }
} catch (ex: Exception) { } catch (ex: Exception) {
hasProcessFailed = true hasProcessFailed = true
@ -112,7 +116,7 @@ class TabState @Inject constructor(
operationRunning = true operationRunning = true
try { try {
block(safeGit) block(git)
} catch (ex: Exception) { } catch (ex: Exception) {
ex.printStackTrace() ex.printStackTrace()
@ -121,52 +125,10 @@ class TabState @Inject constructor(
if (showError) if (showError)
errorsManager.addError(newErrorNow(ex, ex.localizedMessage)) errorsManager.addError(newErrorNow(ex, ex.localizedMessage))
} finally { } finally {
launch {
// Add a slight delay because sometimes the file watcher takes a few moments to notify a change in the
// filesystem, therefore notifying late and being operationRunning already false (which leads to a full
// refresh because there have been changes in the git dir). This can be easily triggered by interactive
// rebase.
delay(500)
operationRunning = false
}
if (refreshType != RefreshType.NONE && (!hasProcessFailed || refreshEvenIfCrashes)) if (refreshType != RefreshType.NONE && (!hasProcessFailed || refreshEvenIfCrashes))
_refreshData.emit(refreshType) _refreshData.emit(refreshType)
}
}
suspend fun coRunOperation(
showError: Boolean = false,
refreshType: RefreshType,
refreshEvenIfCrashes: Boolean = false,
block: suspend (git: Git) -> Unit
) = withContext(Dispatchers.IO) {
var hasProcessFailed = false
operationRunning = true
try {
block(safeGit)
} catch (ex: Exception) {
ex.printStackTrace()
hasProcessFailed = true
if (showError)
errorsManager.addError(newErrorNow(ex, ex.localizedMessage))
} finally {
launch {
// Add a slight delay because sometimes the file watcher takes a few moments to notify a change in the
// filesystem, therefore notifying late and being operationRunning already false (which leads to a full
// refresh because there have been changes in the git dir). This can be easily triggered by interactive
// rebase.
delay(500)
operationRunning = false operationRunning = false
}
if (refreshType != RefreshType.NONE && (!hasProcessFailed || refreshEvenIfCrashes))
_refreshData.emit(refreshType)
} }
} }
@ -211,9 +173,9 @@ class TabState @Inject constructor(
_taskEvent.emit(taskEvent) _taskEvent.emit(taskEvent)
} }
@OptIn(ExperimentalCoroutinesApi::class)
fun refreshFlowFiltered(vararg filters: RefreshType) = refreshData fun refreshFlowFiltered(vararg filters: RefreshType) = refreshData
.filter { refreshType -> .filter { refreshType ->
printLog(TAG, "Filters: ${filters.joinToString()}. Refresh type $refreshType")
filters.contains(refreshType) filters.contains(refreshType)
} }
} }

View File

@ -48,7 +48,6 @@ fun AppTab(
val repositorySelectionStatusValue = repositorySelectionStatus.value val repositorySelectionStatusValue = repositorySelectionStatus.value
val isProcessing by tabViewModel.processing.collectAsState() val isProcessing by tabViewModel.processing.collectAsState()
println("Tab name from scope = ${LocalTabScope.current.tabName.value}")
LocalTabScope.current.appStateManager LocalTabScope.current.appStateManager
Box { Box {
Column( Column(

View File

@ -43,9 +43,7 @@ class BranchesViewModel @Inject constructor(
tabScope.launch { tabScope.launch {
tabState.refreshFlowFiltered(RefreshType.ALL_DATA) tabState.refreshFlowFiltered(RefreshType.ALL_DATA)
.collect { .collect {
tabState.coRunOperation(refreshType = RefreshType.NONE) { git -> refresh(tabState.git)
refresh(git)
}
} }
} }
} }
@ -62,7 +60,6 @@ class BranchesViewModel @Inject constructor(
branchesList.add(0, selectedBranch) branchesList.add(0, selectedBranch)
} }
_branches.value = branchesList _branches.value = branchesList
} }

View File

@ -60,11 +60,9 @@ class DiffViewModel @Inject constructor(
RefreshType.UNCOMMITED_CHANGES, RefreshType.UNCOMMITED_CHANGES,
RefreshType.UNCOMMITED_CHANGES_AND_LOG, RefreshType.UNCOMMITED_CHANGES_AND_LOG,
).collect { ).collect {
tabState.coRunOperation(refreshType = RefreshType.NONE) { val diffResultValue = diffResult.value
val diffResultValue = diffResult.value if (diffResultValue is ViewDiffResult.Loaded) {
if(diffResultValue is ViewDiffResult.Loaded) { updateDiff(diffResultValue.diffEntryType)
updateDiff(diffResultValue.diffEntryType)
}
} }
} }
} }

View File

@ -116,9 +116,7 @@ class LogViewModel @Inject constructor(
RefreshType.ONLY_LOG, RefreshType.ONLY_LOG,
RefreshType.UNCOMMITED_CHANGES_AND_LOG, RefreshType.UNCOMMITED_CHANGES_AND_LOG,
).collect { ).collect {
tabState.coRunOperation(refreshType = RefreshType.NONE) { git -> refresh(tabState.git)
refresh(git)
}
} }
} }
} }

View File

@ -39,11 +39,9 @@ class RebaseInteractiveViewModel @Inject constructor(
println("prepareSteps started") println("prepareSteps started")
tabState.refreshData(RefreshType.REPO_STATE) tabState.refreshData(RefreshType.REPO_STATE)
tabState.coRunOperation(refreshType = RefreshType.NONE) { git -> val messages = getRebaseLinesFullMessageUseCase(tabState.git, steps)
val messages = getRebaseLinesFullMessageUseCase(git, steps)
_rebaseState.value = RebaseInteractiveState.Loaded(steps, messages) _rebaseState.value = RebaseInteractiveState.Loaded(steps, messages)
}
println("prepareSteps mutex lock") println("prepareSteps mutex lock")
rebaseInteractiveMutex.lock() rebaseInteractiveMutex.lock()

View File

@ -38,9 +38,7 @@ class RemotesViewModel @Inject constructor(
tabScope.launch { tabScope.launch {
tabState.refreshFlowFiltered(RefreshType.ALL_DATA, RefreshType.REMOTES) tabState.refreshFlowFiltered(RefreshType.ALL_DATA, RefreshType.REMOTES)
.collect { .collect {
tabState.coRunOperation(refreshType = RefreshType.NONE) { git -> refresh(tabState.git)
refresh(git)
}
} }
} }
} }

View File

@ -30,22 +30,10 @@ class StashesViewModel @Inject constructor(
get() = _stashStatus get() = _stashStatus
init { init {
tabScope.launch {
tabState.refreshData
.filter { refreshType -> refreshType == RefreshType.ALL_DATA }
.collect {
tabState.coRunOperation(refreshType = RefreshType.NONE) { git ->
refresh(git)
}
}
}
tabScope.launch { tabScope.launch {
tabState.refreshFlowFiltered(RefreshType.ALL_DATA, RefreshType.STASHES) tabState.refreshFlowFiltered(RefreshType.ALL_DATA, RefreshType.STASHES)
.collect { .collect {
tabState.coRunOperation(refreshType = RefreshType.NONE) { git -> refresh(tabState.git)
refresh(git)
}
} }
} }
} }

View File

@ -70,9 +70,7 @@ class StatusViewModel @Inject constructor(
RefreshType.UNCOMMITED_CHANGES, RefreshType.UNCOMMITED_CHANGES,
RefreshType.UNCOMMITED_CHANGES_AND_LOG, RefreshType.UNCOMMITED_CHANGES_AND_LOG,
).collect { ).collect {
tabState.coRunOperation(refreshType = RefreshType.NONE) { git -> refresh(tabState.git)
refresh(git)
}
} }
} }
} }
@ -120,13 +118,13 @@ class StatusViewModel @Inject constructor(
} }
fun resetStaged(statusEntry: StatusEntry) = tabState.runOperation( fun resetStaged(statusEntry: StatusEntry) = tabState.runOperation(
refreshType = RefreshType.UNCOMMITED_CHANGES, refreshType = RefreshType.UNCOMMITED_CHANGES_AND_LOG,
) { git -> ) { git ->
resetEntryUseCase(git, statusEntry, staged = true) resetEntryUseCase(git, statusEntry, staged = true)
} }
fun resetUnstaged(statusEntry: StatusEntry) = tabState.runOperation( fun resetUnstaged(statusEntry: StatusEntry) = tabState.runOperation(
refreshType = RefreshType.UNCOMMITED_CHANGES, refreshType = RefreshType.UNCOMMITED_CHANGES_AND_LOG,
) { git -> ) { git ->
resetEntryUseCase(git, statusEntry, staged = false) resetEntryUseCase(git, statusEntry, staged = false)
} }

View File

@ -28,9 +28,7 @@ class SubmodulesViewModel @Inject constructor(
tabScope.launch { tabScope.launch {
tabState.refreshFlowFiltered(RefreshType.ALL_DATA, RefreshType.SUBMODULES) tabState.refreshFlowFiltered(RefreshType.ALL_DATA, RefreshType.SUBMODULES)
.collect { .collect {
tabState.coRunOperation(refreshType = RefreshType.NONE) { git -> refresh(tabState.git)
refresh(git)
}
} }
} }
} }

View File

@ -112,8 +112,6 @@ class TabViewModel @Inject constructor(
when (refreshType) { when (refreshType) {
RefreshType.NONE -> printLog(TAG, "Not refreshing...") RefreshType.NONE -> printLog(TAG, "Not refreshing...")
RefreshType.REPO_STATE -> refreshRepositoryState() RefreshType.REPO_STATE -> refreshRepositoryState()
RefreshType.UNCOMMITED_CHANGES -> checkUncommitedChanges()
RefreshType.UNCOMMITED_CHANGES_AND_LOG -> checkUncommitedChanges()
else -> {} else -> {}
} }
} }
@ -131,11 +129,8 @@ class TabViewModel @Inject constructor(
launch { launch {
tabState.refreshFlowFiltered(RefreshType.ALL_DATA, RefreshType.REPO_STATE) tabState.refreshFlowFiltered(RefreshType.ALL_DATA, RefreshType.REPO_STATE)
.collect { .collect {
tabState.coRunOperation(refreshType = RefreshType.NONE) { git -> loadRepositoryState(tabState.git)
loadRepositoryState(git)
}
} }
} }
} }
} }
@ -166,7 +161,7 @@ class TabViewModel @Inject constructor(
repository.workTree // test if repository is valid repository.workTree // test if repository is valid
_repositorySelectionStatus.value = RepositorySelectionStatus.Open(repository) _repositorySelectionStatus.value = RepositorySelectionStatus.Open(repository)
val git = Git(repository) val git = Git(repository)
tabState.git = git tabState.initGit(git)
onRepositoryChanged(repository.directory.parent) onRepositoryChanged(repository.directory.parent)
refreshRepositoryInfo() refreshRepositoryInfo()
@ -289,6 +284,7 @@ class TabViewModel @Inject constructor(
refreshType = RefreshType.NONE, refreshType = RefreshType.NONE,
) { ) {
updateDiffEntry() updateDiffEntry()
tabState.refreshData(RefreshType.UNCOMMITED_CHANGES_AND_LOG)
// //
// // Stashes list should only be updated if we are doing a stash operation, however it's a small operation // // Stashes list should only be updated if we are doing a stash operation, however it's a small operation
// // that we can afford to do when doing other operations // // that we can afford to do when doing other operations
@ -296,9 +292,9 @@ class TabViewModel @Inject constructor(
// loadRepositoryState(git) // loadRepositoryState(git)
} }
private fun refreshRepositoryInfo() = tabState.safeProcessing( private suspend fun refreshRepositoryInfo() {
refreshType = RefreshType.ALL_DATA, tabState.refreshData(RefreshType.ALL_DATA)
) {} }
fun credentialsDenied() { fun credentialsDenied() {
credentialsStateManager.updateState(CredentialsState.CredentialsDenied) credentialsStateManager.updateState(CredentialsState.CredentialsDenied)
@ -413,8 +409,8 @@ class TabViewModel @Inject constructor(
historyViewModel = null historyViewModel = null
} }
fun refreshAll() { fun refreshAll() = tabScope.launch {
printLog(TAG, "Manual refresh triggered") printLog(TAG, "Manual refresh triggered. IS OPERATION RUNNING ${tabState.operationRunning}")
if (!tabState.operationRunning) { if (!tabState.operationRunning) {
refreshRepositoryInfo() refreshRepositoryInfo()
} }

View File

@ -30,9 +30,7 @@ class TagsViewModel @Inject constructor(
tabScope.launch { tabScope.launch {
tabState.refreshFlowFiltered(RefreshType.ALL_DATA, RefreshType.STASHES) tabState.refreshFlowFiltered(RefreshType.ALL_DATA, RefreshType.STASHES)
.collect { .collect {
tabState.coRunOperation(refreshType = RefreshType.NONE) { git -> refresh(tabState.git)
refresh(git)
}
} }
} }
} }