From fda901c0f2783f027efc12a3a1bf7761fc300cf4 Mon Sep 17 00:00:00 2001 From: Abdelilah El Aissaoui Date: Fri, 15 Oct 2021 02:34:26 +0200 Subject: [PATCH] Now opened tabs will be saved --- build.gradle.kts | 2 + src/main/kotlin/app/App.kt | 61 ++++++++++++++----- .../{GPreferences.kt => AppPreferences.kt} | 11 +++- src/main/kotlin/app/AppStateManager.kt | 56 +++++++++++++++++ src/main/kotlin/app/di/AppComponent.kt | 2 + src/main/kotlin/app/git/GitManager.kt | 26 ++++---- src/main/kotlin/app/ui/SystemDialogs.kt | 12 ++-- .../app/ui/components/RepositoriesTabPanel.kt | 19 ++---- 8 files changed, 142 insertions(+), 47 deletions(-) rename src/main/kotlin/app/{GPreferences.kt => AppPreferences.kt} (58%) create mode 100644 src/main/kotlin/app/AppStateManager.kt diff --git a/build.gradle.kts b/build.gradle.kts index 8944256..6108161 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,6 +6,7 @@ plugins { // __KOTLIN_COMPOSE_VERSION__ kotlin("jvm") version "1.5.21" kotlin("kapt") version "1.5.21" + kotlin("plugin.serialization") version "1.5.21" // __LATEST_COMPOSE_RELEASE_VERSION__ id("org.jetbrains.compose") version "1.0.0-alpha3" } @@ -27,6 +28,7 @@ dependencies { implementation("org.apache.sshd:sshd-core:2.7.0") implementation("com.google.dagger:dagger:2.39.1") implementation("org.jetbrains.kotlin:kotlin-reflect:1.5.21") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.0") kapt("com.google.dagger:dagger-compiler:2.39.1") } diff --git a/src/main/kotlin/app/App.kt b/src/main/kotlin/app/App.kt index 9db5ad6..700b684 100644 --- a/src/main/kotlin/app/App.kt +++ b/src/main/kotlin/app/App.kt @@ -29,13 +29,18 @@ class Main { @Inject lateinit var gitManagerProvider: Provider + @Inject + lateinit var appStateManager: AppStateManager init { appComponent.inject(this) + + appStateManager.loadRepositoriesTabs() } fun start() = application { var isOpen by remember { mutableStateOf(true) } + if (isOpen) { Window( title = "Gitnuro", @@ -49,15 +54,19 @@ class Main { ) { AppTheme { val tabs = remember { - val tabName = mutableStateOf("New tab") - mutableStateOf( - listOf( - TabInformation(tabName, key = 0) { - val gitManager = remember { gitManagerProvider.get() } - Gitnuro(gitManager, false, tabName) - }, + + val repositoriesSavedTabs = appStateManager.openRepositoriesPathsTabs + var repoTabs = repositoriesSavedTabs.map { repositoryTab -> + newAppTab(key = repositoryTab.key, path = repositoryTab.value) + } + + if (repoTabs.isEmpty()) { + repoTabs = listOf( + newAppTab() ) - ) + } + + mutableStateOf(repoTabs) } var selectedTabKey by remember { mutableStateOf(0) } @@ -74,12 +83,14 @@ class Main { onTabSelected = { newSelectedTabKey -> selectedTabKey = newSelectedTabKey }, - newTabContent = { tabName -> - val gitManager = remember { gitManagerProvider.get() } - Gitnuro(gitManager, true, tabName) + newTabContent = { key -> + newAppTab(key) }, onTabsUpdated = { tabInformationList -> tabs.value = tabInformationList + }, + onTabClosed = { key -> + appStateManager.repositoryTabRemoved(key) } ) @@ -111,13 +122,35 @@ class Main { } } } + + private fun newAppTab( + key: Int = 0, + tabName: MutableState = mutableStateOf("New tab"), + path: String? = null, + ): TabInformation { + + return TabInformation( + title = tabName, + key = key + ) { + val gitManager = remember { gitManagerProvider.get() } + gitManager.onRepositoryChanged = { path -> + if (path == null) { + appStateManager.repositoryTabRemoved(key) + } else + appStateManager.repositoryTabChanged(key, path) + } + + Gitnuro(gitManager, path, tabName) + } + } } @Composable -fun Gitnuro(gitManager: GitManager, isNewTab: Boolean, tabName: MutableState) { +fun Gitnuro(gitManager: GitManager, repositoryPath: String?, tabName: MutableState) { LaunchedEffect(gitManager) { - if (!isNewTab) - gitManager.loadLatestOpenedRepository() + if (repositoryPath != null) + gitManager.openRepository(repositoryPath) } diff --git a/src/main/kotlin/app/GPreferences.kt b/src/main/kotlin/app/AppPreferences.kt similarity index 58% rename from src/main/kotlin/app/GPreferences.kt rename to src/main/kotlin/app/AppPreferences.kt index bc754fa..e1c5343 100644 --- a/src/main/kotlin/app/GPreferences.kt +++ b/src/main/kotlin/app/AppPreferences.kt @@ -2,14 +2,23 @@ package app import java.util.prefs.Preferences import javax.inject.Inject +import javax.inject.Singleton private const val PREFERENCES_NAME = "GitnuroConfig" +private const val PREF_LATEST_REPOSITORIES_OPENED = "latestRepositoriesOpened" private const val PREF_LAST_OPENED_REPOSITORY_PATH = "lastOpenedRepositoryPath" -class GPreferences @Inject constructor() { +@Singleton +class AppPreferences @Inject constructor() { private val preferences: Preferences = Preferences.userRoot().node(PREFERENCES_NAME) + var latestTabsOpened: String + get() = preferences.get(PREF_LATEST_REPOSITORIES_OPENED, "") + set(value) { + preferences.put(PREF_LATEST_REPOSITORIES_OPENED, value) + } + var latestOpenedRepositoryPath: String get() = preferences.get(PREF_LAST_OPENED_REPOSITORY_PATH, "") set(value) { diff --git a/src/main/kotlin/app/AppStateManager.kt b/src/main/kotlin/app/AppStateManager.kt new file mode 100644 index 0000000..e949df0 --- /dev/null +++ b/src/main/kotlin/app/AppStateManager.kt @@ -0,0 +1,56 @@ +package app + +import kotlinx.coroutines.* +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AppStateManager @Inject constructor( + private val appPreferences: AppPreferences, +) { + + private val _openRepositoriesPaths = mutableMapOf() + val openRepositoriesPathsTabs: Map + get() = _openRepositoriesPaths + + private val appStateScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) // TODO Stop this when closing the app + + var latestOpenedRepositoryPath: String + get() = appPreferences.latestOpenedRepositoryPath + set(value) { + appPreferences.latestOpenedRepositoryPath = value + } + + fun repositoryTabChanged(key: Int, path: String) { + _openRepositoriesPaths[key] = path + + updateSavedRepositoryTabs() + } + + fun repositoryTabRemoved(key: Int) { + _openRepositoriesPaths.remove(key) + + updateSavedRepositoryTabs() + } + + private fun updateSavedRepositoryTabs() = appStateScope.launch(Dispatchers.IO) { + val tabsList = _openRepositoriesPaths.map { it.value } + appPreferences.latestTabsOpened = Json.encodeToString(tabsList) + } + + fun loadRepositoriesTabs() = appStateScope.launch(Dispatchers.IO) { + val repositoriesSaved = appPreferences.latestTabsOpened + + if (repositoriesSaved.isNotEmpty()) { + val repositoriesList = Json.decodeFromString>(repositoriesSaved) + + repositoriesList.forEachIndexed { index, repository -> + _openRepositoriesPaths[index] = repository + } + } + + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/di/AppComponent.kt b/src/main/kotlin/app/di/AppComponent.kt index 0f73320..97578d8 100644 --- a/src/main/kotlin/app/di/AppComponent.kt +++ b/src/main/kotlin/app/di/AppComponent.kt @@ -2,7 +2,9 @@ package app.di import app.Main import dagger.Component +import javax.inject.Singleton +@Singleton @Component interface AppComponent { fun inject(main: Main) diff --git a/src/main/kotlin/app/git/GitManager.kt b/src/main/kotlin/app/git/GitManager.kt index 754e1b9..a474535 100644 --- a/src/main/kotlin/app/git/GitManager.kt +++ b/src/main/kotlin/app/git/GitManager.kt @@ -11,19 +11,20 @@ import org.eclipse.jgit.lib.Ref import org.eclipse.jgit.lib.Repository import org.eclipse.jgit.revwalk.RevCommit import org.eclipse.jgit.storage.file.FileRepositoryBuilder -import app.GPreferences +import app.AppPreferences +import app.AppStateManager import java.io.File import javax.inject.Inject class GitManager @Inject constructor( - private val preferences: GPreferences, private val statusManager: StatusManager, private val logManager: LogManager, private val remoteOperationsManager: RemoteOperationsManager, private val branchesManager: BranchesManager, private val stashManager: StashManager, private val diffManager: DiffManager, + private val appStateManager: AppStateManager, ) { val repositoryName: String get() = safeGit.repository.directory.parentFile.name @@ -59,12 +60,12 @@ class GitManager @Inject constructor( val stashStatus: StateFlow get() = stashManager.stashStatus - val latestDirectoryOpened: File? - get() = File(preferences.latestOpenedRepositoryPath).parentFile - val credentialsState: StateFlow get() = credentialsStateManager.credentialsState + val latestOpenedRepositoryPath: String + get() = appStateManager.latestOpenedRepositoryPath + private var git: Git? = null val safeGit: Git @@ -77,12 +78,8 @@ class GitManager @Inject constructor( return git } - - suspend fun loadLatestOpenedRepository() = withContext(Dispatchers.IO) { - val latestOpenedRepositoryPath = preferences.latestOpenedRepositoryPath - if (latestOpenedRepositoryPath.isNotEmpty()) { - openRepository(File(latestOpenedRepositoryPath)) - } + fun openRepository(directory: String) { + openRepository(File(directory)) } fun openRepository(directory: File) { @@ -107,13 +104,16 @@ class GitManager @Inject constructor( try { repository.workTree // test if repository is valid - preferences.latestOpenedRepositoryPath = gitDirectory.path _repositorySelectionStatus.value = RepositorySelectionStatus.Open(repository) git = Git(repository) + onRepositoryChanged(repository.directory.parent) + appStateManager.latestOpenedRepositoryPath = directory.absolutePath refreshRepositoryInfo() } catch (ex: Exception) { ex.printStackTrace() + onRepositoryChanged(null) + } } @@ -213,6 +213,8 @@ class GitManager @Inject constructor( fun stageAll() = managerScope.launch { statusManager.stageAll(safeGit) } + + var onRepositoryChanged: (path: String?) -> Unit = {} } diff --git a/src/main/kotlin/app/ui/SystemDialogs.kt b/src/main/kotlin/app/ui/SystemDialogs.kt index 94728f1..7a67166 100644 --- a/src/main/kotlin/app/ui/SystemDialogs.kt +++ b/src/main/kotlin/app/ui/SystemDialogs.kt @@ -2,16 +2,16 @@ import app.git.GitManager import javax.swing.JFileChooser fun openRepositoryDialog(gitManager: GitManager) { - val latestDirectoryOpened = gitManager.latestDirectoryOpened + val latestDirectoryOpened = gitManager.latestOpenedRepositoryPath - val f = if (latestDirectoryOpened == null) + val fileChooser = if (latestDirectoryOpened.isEmpty()) JFileChooser() else JFileChooser(latestDirectoryOpened) - f.fileSelectionMode = JFileChooser.DIRECTORIES_ONLY - f.showSaveDialog(null) + fileChooser.fileSelectionMode = JFileChooser.DIRECTORIES_ONLY + fileChooser.showSaveDialog(null) - if (f.selectedFile != null) - gitManager.openRepository(f.selectedFile) + if (fileChooser.selectedFile != null) + gitManager.openRepository(fileChooser.selectedFile) } \ No newline at end of file diff --git a/src/main/kotlin/app/ui/components/RepositoriesTabPanel.kt b/src/main/kotlin/app/ui/components/RepositoriesTabPanel.kt index 34970e2..04a7515 100644 --- a/src/main/kotlin/app/ui/components/RepositoriesTabPanel.kt +++ b/src/main/kotlin/app/ui/components/RepositoriesTabPanel.kt @@ -14,8 +14,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import app.theme.primaryLight -import app.theme.primaryTextColor import app.theme.tabColorActive import app.theme.tabColorInactive @@ -27,7 +25,8 @@ fun RepositoriesTabPanel( selectedTabKey: Int, onTabSelected: (Int) -> Unit, onTabsUpdated: (List) -> Unit, - newTabContent: @Composable (tabTitle: MutableState) -> Unit, + onTabClosed: (Int) -> Unit, + newTabContent: (key: Int) -> TabInformation, ) { var tabsIdentifier by remember { mutableStateOf(tabs.count()) @@ -40,13 +39,7 @@ fun RepositoriesTabPanel( tabsIdentifier++ - val tabName = mutableStateOf("New tab") - - tabsCopy.add( - TabInformation(tabName, key = tabsIdentifier) { - newTabContent(tabName) - } - ) + tabsCopy.add(newTabContent(tabsIdentifier)) onTabSelected(tabsIdentifier) onTabsUpdated(tabsCopy) @@ -83,17 +76,15 @@ fun RepositoriesTabPanel( } else { tabsIdentifier++ - val tabName = mutableStateOf("New tab") tabsCopy.add( - TabInformation(tabName, key = tabsIdentifier) { - newTabContent(tabName) - } + newTabContent(tabsIdentifier) ) onTabSelected(tabsIdentifier) } } + onTabClosed(tab.key) onTabsUpdated(tabsCopy) } )