Moved delayed refresh to tabViewModel instead of the file watched and added detection of git dir changed to refresh the whole repository information

This commit is contained in:
Abdelilah El Aissaoui 2022-02-25 16:51:52 +01:00
parent bbea356eb2
commit aae80445f9
2 changed files with 58 additions and 31 deletions

View File

@ -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<Long>()
val changesNotifier: SharedFlow<Long> = _changesNotifier
private val _changesNotifier = MutableSharedFlow<Boolean>()
val changesNotifier: SharedFlow<Boolean> = _changesNotifier
val keys = mutableMapOf<WatchKey, Path>()
suspend fun watchDirectoryPath(pathStr: String, ignoredDirsPath: List<String>) = 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()
}

View File

@ -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() {