Improved welcome/new tab page
This commit is contained in:
parent
cc2a7e180f
commit
cd592292d1
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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) {
|
||||
_openRepositoriesPaths[key] = path
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -7,4 +7,25 @@ val String.md5: String
|
||||
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
|
||||
}
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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,56 +14,194 @@ 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)
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.padding(end = 8.dp),
|
||||
painter = painter,
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colors.primary),
|
||||
)
|
||||
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,
|
||||
)
|
||||
Text(
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
1
src/main/resources/bug.svg
Normal file
1
src/main/resources/bug.svg
Normal 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 |
1
src/main/resources/code.svg
Normal file
1
src/main/resources/code.svg
Normal 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 |
Loading…
Reference in New Issue
Block a user