Migrated project to use Dagger

This commit is contained in:
Abdelilah El Aissaoui 2021-10-06 20:45:33 +02:00
parent 25990253fa
commit 2b3c12e20d
17 changed files with 214 additions and 148 deletions

View file

@ -25,8 +25,8 @@ dependencies {
implementation(compose.desktop.currentOs)
implementation("org.eclipse.jgit:org.eclipse.jgit:5.13.0.202109080827-r")
implementation("org.apache.sshd:sshd-core:2.7.0")
implementation("com.google.dagger:dagger:2.38.1")
kapt("com.google.dagger:dagger-compiler:2.38.1")
implementation("com.google.dagger:dagger:2.39.1")
kapt("com.google.dagger:dagger-compiler:2.39.1")
}
tasks.withType<KotlinCompile>() {

View file

@ -1,12 +1,13 @@
package app
import java.util.prefs.Preferences
import javax.inject.Inject
private const val PREFERENCES_NAME = "GitnuroConfig"
private const val PREF_LAST_OPENED_REPOSITORY_PATH = "lastOpenedRepositoryPath"
class GPreferences {
class GPreferences @Inject constructor() {
private val preferences: Preferences = Preferences.userRoot().node(PREFERENCES_NAME)
var latestOpenedRepositoryPath: String

155
src/main/kotlin/app/Main.kt Normal file
View file

@ -0,0 +1,155 @@
package app
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.*
import androidx.compose.ui.zIndex
import app.di.DaggerAppComponent
import app.git.GitManager
import app.git.RepositorySelectionStatus
import app.theme.GitnuroTheme
import app.ui.RepositoryOpenPage
import app.ui.WelcomePage
import app.ui.components.RepositoriesTabPanel
import app.ui.components.TabInformation
import javax.inject.Inject
import javax.inject.Provider
class Main {
val appComponent = DaggerAppComponent.create()
@Inject
lateinit var gitManagerProvider: Provider<GitManager>
init {
appComponent.inject(this)
}
fun app() = application {
var isOpen by remember { mutableStateOf(true) }
if (isOpen) {
Window(
title = "Gitnuro",
onCloseRequest = {
isOpen = false
},
state = rememberWindowState(
placement = WindowPlacement.Maximized,
size = WindowSize(1280.dp, 720.dp)
)
) {
GitnuroTheme {
val tabs = remember {
val tabName = mutableStateOf("New tab")
mutableStateOf(
listOf(
TabInformation(tabName, key = 0) {
val gitManager = remember { gitManagerProvider.get() }
Gitnuro(gitManager, false, tabName)
},
)
)
}
var selectedTabKey by remember { mutableStateOf(0) }
Column {
RepositoriesTabPanel(
modifier = Modifier
.padding(top = 4.dp, bottom = 2.dp, start = 4.dp, end = 4.dp)
.fillMaxWidth(),
tabs = tabs.value,
selectedTabKey = selectedTabKey,
onTabSelected = { newSelectedTabKey ->
selectedTabKey = newSelectedTabKey
},
newTabContent = { tabName ->
val gitManager = remember { gitManagerProvider.get() }
Gitnuro(gitManager, true, tabName)
},
onTabsUpdated = { tabInformationList ->
tabs.value = tabInformationList
}
)
LazyColumn(
modifier = Modifier
.fillMaxSize(),
) {
items(items = tabs.value, key = { it.key }) {
val isItemSelected = it.key == selectedTabKey
var tabMod: Modifier = if (!isItemSelected)
Modifier.size(0.dp)
else
Modifier
.fillParentMaxSize()
tabMod = tabMod.background(MaterialTheme.colors.primary)
.alpha(if (isItemSelected) 1f else -1f)
.zIndex(if (isItemSelected) 1f else -1f)
Box(
modifier = tabMod,
) {
it.content(it)
}
}
}
}
}
}
}
}
}
@Composable
fun Gitnuro(gitManager: GitManager, isNewTab: Boolean, tabName: MutableState<String>) {
LaunchedEffect(gitManager) {
if (!isNewTab)
gitManager.loadLatestOpenedRepository()
}
val repositorySelectionStatus by gitManager.repositorySelectionStatus.collectAsState()
if (repositorySelectionStatus is RepositorySelectionStatus.Open) {
tabName.value = gitManager.repositoryName
}
Column(
modifier = Modifier
.background(MaterialTheme.colors.background)
.fillMaxSize()
) {
Crossfade(targetState = repositorySelectionStatus) {
@Suppress("UnnecessaryVariable") // Don't inline it because smart cast won't work
when (repositorySelectionStatus) {
RepositorySelectionStatus.None -> {
WelcomePage(gitManager = gitManager)
}
RepositorySelectionStatus.Loading -> {
LoadingRepository()
}
is RepositorySelectionStatus.Open -> {
RepositoryOpenPage(gitManager = gitManager)
}
}
}
}
}
@Composable
fun LoadingRepository() {
Box { }
}

View file

@ -6,8 +6,9 @@ import java.io.InputStream
import java.io.OutputStream
import java.io.PipedInputStream
import java.io.PipedOutputStream
import javax.inject.Inject
class GProcess : Process() {
class GProcess @Inject constructor() : Process() {
private lateinit var channel: ChannelExec
private val outputStream = PipedOutputStream()
private val inputStream = PipedInputStream()

View file

@ -4,10 +4,14 @@ import org.apache.sshd.client.SshClient
import org.apache.sshd.client.future.ConnectFuture
import org.eclipse.jgit.transport.RemoteSession
import org.eclipse.jgit.transport.URIish
import javax.inject.Inject
import javax.inject.Provider
private const val DEFAULT_SSH_PORT = 22
class GRemoteSession : RemoteSession {
class GRemoteSession @Inject constructor(
private val processProvider: Provider<GProcess>,
): RemoteSession {
private val client = SshClient.setUpDefaultClient()
private var connectFuture: ConnectFuture? = null
@ -18,7 +22,7 @@ class GRemoteSession : RemoteSession {
val session = connectFuture.clientSession
session.auth().verify()
val process = GProcess()
val process = processProvider.get()
process.setup(session, commandName)
return process
}

View file

@ -5,8 +5,12 @@ import org.eclipse.jgit.transport.RemoteSession
import org.eclipse.jgit.transport.SshSessionFactory
import org.eclipse.jgit.transport.URIish
import org.eclipse.jgit.util.FS
import javax.inject.Inject
import javax.inject.Provider
class GSessionManager {
class GSessionManager @Inject constructor(
private val sessionProvider: Provider<GRemoteSession>
) {
fun generateSshSessionFactory(): SshSessionFactory {
return object : SshSessionFactory() {
override fun getSession(
@ -15,8 +19,9 @@ class GSessionManager {
fs: FS?,
tms: Int
): RemoteSession {
val remoteSession = GRemoteSession()
val remoteSession = sessionProvider.get()
remoteSession.setup(uri)
return remoteSession
}

View file

@ -1,7 +1,9 @@
package app.di
import app.Main
import dagger.Component
@Component
interface AppComponent {
fun inject(main: Main)
}

View file

@ -6,8 +6,9 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.Ref
import javax.inject.Inject
class BranchesManager {
class BranchesManager @Inject constructor() {
private val _branches = MutableStateFlow<List<Ref>>(listOf())
val branches: StateFlow<List<Ref>>
get() = _branches

View file

@ -5,13 +5,13 @@ import org.eclipse.jgit.api.Git
import org.eclipse.jgit.transport.*
import java.io.File
import java.io.IOException
import javax.inject.Inject
private const val REMOTE_URL = ""
class CloneManager {
private val sessionManager = GSessionManager()
class CloneManager @Inject constructor(
private val sessionManager: GSessionManager,
) {
fun cloneTest() {
// prepare a new folder for the cloned repository

View file

@ -14,8 +14,9 @@ import org.eclipse.jgit.treewalk.AbstractTreeIterator
import org.eclipse.jgit.treewalk.CanonicalTreeParser
import org.eclipse.jgit.treewalk.FileTreeIterator
import java.io.ByteArrayOutputStream
import javax.inject.Inject
class DiffManager {
class DiffManager @Inject constructor() {
suspend fun diffFormat(git: Git, diffEntryType: DiffEntryType): List<String> = withContext(Dispatchers.IO) {
val diffEntry = diffEntryType.diffEntry
val byteArrayOutputStream = ByteArrayOutputStream()

View file

@ -13,18 +13,21 @@ import org.eclipse.jgit.revwalk.RevCommit
import org.eclipse.jgit.storage.file.FileRepositoryBuilder
import app.GPreferences
import java.io.File
import javax.inject.Inject
class GitManager {
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,
) {
val repositoryName: String
get() = safeGit.repository.directory.parentFile.name
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 credentialsStateManager = CredentialsStateManager
private val managerScope = CoroutineScope(SupervisorJob())
@ -75,7 +78,7 @@ class GitManager {
}
fun loadLatestOpenedRepository() {
suspend fun loadLatestOpenedRepository() = withContext(Dispatchers.IO) {
val latestOpenedRepositoryPath = preferences.latestOpenedRepositoryPath
if (latestOpenedRepositoryPath.isNotEmpty()) {
openRepository(File(latestOpenedRepositoryPath))
@ -83,10 +86,10 @@ class GitManager {
}
fun openRepository(directory: File) {
val gitDirectory = if (directory.name == ".app.git") {
val gitDirectory = if (directory.name == ".git") {
directory
} else {
val gitDir = File(directory, ".app.git")
val gitDir = File(directory, ".git")
if (gitDir.exists() && gitDir.isDirectory) {
gitDir
} else

View file

@ -7,8 +7,9 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.revwalk.RevCommit
import javax.inject.Inject
class LogManager {
class LogManager @Inject constructor() {
private val _logStatus = MutableStateFlow<LogStatus>(LogStatus.Loaded(listOf()))
val logStatus: StateFlow<LogStatus>

View file

@ -5,10 +5,11 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.transport.*
import javax.inject.Inject
class RemoteOperationsManager {
private val sessionManager = GSessionManager()
class RemoteOperationsManager @Inject constructor(
private val sessionManager: GSessionManager
) {
suspend fun pull(git: Git) = withContext(Dispatchers.IO) {
git
.pull()

View file

@ -6,8 +6,9 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.revwalk.RevCommit
import javax.inject.Inject
class StashManager {
class StashManager @Inject constructor() {
private val _stashStatus = MutableStateFlow<StashStatus>(StashStatus.Loaded(listOf()))
val stashStatus: StateFlow<StashStatus>
get() = _stashStatus

View file

@ -9,8 +9,9 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.diff.DiffEntry
import javax.inject.Inject
class StatusManager {
class StatusManager @Inject constructor() {
private val _stageStatus = MutableStateFlow<StageStatus>(StageStatus.Loaded(listOf(), listOf()))
val stageStatus: StateFlow<StageStatus>

View file

@ -11,6 +11,8 @@ import androidx.compose.ui.draw.alpha
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.*
import androidx.compose.ui.zIndex
import app.Main
import app.di.DaggerAppComponent
import app.git.GitManager
import app.git.RepositorySelectionStatus
import app.theme.*
@ -20,120 +22,7 @@ import app.ui.components.RepositoriesTabPanel
import app.ui.components.TabInformation
@OptIn(ExperimentalComposeUiApi::class)
fun main() = application {
var isOpen by remember { mutableStateOf(true) }
if (isOpen) {
Window(
title = "Gitnuro",
onCloseRequest = {
isOpen = false
},
state = rememberWindowState(placement = WindowPlacement.Maximized, size = WindowSize(1280.dp, 720.dp))
) {
GitnuroTheme {
val tabs = remember {
val tabName = mutableStateOf("New tab")
mutableStateOf(
listOf(
TabInformation(tabName, key = 0) {
Gitnuro(false, tabName)
},
)
)
}
var selectedTabKey by remember { mutableStateOf(0) }
Column {
RepositoriesTabPanel(
modifier = Modifier
.padding(top = 4.dp, bottom = 2.dp, start = 4.dp, end = 4.dp)
.fillMaxWidth(),
tabs = tabs.value,
selectedTabKey = selectedTabKey,
onTabSelected = { newSelectedTabKey ->
selectedTabKey = newSelectedTabKey
},
newTabContent = { tabName ->
Gitnuro(true, tabName)
},
onTabsUpdated = { tabInformationList ->
tabs.value = tabInformationList
}
)
LazyColumn(
modifier = Modifier
.fillMaxSize(),
) {
items(items = tabs.value, key = { it.key }) {
val isItemSelected = it.key == selectedTabKey
var tabMod: Modifier = if (!isItemSelected)
Modifier.size(0.dp)
else
Modifier
.fillParentMaxSize()
tabMod = tabMod.background(MaterialTheme.colors.primary)
.alpha(if (isItemSelected) 1f else -1f)
.zIndex(if (isItemSelected) 1f else -1f)
Box(
modifier = tabMod,
) {
it.content()
}
}
}
}
}
}
}
}
@Composable
fun Gitnuro(isNewTab: Boolean, tabName: MutableState<String>) {
val gitManager = remember {
GitManager().apply {
if (!isNewTab)
loadLatestOpenedRepository()
}
}
val repositorySelectionStatus by gitManager.repositorySelectionStatus.collectAsState()
if (repositorySelectionStatus is RepositorySelectionStatus.Open) {
tabName.value = gitManager.repositoryName
}
Column(
modifier = Modifier
.background(MaterialTheme.colors.background)
.fillMaxSize()
) {
Crossfade(targetState = repositorySelectionStatus) {
@Suppress("UnnecessaryVariable") // Don't inline it because smart cast won't work
when (repositorySelectionStatus) {
RepositorySelectionStatus.None -> {
WelcomePage(gitManager = gitManager)
}
RepositorySelectionStatus.Loading -> {
LoadingRepository()
}
is RepositorySelectionStatus.Open -> {
RepositoryOpenPage(gitManager = gitManager)
}
}
}
}
}
@Composable
fun LoadingRepository() {
Box { }
fun main() {
val main = Main()
main.app()
}

View file

@ -154,5 +154,5 @@ fun Tab(title: MutableState<String>, selected: Boolean, onClick: () -> Unit, onC
class TabInformation(
val title: MutableState<String>,
val key: Int,
val content: @Composable () -> Unit
val content: @Composable (TabInformation) -> Unit
)