From aae80445f9b0d78af0f31a2bfccedd90b21e7f91 Mon Sep 17 00:00:00 2001 From: Abdelilah El Aissaoui Date: Fri, 25 Feb 2022 16:51:52 +0100 Subject: [PATCH] Moved delayed refresh to tabViewModel instead of the file watched and added detection of git dir changed to refresh the whole repository information --- src/main/kotlin/app/git/FileChangesWatcher.kt | 38 ++++---------- .../kotlin/app/viewmodels/TabViewModel.kt | 51 +++++++++++++++++-- 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/src/main/kotlin/app/git/FileChangesWatcher.kt b/src/main/kotlin/app/git/FileChangesWatcher.kt index 400968c..3cc046c 100644 --- a/src/main/kotlin/app/git/FileChangesWatcher.kt +++ b/src/main/kotlin/app/git/FileChangesWatcher.kt @@ -1,5 +1,6 @@ package app.git +import app.extensions.systemSeparator import kotlinx.coroutines.* import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow @@ -9,18 +10,14 @@ import java.nio.file.StandardWatchEventKinds.* import java.nio.file.attribute.BasicFileAttributes import javax.inject.Inject -private const val MIN_TIME_IN_MS_BETWEEN_REFRESHES = 500L - class FileChangesWatcher @Inject constructor() { - private var lastNotify = 0L - private var asyncJob: Job? = null - private val _changesNotifier = MutableSharedFlow() - val changesNotifier: SharedFlow = _changesNotifier + private val _changesNotifier = MutableSharedFlow() + val changesNotifier: SharedFlow = _changesNotifier + val keys = mutableMapOf() suspend fun watchDirectoryPath(pathStr: String, ignoredDirsPath: List) = withContext(Dispatchers.IO) { println(ignoredDirsPath) - val watchService = FileSystems.getDefault().newWatchService() val path = Paths.get(pathStr) @@ -39,7 +36,8 @@ class FileChangesWatcher @Inject constructor() { val isIgnoredDirectory = ignoredDirsPath.any { "$pathStr/$it" == dir.toString() } return if (!isIgnoredDirectory) { - dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY) + val watchKey = dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY) + keys[watchKey] = dir FileVisitResult.CONTINUE } else { FileVisitResult.SKIP_SUBTREE @@ -51,29 +49,15 @@ class FileChangesWatcher @Inject constructor() { while (watchService.take().also { key = it } != null) { key.pollEvents() - println("Polled events") + println("Polled events on dir ${keys[key]}") - asyncJob?.cancel() + val dir = keys[key] ?: return@withContext - // Sometimes external apps can run filesystem multiple operations in a fraction of a second. - // To prevent excessive updates, we add a slight delay between updates emission to prevent slowing down - // the app by constantly running "git status". - val currentTimeMillis = System.currentTimeMillis() - val diffTime = currentTimeMillis - lastNotify + val hasGitDirectoryChanged = dir.startsWith("$pathStr$systemSeparator.git$systemSeparator") - if (diffTime > MIN_TIME_IN_MS_BETWEEN_REFRESHES) { - _changesNotifier.emit(currentTimeMillis) - println("Sync emit with diff time $diffTime") - } else { - asyncJob = async { - delay(MIN_TIME_IN_MS_BETWEEN_REFRESHES) - println("Async emit") - if (isActive) - _changesNotifier.emit(currentTimeMillis) - } - } + println("Has git dir changed: $hasGitDirectoryChanged") - lastNotify = currentTimeMillis + _changesNotifier.emit(hasGitDirectoryChanged) key.reset() } diff --git a/src/main/kotlin/app/viewmodels/TabViewModel.kt b/src/main/kotlin/app/viewmodels/TabViewModel.kt index 33869a7..b0255f9 100644 --- a/src/main/kotlin/app/viewmodels/TabViewModel.kt +++ b/src/main/kotlin/app/viewmodels/TabViewModel.kt @@ -20,6 +20,7 @@ import org.eclipse.jgit.storage.file.FileRepositoryBuilder import java.io.File import javax.inject.Inject +private const val MIN_TIME_IN_MS_BETWEEN_REFRESHES = 500L class TabViewModel @Inject constructor( val logViewModel: LogViewModel, @@ -144,12 +145,42 @@ class TabViewModel @Inject constructor( private suspend fun watchRepositoryChanges(git: Git) = tabState.managerScope.launch(Dispatchers.IO) { val ignored = git.status().call().ignoredNotInIndex.toList() + var asyncJob: Job? = null + var lastNotify = 0L + var hasGitDirChanged = false launch { - fileChangesWatcher.changesNotifier.collect { + fileChangesWatcher.changesNotifier.collect { latestUpdateChangedGitDir -> if (!tabState.operationRunning) { // Only update if there isn't any process running - println("Changes detected, loading status") - checkUncommitedChanges() + if(latestUpdateChangedGitDir) { + hasGitDirChanged = true + } + + asyncJob?.cancel() + + // Sometimes external apps can run filesystem multiple operations in a fraction of a second. + // To prevent excessive updates, we add a slight delay between updates emission to prevent slowing down + // the app by constantly running "git status". + val currentTimeMillis = System.currentTimeMillis() + val diffTime = currentTimeMillis - lastNotify + + // When .git dir has changed, do the refresh with a delay to avoid doing operations while a git + // operation may be running + if (diffTime > MIN_TIME_IN_MS_BETWEEN_REFRESHES && !hasGitDirChanged) { + updateApp(false) + println("Sync emit with diff time $diffTime") + } else { + asyncJob = async { + delay(MIN_TIME_IN_MS_BETWEEN_REFRESHES) + println("Async emit") + if (isActive) + updateApp(hasGitDirChanged) + + hasGitDirChanged = false + } + } + + lastNotify = currentTimeMillis } } } @@ -159,6 +190,18 @@ class TabViewModel @Inject constructor( ) } + suspend fun updateApp(hasGitDirChanged: Boolean) { + if(hasGitDirChanged) { + println("Changes detected in git directory, full refresh") + + refreshRepositoryInfo() + } else { + println("Changes detected, partial refresh") + + checkUncommitedChanges() + } + } + private suspend fun checkUncommitedChanges(fullUpdateLog: Boolean = false) = tabState.runOperation( refreshType = RefreshType.NONE, ) { git -> @@ -182,13 +225,13 @@ class TabViewModel @Inject constructor( private suspend fun refreshRepositoryInfo() = tabState.safeProcessing( refreshType = RefreshType.NONE, ) { git -> + loadRepositoryState(git) logViewModel.refresh(git) branchesViewModel.refresh(git) remotesViewModel.refresh(git) tagsViewModel.refresh(git) statusViewModel.refresh(git) stashesViewModel.refresh(git) - loadRepositoryState(git) } fun credentialsDenied() {