Migrated project to use Dagger
This commit is contained in:
parent
25990253fa
commit
2b3c12e20d
17 changed files with 214 additions and 148 deletions
|
@ -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>() {
|
||||
|
|
|
@ -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
155
src/main/kotlin/app/Main.kt
Normal 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 { }
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package app.di
|
||||
|
||||
import app.Main
|
||||
import dagger.Component
|
||||
|
||||
@Component
|
||||
interface AppComponent {
|
||||
fun inject(main: Main)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
)
|
Loading…
Reference in a new issue