Make sure changing repo starts in a clean state & implemented tab lazy loading

Changing the open repository in the current tab will now create a new tab that replaces the current one instead of updating the TabViewModel and having to make sure that every subviewmodel handles the change of repository properly.

This also allowed implementing lazy loading of a tab, so it does not load every repo at the same time.

Fixes #122
This commit is contained in:
Abdelilah El Aissaoui 2023-06-04 18:56:43 +02:00
parent 8462a40733
commit 60c15131db
No known key found for this signature in database
GPG Key ID: 7587FC860F594869
5 changed files with 60 additions and 12 deletions

View File

@ -34,6 +34,17 @@ fun AppTab(
val repositorySelectionStatusValue = repositorySelectionStatus.value val repositorySelectionStatusValue = repositorySelectionStatus.value
val processingState = tabViewModel.processing.collectAsState().value val processingState = tabViewModel.processing.collectAsState().value
LaunchedEffect(tabViewModel) {
// Init the tab content when the tab is selected and also remove the "initialPath" to avoid opening the
// repository everytime the user changes between tabs
val initialPath = tabViewModel.initialPath
tabViewModel.initialPath = null
if (initialPath != null) {
tabViewModel.openRepository(initialPath)
}
}
Box { Box {
Column( Column(
modifier = Modifier modifier = Modifier

View File

@ -20,6 +20,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.* import androidx.compose.ui.unit.*
import com.jetpackduba.gitnuro.AppConstants import com.jetpackduba.gitnuro.AppConstants
import com.jetpackduba.gitnuro.LocalTabScope
import com.jetpackduba.gitnuro.extensions.handMouseClickable import com.jetpackduba.gitnuro.extensions.handMouseClickable
import com.jetpackduba.gitnuro.git.DiffEntryType import com.jetpackduba.gitnuro.git.DiffEntryType
import com.jetpackduba.gitnuro.keybindings.KeybindingOption import com.jetpackduba.gitnuro.keybindings.KeybindingOption
@ -137,6 +138,7 @@ fun RepositoryOpenPage(
onCancelRebaseInteractive = { tabViewModel.cancelRebaseInteractive() } onCancelRebaseInteractive = { tabViewModel.cancelRebaseInteractive() }
) )
} else { } else {
val currentTabInformation = LocalTabScope.current
Column(modifier = Modifier.weight(1f)) { Column(modifier = Modifier.weight(1f)) {
Menu( Menu(
modifier = Modifier modifier = Modifier
@ -150,7 +152,7 @@ fun RepositoryOpenPage(
val repo = tabViewModel.openDirectoryPicker() val repo = tabViewModel.openDirectoryPicker()
if (repo != null) { if (repo != null) {
tabViewModel.openRepository(repo) tabViewModel.openAnotherRepository(repo, currentTabInformation)
} }
}, },
onQuickActions = { showQuickActionsDialog = true }, onQuickActions = { showQuickActionsDialog = true },

View File

@ -42,16 +42,23 @@ class TabsManager @Inject constructor(
_currentTab.value = _tabsFlow.value.first() _currentTab.value = _tabsFlow.value.first()
} }
fun addNewTabFromPath(path: String, selectTab: Boolean) { fun addNewTabFromPath(path: String, selectTab: Boolean, tabToBeReplaced: TabInformation? = null) {
val newTab = newAppTab( val newTab = newAppTab(
tabName = mutableStateOf(""), tabName = mutableStateOf(""),
path = path, path = path,
) )
_tabsFlow.update { _tabsFlow.update {
it.toMutableList().apply { val newTabsList = it.toMutableList()
add(newTab)
if (tabToBeReplaced != null) {
val index = newTabsList.indexOf(tabToBeReplaced)
newTabsList[index] = newTab
} else {
newTabsList.add(newTab)
} }
newTabsList
} }
if (selectTab) { if (selectTab) {
@ -116,7 +123,7 @@ class TabsManager @Inject constructor(
} }
private fun newAppTab( private fun newAppTab(
tabName: MutableState<String> = mutableStateOf("New tab"), tabName: MutableState<String> = mutableStateOf(TabInformation.DEFAULT_NAME),
path: String? = null, path: String? = null,
): TabInformation { ): TabInformation {
return TabInformation( return TabInformation(

View File

@ -242,20 +242,34 @@ class TabInformation(
init { init {
tabComponent.inject(this) tabComponent.inject(this)
tabViewModel.onRepositoryChanged = { path -> if (initialPath != null) {
this.path = path tabName.value = Path(initialPath).name
}
tabViewModel.onRepositoryChanged = { newPath ->
this.path = newPath
if (newPath == null) {
tabName.value = TabInformation.DEFAULT_NAME
} else {
tabName.value = Path(newPath).name
appStateManager.repositoryTabChanged(newPath)
}
tabName.value = Path(path).name
appStateManager.repositoryTabChanged(path)
onTabPathChanged() onTabPathChanged()
} }
if (initialPath != null)
tabViewModel.openRepository(initialPath) // Set the path that should be loaded when the tab is selected for the first time
tabViewModel.initialPath = initialPath
} }
fun dispose() { fun dispose() {
tabViewModel.dispose() tabViewModel.dispose()
} }
companion object {
const val DEFAULT_NAME = "New tab"
}
} }
fun emptyTabInformation() = TabInformation(mutableStateOf(""), "", {}, null) fun emptyTabInformation() = TabInformation(mutableStateOf(""), "", {}, null)

View File

@ -22,6 +22,8 @@ import com.jetpackduba.gitnuro.system.OpenFilePickerUseCase
import com.jetpackduba.gitnuro.system.OpenUrlInBrowserUseCase import com.jetpackduba.gitnuro.system.OpenUrlInBrowserUseCase
import com.jetpackduba.gitnuro.system.PickerType import com.jetpackduba.gitnuro.system.PickerType
import com.jetpackduba.gitnuro.ui.SelectedItem import com.jetpackduba.gitnuro.ui.SelectedItem
import com.jetpackduba.gitnuro.ui.TabsManager
import com.jetpackduba.gitnuro.ui.components.TabInformation
import com.jetpackduba.gitnuro.updates.Update import com.jetpackduba.gitnuro.updates.Update
import com.jetpackduba.gitnuro.updates.UpdatesRepository import com.jetpackduba.gitnuro.updates.UpdatesRepository
import kotlinx.coroutines.* import kotlinx.coroutines.*
@ -66,8 +68,10 @@ class TabViewModel @Inject constructor(
private val abortRebaseUseCase: AbortRebaseUseCase, private val abortRebaseUseCase: AbortRebaseUseCase,
private val openFilePickerUseCase: OpenFilePickerUseCase, private val openFilePickerUseCase: OpenFilePickerUseCase,
private val openUrlInBrowserUseCase: OpenUrlInBrowserUseCase, private val openUrlInBrowserUseCase: OpenUrlInBrowserUseCase,
private val tabsManager: TabsManager,
private val tabScope: CoroutineScope, private val tabScope: CoroutineScope,
) { ) {
var initialPath: String? = null // Stores the path that should be opened when the tab is selected
val errorsManager: ErrorsManager = tabState.errorsManager val errorsManager: ErrorsManager = tabState.errorsManager
val selectedItem: StateFlow<SelectedItem> = tabState.selectedItem val selectedItem: StateFlow<SelectedItem> = tabState.selectedItem
var diffViewModel: DiffViewModel? = null var diffViewModel: DiffViewModel? = null
@ -163,6 +167,15 @@ class TabViewModel @Inject constructor(
rebaseInteractiveViewModel?.startRebaseInteractive(taskEvent.revCommit) rebaseInteractiveViewModel?.startRebaseInteractive(taskEvent.revCommit)
} }
/**
* To make sure the tab opens the new repository with a clean state,
* instead of opening the repo in the same ViewModel we simply create a new tab with a new TabViewModel
* replacing the current tab
*/
fun openAnotherRepository(directory: String, current: TabInformation) {
tabsManager.addNewTabFromPath(directory, true, current)
}
fun openRepository(directory: String) { fun openRepository(directory: String) {
openRepository(File(directory)) openRepository(File(directory))
} }
@ -197,6 +210,7 @@ class TabViewModel @Inject constructor(
watchRepositoryChanges(git) watchRepositoryChanges(git)
} catch (ex: Exception) { } catch (ex: Exception) {
onRepositoryChanged(null)
ex.printStackTrace() ex.printStackTrace()
errorsManager.addError(newErrorNow(ex, ex.localizedMessage)) errorsManager.addError(newErrorNow(ex, ex.localizedMessage))
_repositorySelectionStatus.value = RepositorySelectionStatus.None _repositorySelectionStatus.value = RepositorySelectionStatus.None
@ -324,7 +338,7 @@ class TabViewModel @Inject constructor(
credentialsStateManager.updateState(CredentialsAccepted.SshCredentialsAccepted(password)) credentialsStateManager.updateState(CredentialsAccepted.SshCredentialsAccepted(password))
} }
var onRepositoryChanged: (path: String) -> Unit = {} var onRepositoryChanged: (path: String?) -> Unit = {}
fun dispose() { fun dispose() {
tabScope.cancel() tabScope.cancel()