From 60c15131db563a6e1e30d11437d71a916e723a2f Mon Sep 17 00:00:00 2001 From: Abdelilah El Aissaoui Date: Sun, 4 Jun 2023 18:56:43 +0200 Subject: [PATCH] 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 --- .../com/jetpackduba/gitnuro/ui/AppTab.kt | 11 ++++++++ .../jetpackduba/gitnuro/ui/RepositoryOpen.kt | 4 ++- .../com/jetpackduba/gitnuro/ui/TabsManager.kt | 15 ++++++++--- .../ui/components/RepositoriesTabPanel.kt | 26 ++++++++++++++----- .../gitnuro/viewmodels/TabViewModel.kt | 16 +++++++++++- 5 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/AppTab.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/AppTab.kt index 8d13f51..038f014 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/AppTab.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/AppTab.kt @@ -34,6 +34,17 @@ fun AppTab( val repositorySelectionStatusValue = repositorySelectionStatus.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 { Column( modifier = Modifier diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt index b83da48..6ff1537 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.* import com.jetpackduba.gitnuro.AppConstants +import com.jetpackduba.gitnuro.LocalTabScope import com.jetpackduba.gitnuro.extensions.handMouseClickable import com.jetpackduba.gitnuro.git.DiffEntryType import com.jetpackduba.gitnuro.keybindings.KeybindingOption @@ -137,6 +138,7 @@ fun RepositoryOpenPage( onCancelRebaseInteractive = { tabViewModel.cancelRebaseInteractive() } ) } else { + val currentTabInformation = LocalTabScope.current Column(modifier = Modifier.weight(1f)) { Menu( modifier = Modifier @@ -150,7 +152,7 @@ fun RepositoryOpenPage( val repo = tabViewModel.openDirectoryPicker() if (repo != null) { - tabViewModel.openRepository(repo) + tabViewModel.openAnotherRepository(repo, currentTabInformation) } }, onQuickActions = { showQuickActionsDialog = true }, diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/TabsManager.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/TabsManager.kt index 3ffa825..d9cbc6d 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/TabsManager.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/TabsManager.kt @@ -42,16 +42,23 @@ class TabsManager @Inject constructor( _currentTab.value = _tabsFlow.value.first() } - fun addNewTabFromPath(path: String, selectTab: Boolean) { + fun addNewTabFromPath(path: String, selectTab: Boolean, tabToBeReplaced: TabInformation? = null) { val newTab = newAppTab( tabName = mutableStateOf(""), path = path, ) _tabsFlow.update { - it.toMutableList().apply { - add(newTab) + val newTabsList = it.toMutableList() + + if (tabToBeReplaced != null) { + val index = newTabsList.indexOf(tabToBeReplaced) + newTabsList[index] = newTab + } else { + newTabsList.add(newTab) } + + newTabsList } if (selectTab) { @@ -116,7 +123,7 @@ class TabsManager @Inject constructor( } private fun newAppTab( - tabName: MutableState = mutableStateOf("New tab"), + tabName: MutableState = mutableStateOf(TabInformation.DEFAULT_NAME), path: String? = null, ): TabInformation { return TabInformation( diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/components/RepositoriesTabPanel.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/components/RepositoriesTabPanel.kt index 4e61814..8e55204 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/components/RepositoriesTabPanel.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/components/RepositoriesTabPanel.kt @@ -242,20 +242,34 @@ class TabInformation( init { tabComponent.inject(this) - tabViewModel.onRepositoryChanged = { path -> - this.path = path + if (initialPath != null) { + 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() } - 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() { tabViewModel.dispose() } + + companion object { + const val DEFAULT_NAME = "New tab" + } } fun emptyTabInformation() = TabInformation(mutableStateOf(""), "", {}, null) diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/TabViewModel.kt b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/TabViewModel.kt index e9b4151..27aecff 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/TabViewModel.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/TabViewModel.kt @@ -22,6 +22,8 @@ import com.jetpackduba.gitnuro.system.OpenFilePickerUseCase import com.jetpackduba.gitnuro.system.OpenUrlInBrowserUseCase import com.jetpackduba.gitnuro.system.PickerType 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.UpdatesRepository import kotlinx.coroutines.* @@ -66,8 +68,10 @@ class TabViewModel @Inject constructor( private val abortRebaseUseCase: AbortRebaseUseCase, private val openFilePickerUseCase: OpenFilePickerUseCase, private val openUrlInBrowserUseCase: OpenUrlInBrowserUseCase, + private val tabsManager: TabsManager, 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 selectedItem: StateFlow = tabState.selectedItem var diffViewModel: DiffViewModel? = null @@ -163,6 +167,15 @@ class TabViewModel @Inject constructor( 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) { openRepository(File(directory)) } @@ -197,6 +210,7 @@ class TabViewModel @Inject constructor( watchRepositoryChanges(git) } catch (ex: Exception) { + onRepositoryChanged(null) ex.printStackTrace() errorsManager.addError(newErrorNow(ex, ex.localizedMessage)) _repositorySelectionStatus.value = RepositorySelectionStatus.None @@ -324,7 +338,7 @@ class TabViewModel @Inject constructor( credentialsStateManager.updateState(CredentialsAccepted.SshCredentialsAccepted(password)) } - var onRepositoryChanged: (path: String) -> Unit = {} + var onRepositoryChanged: (path: String?) -> Unit = {} fun dispose() { tabScope.cancel()