Improved welcome/new tab page

This commit is contained in:
Abdelilah El Aissaoui 2021-10-16 03:39:59 +02:00
parent cc2a7e180f
commit cd592292d1
9 changed files with 222 additions and 45 deletions

View File

@ -200,11 +200,7 @@ fun App(gitManager: GitManager, repositoryPath: String?, tabName: MutableState<S
if (isProcessing)
Box(modifier = Modifier.fillMaxSize()) //TODO this should block of the mouse/keyboard events while visible
}
}
}
@Composable

View File

@ -6,22 +6,22 @@ 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"
private const val PREF_LATEST_REPOSITORIES_TABS_OPENED = "latestRepositoriesTabsOpened"
private const val PREF_LAST_OPENED_REPOSITORIES_PATH = "lastOpenedRepositoriesList"
@Singleton
class AppPreferences @Inject constructor() {
private val preferences: Preferences = Preferences.userRoot().node(PREFERENCES_NAME)
var latestTabsOpened: String
get() = preferences.get(PREF_LATEST_REPOSITORIES_OPENED, "")
get() = preferences.get(PREF_LATEST_REPOSITORIES_TABS_OPENED, "")
set(value) {
preferences.put(PREF_LATEST_REPOSITORIES_OPENED, value)
preferences.put(PREF_LATEST_REPOSITORIES_TABS_OPENED, value)
}
var latestOpenedRepositoryPath: String
get() = preferences.get(PREF_LAST_OPENED_REPOSITORY_PATH, "")
var latestOpenedRepositoriesPath: String
get() = preferences.get(PREF_LAST_OPENED_REPOSITORIES_PATH, "")
set(value) {
preferences.put(PREF_LAST_OPENED_REPOSITORY_PATH, value)
preferences.put(PREF_LAST_OPENED_REPOSITORIES_PATH, value)
}
}

View File

