diff --git a/build.gradle.kts b/build.gradle.kts index fc09385..c218790 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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() { diff --git a/src/main/kotlin/app/GPreferences.kt b/src/main/kotlin/app/GPreferences.kt index 2c53019..bc754fa 100644 --- a/src/main/kotlin/app/GPreferences.kt +++ b/src/main/kotlin/app/GPreferences.kt @@ -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 diff --git a/src/main/kotlin/app/Main.kt b/src/main/kotlin/app/Main.kt new file mode 100644 index 0000000..2c5fc8b --- /dev/null +++ b/src/main/kotlin/app/Main.kt @@ -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 + + + 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) { + 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 { } +} diff --git a/src/main/kotlin/app/credentials/GProcess.kt b/src/main/kotlin/app/credentials/GProcess.kt index f573ac7..cd0711e 100644 --- a/src/main/kotlin/app/credentials/GProcess.kt +++ b/src/main/kotlin/app/credentials/GProcess.kt @@ -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() diff --git a/src/main/kotlin/app/credentials/GRemoteSession.kt b/src/main/kotlin/app/credentials/GRemoteSession.kt index 84d8490..5f44950 100644 --- a/src/main/kotlin/app/credentials/GRemoteSession.kt +++ b/src/main/kotlin/app/credentials/GRemoteSession.kt @@ -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, +): 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 } diff --git a/src/main/kotlin/app/credentials/GSessionManager.kt b/src/main/kotlin/app/credentials/GSessionManager.kt index 9e73088..66d6b10 100644 --- a/src/main/kotlin/app/credentials/GSessionManager.kt +++ b/src/main/kotlin/app/credentials/GSessionManager.kt @@ -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 +) { 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 } diff --git a/src/main/kotlin/app/di/AppComponent.kt b/src/main/kotlin/app/di/AppComponent.kt index 0c26efd..0f73320 100644 --- a/src/main/kotlin/app/di/AppComponent.kt +++ b/src/main/kotlin/app/di/AppComponent.kt @@ -1,7 +1,9 @@ package app.di +import app.Main import dagger.Component @Component interface AppComponent { + fun inject(main: Main) } \ No newline at end of file diff --git a/src/main/kotlin/app/git/BranchesManager.kt b/src/main/kotlin/app/git/BranchesManager.kt index 94458fb..08e2382 100644 --- a/src/main/kotlin/app/git/BranchesManager.kt +++ b/src/main/kotlin/app/git/BranchesManager.kt @@ -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>(listOf()) val branches: StateFlow> get() = _branches diff --git a/src/main/kotlin/app/git/CloneManager.kt b/src/main/kotlin/app/git/CloneManager.kt index 86e2e05..9fb84f2 100644 --- a/src/main/kotlin/app/git/CloneManager.kt +++ b/src/main/kotlin/app/git/CloneManager.kt @@ -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 diff --git a/src/main/kotlin/app/git/DiffManager.kt b/src/main/kotlin/app/git/DiffManager.kt index 8a79562..c9e65f3 100644 --- a/src/main/kotlin/app/git/DiffManager.kt +++ b/src/main/kotlin/app/git/DiffManager.kt @@ -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 = withContext(Dispatchers.IO) { val diffEntry = diffEntryType.diffEntry val byteArrayOutputStream = ByteArrayOutputStream() diff --git a/src/main/kotlin/app/git/GitManager.kt b/src/main/kotlin/app/git/GitManager.kt index bef2315..bce781f 100644 --- a/src/main/kotlin/app/git/GitManager.kt +++ b/src/main/kotlin/app/git/GitManager.kt @@ -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 diff --git a/src/main/kotlin/app/git/LogManager.kt b/src/main/kotlin/app/git/LogManager.kt index 0da3483..54d3ab5 100644 --- a/src/main/kotlin/app/git/LogManager.kt +++ b/src/main/kotlin/app/git/LogManager.kt @@ -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.Loaded(listOf())) val logStatus: StateFlow diff --git a/src/main/kotlin/app/git/RemoteOperationsManager.kt b/src/main/kotlin/app/git/RemoteOperationsManager.kt index 2ff5fdb..ea355ec 100644 --- a/src/main/kotlin/app/git/RemoteOperationsManager.kt +++ b/src/main/kotlin/app/git/RemoteOperationsManager.kt @@ -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() diff --git a/src/main/kotlin/app/git/StashManager.kt b/src/main/kotlin/app/git/StashManager.kt index 326fc3a..fdc8b1f 100644 --- a/src/main/kotlin/app/git/StashManager.kt +++ b/src/main/kotlin/app/git/StashManager.kt @@ -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.Loaded(listOf())) val stashStatus: StateFlow get() = _stashStatus diff --git a/src/main/kotlin/app/git/StatusManager.kt b/src/main/kotlin/app/git/StatusManager.kt index cf22091..f253aa4 100644 --- a/src/main/kotlin/app/git/StatusManager.kt +++ b/src/main/kotlin/app/git/StatusManager.kt @@ -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.Loaded(listOf(), listOf())) val stageStatus: StateFlow diff --git a/src/main/kotlin/app/main.kt b/src/main/kotlin/app/main.kt index 559edf4..2d80339 100644 --- a/src/main/kotlin/app/main.kt +++ b/src/main/kotlin/app/main.kt @@ -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) { - 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() } \ 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 20b0d1e..8aa30b1 100644 --- a/src/main/kotlin/app/ui/components/RepositoriesTabPanel.kt +++ b/src/main/kotlin/app/ui/components/RepositoriesTabPanel.kt @@ -154,5 +154,5 @@ fun Tab(title: MutableState, selected: Boolean, onClick: () -> Unit, onC class TabInformation( val title: MutableState, val key: Int, - val content: @Composable () -> Unit + val content: @Composable (TabInformation) -> Unit ) \ No newline at end of file