@ -11,36 +11,52 @@ import javax.inject.Singleton
class AppStateManager @Inject constructor(
private val appPreferences: AppPreferences,
) {
private val _openRepositoriesPaths = mutableMapOf<Int, String>()
val openRepositoriesPathsTabs: Map<Int, String>
get() = _openRepositoriesPaths
private val _latestOpenedRepositoriesPaths = mutableListOf<String>()
val latestOpenedRepositoriesPaths: List<String>
get() = _latestOpenedRepositoriesPaths
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
}
val latestOpenedRepositoryPath: String
get() = _latestOpenedRepositoriesPaths.firstOrNull() ?: ""
fun repositoryTabChanged(key: Int, path: String) {
fun repositoryTabChanged(key: Int, path: String) = appStateScope.launch(Dispatchers.IO) {
// Do not save already saved repos
if (!_openRepositoriesPaths.containsValue(path))
_openRepositoriesPaths[key] = path
// Remove any previously existing path
_latestOpenedRepositoriesPaths.remove(path)
// Add the latest one to the beginning
_latestOpenedRepositoriesPaths.add(0, path)
if (_latestOpenedRepositoriesPaths.count() > 5)
_latestOpenedRepositoriesPaths.removeLast()
updateSavedRepositoryTabs()
updateLatestRepositoryTabs()
}
fun repositoryTabRemoved(key: Int) {
fun repositoryTabRemoved(key: Int) = appStateScope.launch(Dispatchers.IO) {
_openRepositoriesPaths.remove(key)
updateSavedRepositoryTabs()
}
private fun updateSavedRepositoryTabs() = appStateScope.launch(Dispatchers.IO) {
private suspend fun updateSavedRepositoryTabs() = withContext(Dispatchers.IO) {
val tabsList = _openRepositoriesPaths.map { it.value }
appPreferences.latestTabsOpened = Json.encodeToString(tabsList)
}
private suspend fun updateLatestRepositoryTabs() = withContext(Dispatchers.IO) {
appPreferences.latestOpenedRepositoriesPath = Json.encodeToString(_latestOpenedRepositoriesPaths)
}
fun loadRepositoriesTabs() = appStateScope.launch(Dispatchers.IO) {
val repositoriesSaved = appPreferences.latestTabsOpened
@ -52,5 +68,10 @@ class AppStateManager @Inject constructor(
}
}
val repositoriesPathsSaved = appPreferences.latestOpenedRepositoriesPath
if(repositoriesPathsSaved.isNotEmpty()) {
val repositories = Json.decodeFromString<List<String>>(repositoriesPathsSaved)
_latestOpenedRepositoriesPaths.addAll(repositories)
}
}
}

View File

@ -8,3 +8,24 @@ get() {
val md = MessageDigest.getInstance("MD5")
return BigInteger(1, md.digest(this.toByteArray())).toString(16).padStart(32, '0')
}
val String.dirName: String
get() {
val parts = this.split("/")
return if(parts.isNotEmpty())
parts.last()
else
this
}
val String.dirPath: String
get() {
val parts = this.split("/").toMutableList()
return if(parts.count() > 1) {
parts.removeLast()
parts.joinToString("/")
} else
this
}

View File

@ -11,7 +11,6 @@ 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.AppPreferences
import app.AppStateManager
import java.io.File
import javax.inject.Inject
@ -24,7 +23,7 @@ class GitManager @Inject constructor(
private val branchesManager: BranchesManager,
private val stashManager: StashManager,
private val diffManager: DiffManager,
private val appStateManager: AppStateManager,
val appStateManager: AppStateManager,
) {
val repositoryName: String
get() = safeGit.repository.directory.parentFile.name
@ -63,9 +62,6 @@ class GitManager @Inject constructor(
val credentialsState: StateFlow<CredentialsState>
get() = credentialsStateManager.credentialsState
val latestOpenedRepositoryPath: String
get() = appStateManager.latestOpenedRepositoryPath
private var git: Git? = null
val safeGit: Git
@ -109,7 +105,6 @@ class GitManager @Inject constructor(
git = Git(repository)
onRepositoryChanged(repository.directory.parent)
appStateManager.latestOpenedRepositoryPath = directory.absolutePath
refreshRepositoryInfo()
} catch (ex: Exception) {
ex.printStackTrace()

View File

@ -2,7 +2,8 @@ import app.git.GitManager
import javax.swing.JFileChooser
fun openRepositoryDialog(gitManager: GitManager) {
val latestDirectoryOpened = gitManager.latestOpenedRepositoryPath
val appStateManager = gitManager.appStateManager
val latestDirectoryOpened = appStateManager.latestOpenedRepositoryPath
val fileChooser = if (latestDirectoryOpened.isEmpty())
JFileChooser()

View File

@ -1,7 +1,10 @@
package app.ui
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@ -11,49 +14,153 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import app.git.GitManager
import openRepositoryDialog
import androidx.compose.foundation.lazy.items
import androidx.compose.ui.Alignment
import androidx.compose.ui.BiasAlignment
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.sp
import app.extensions.dirName
import app.extensions.dirPath
import app.theme.primaryTextColor
import app.theme.secondaryTextColor
import java.awt.Desktop
import java.net.URI
@Composable
fun WelcomePage(gitManager: GitManager) {
val appStateManager = gitManager.appStateManager
Row(
modifier = Modifier
.fillMaxSize(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = BiasAlignment.Vertical(-0.5f),
) {
Column(
modifier = Modifier
.fillMaxHeight(),
.padding(end = 32.dp),
verticalArrangement = Arrangement.Center,
) {
Text(
text = "Gitnuro",
fontSize = 32.sp,
modifier = Modifier
.padding(bottom = 16.dp),
)
ButtonTile(
title = "Open repository",
modifier = Modifier
.padding(bottom = 8.dp),
title = "Open a repository",
painter = painterResource("open.svg"),
onClick = { openRepositoryDialog(gitManager) }
)
ButtonTile(
title = "Clone repository",
modifier = Modifier
.padding(bottom = 8.dp),
title = "Clone a repository",
painter = painterResource("open.svg"),
onClick = {}
onClick = { }
)
ButtonTile(
modifier = Modifier
.padding(bottom = 8.dp),
title = "Start a local repository",
painter = painterResource("open.svg"),
onClick = { }
)
Text(
text = "About Gitnuro",
fontSize = 18.sp,
modifier = Modifier
.padding(top = 16.dp, bottom = 8.dp),
)
IconTextButton(
title = "Source code",
painter = painterResource("code.svg"),
onClick = {
Desktop.getDesktop().browse(URI("https://github.com/aeab13/Gitnuro"))
}
)
IconTextButton(
title = "Report a bug",
painter = painterResource("bug.svg"),
onClick = {
Desktop.getDesktop().browse(URI("https://github.com/aeab13/Gitnuro/issues"))
}
)
}
Column(
modifier = Modifier
.padding(start = 32.dp),
) {
Text(
text = "Recent",
fontSize = 18.sp,
modifier = Modifier
.padding(top = 16.dp, bottom = 8.dp),
)
LazyColumn {
items(items = appStateManager.latestOpenedRepositoriesPaths) { repo ->
val repoDirName = repo.dirName
val repoDirPath = repo.dirPath
Row(
verticalAlignment = Alignment.CenterVertically,
) {
TextButton(
onClick = {
gitManager.openRepository(repo)
}
) {
Text(
text = repoDirName,
fontSize = 14.sp,
color = MaterialTheme.colors.primary,
)
}
Text(
text = repoDirPath,
fontSize = 14.sp,
modifier = Modifier.padding(start = 4.dp),
color = MaterialTheme.colors.secondaryTextColor
)
}
}
}
}
}
}
@Composable
fun ButtonTile(
modifier: Modifier = Modifier,
title: String,
painter: Painter,
onClick: () -> Unit,
) {
OutlinedButton(
onClick = onClick,
modifier = Modifier.size(width = 200.dp, height = 56.dp)
modifier = modifier.size(width = 280.dp, height = 56.dp)
) {
Row(
modifier = Modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically,
) {
Image(
modifier = Modifier
.size(24.dp)
.padding(end = 8.dp),
.padding(end = 8.dp)
.size(24.dp),
painter = painter,
contentDescription = null,
colorFilter = ColorFilter.tint(MaterialTheme.colors.primary),
@ -63,4 +170,38 @@ fun ButtonTile(
text = title,
)
}
}
}
@Composable
fun IconTextButton(
modifier: Modifier = Modifier,
title: String,
painter: Painter,
onClick: () -> Unit,
) {
TextButton(
onClick = onClick,
modifier = modifier.size(width = 280.dp, height = 40.dp)
) {
Row(
modifier = Modifier
.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically,
) {
Image(
modifier = Modifier
.padding(end = 8.dp)
.size(24.dp),
painter = painter,
contentDescription = null,
colorFilter = ColorFilter.tint(MaterialTheme.colors.primary),
)
Text(
text = title,
)
}
}
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M20 8h-2.81c-.45-.78-1.07-1.45-1.82-1.96L17 4.41 15.59 3l-2.17 2.17C12.96 5.06 12.49 5 12 5c-.49 0-.96.06-1.41.17L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8zm-6 8h-4v-2h4v2zm0-4h-4v-2h4v2z"/></svg>

After

Width:  |  Height:  |  Size: 531 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg>

After

Width:  |  Height:  |  Size: 249 B