Creation of multiple viewmodels that hold data state
This commit is contained in:
parent
997a651faf
commit
9c53ce726e
@ -44,7 +44,7 @@ tasks.withType<KotlinCompile>() {
|
|||||||
compose.desktop {
|
compose.desktop {
|
||||||
application {
|
application {
|
||||||
mainClass = "MainKt"
|
mainClass = "MainKt"
|
||||||
|
//
|
||||||
nativeDistributions {
|
nativeDistributions {
|
||||||
includeAllModules = true
|
includeAllModules = true
|
||||||
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb, TargetFormat.AppImage)
|
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb, TargetFormat.AppImage)
|
||||||
|
@ -24,21 +24,15 @@ import androidx.compose.ui.window.application
|
|||||||
import androidx.compose.ui.window.rememberWindowState
|
import androidx.compose.ui.window.rememberWindowState
|
||||||
import androidx.compose.ui.zIndex
|
import androidx.compose.ui.zIndex
|
||||||
import app.di.DaggerAppComponent
|
import app.di.DaggerAppComponent
|
||||||
import app.git.GitManager
|
|
||||||
import app.theme.AppTheme
|
import app.theme.AppTheme
|
||||||
import app.ui.AppTab
|
|
||||||
import app.ui.components.RepositoriesTabPanel
|
import app.ui.components.RepositoriesTabPanel
|
||||||
import app.ui.components.TabInformation
|
import app.ui.components.TabInformation
|
||||||
import app.ui.dialogs.SettingsDialog
|
import app.ui.dialogs.SettingsDialog
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Provider
|
|
||||||
|
|
||||||
class Main {
|
class Main {
|
||||||
private val appComponent = DaggerAppComponent.create()
|
private val appComponent = DaggerAppComponent.create()
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var gitManagerProvider: Provider<GitManager>
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var appStateManager: AppStateManager
|
lateinit var appStateManager: AppStateManager
|
||||||
|
|
||||||
@ -47,7 +41,7 @@ class Main {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
appComponent.inject(this)
|
appComponent.inject(this)
|
||||||
|
println("AppStateManagerReference $appStateManager")
|
||||||
appStateManager.loadRepositoriesTabs()
|
appStateManager.loadRepositoriesTabs()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,19 +194,11 @@ class Main {
|
|||||||
): TabInformation {
|
): TabInformation {
|
||||||
|
|
||||||
return TabInformation(
|
return TabInformation(
|
||||||
title = tabName,
|
tabName = tabName,
|
||||||
key = key
|
key = key,
|
||||||
) {
|
path = path,
|
||||||
val gitManager = remember { gitManagerProvider.get() }
|
appComponent = appComponent,
|
||||||
gitManager.onRepositoryChanged = { path ->
|
)
|
||||||
if (path == null) {
|
|
||||||
appStateManager.repositoryTabRemoved(key)
|
|
||||||
} else
|
|
||||||
appStateManager.repositoryTabChanged(key, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
AppTab(gitManager, path, tabName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package app.di
|
package app.di
|
||||||
|
|
||||||
|
import app.AppStateManager
|
||||||
import app.Main
|
import app.Main
|
||||||
import dagger.Component
|
import dagger.Component
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -8,4 +9,5 @@ import javax.inject.Singleton
|
|||||||
@Component
|
@Component
|
||||||
interface AppComponent {
|
interface AppComponent {
|
||||||
fun inject(main: Main)
|
fun inject(main: Main)
|
||||||
|
fun appStateManager(): AppStateManager
|
||||||
}
|
}
|
10
src/main/kotlin/app/di/TabComponent.kt
Normal file
10
src/main/kotlin/app/di/TabComponent.kt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package app.di
|
||||||
|
|
||||||
|
import app.ui.components.TabInformation
|
||||||
|
import dagger.Component
|
||||||
|
|
||||||
|
@TabScope
|
||||||
|
@Component(dependencies = [ AppComponent::class ])
|
||||||
|
interface TabComponent {
|
||||||
|
fun inject(tabInformation: TabInformation)
|
||||||
|
}
|
7
src/main/kotlin/app/di/TabScope.kt
Normal file
7
src/main/kotlin/app/di/TabScope.kt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package app.di
|
||||||
|
|
||||||
|
import javax.inject.Scope
|
||||||
|
|
||||||
|
@Scope
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class TabScope
|
@ -1,9 +1,12 @@
|
|||||||
package app.git
|
package app.git
|
||||||
|
|
||||||
|
import app.extensions.isBranch
|
||||||
|
import app.extensions.simpleName
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.eclipse.jgit.api.CreateBranchCommand
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.api.ListBranchCommand
|
import org.eclipse.jgit.api.ListBranchCommand
|
||||||
import org.eclipse.jgit.api.MergeCommand
|
import org.eclipse.jgit.api.MergeCommand
|
||||||
@ -32,17 +35,6 @@ class BranchesManager @Inject constructor() {
|
|||||||
return branchList.firstOrNull { it.name == branchName }
|
return branchList.firstOrNull { it.name == branchName }
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun loadBranches(git: Git) = withContext(Dispatchers.IO) {
|
|
||||||
val branchList = getBranches(git)
|
|
||||||
|
|
||||||
val branchName = git
|
|
||||||
.repository
|
|
||||||
.fullBranch
|
|
||||||
|
|
||||||
_branches.value = branchList
|
|
||||||
_currentBranch.value = branchName
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getBranches(git: Git) = withContext(Dispatchers.IO) {
|
suspend fun getBranches(git: Git) = withContext(Dispatchers.IO) {
|
||||||
return@withContext git
|
return@withContext git
|
||||||
.branchList()
|
.branchList()
|
||||||
@ -55,8 +47,6 @@ class BranchesManager @Inject constructor() {
|
|||||||
.setCreateBranch(true)
|
.setCreateBranch(true)
|
||||||
.setName(branchName)
|
.setName(branchName)
|
||||||
.call()
|
.call()
|
||||||
|
|
||||||
loadBranches(git)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun createBranchOnCommit(git: Git, branch: String, revCommit: RevCommit) = withContext(Dispatchers.IO) {
|
suspend fun createBranchOnCommit(git: Git, branch: String, revCommit: RevCommit) = withContext(Dispatchers.IO) {
|
||||||
@ -95,4 +85,17 @@ class BranchesManager @Inject constructor() {
|
|||||||
.setListMode(ListBranchCommand.ListMode.REMOTE)
|
.setListMode(ListBranchCommand.ListMode.REMOTE)
|
||||||
.call()
|
.call()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun checkoutRef(git: Git, ref: Ref) = withContext(Dispatchers.IO) {
|
||||||
|
git.checkout().apply {
|
||||||
|
setName(ref.name)
|
||||||
|
if (ref.isBranch && ref.name.startsWith("refs/remotes/")) {
|
||||||
|
setCreateBranch(true)
|
||||||
|
setName(ref.simpleName)
|
||||||
|
setStartPoint(ref.objectId.name)
|
||||||
|
setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK)
|
||||||
|
}
|
||||||
|
call()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -21,17 +21,8 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
class LogManager @Inject constructor(
|
class LogManager @Inject constructor(
|
||||||
private val statusManager: StatusManager,
|
private val statusManager: StatusManager,
|
||||||
private val branchesManager: BranchesManager,
|
|
||||||
) {
|
) {
|
||||||
private val _logStatus = MutableStateFlow<LogStatus>(LogStatus.Loading)
|
suspend fun loadLog(git: Git, currentBranch: Ref?) = withContext(Dispatchers.IO) {
|
||||||
|
|
||||||
val logStatus: StateFlow<LogStatus>
|
|
||||||
get() = _logStatus
|
|
||||||
|
|
||||||
suspend fun loadLog(git: Git) = withContext(Dispatchers.IO) {
|
|
||||||
_logStatus.value = LogStatus.Loading
|
|
||||||
|
|
||||||
val currentBranch = branchesManager.currentBranchRef(git)
|
|
||||||
val commitList = GraphCommitList()
|
val commitList = GraphCommitList()
|
||||||
val repositoryState = git.repository.repositoryState
|
val repositoryState = git.repository.repositoryState
|
||||||
|
|
||||||
@ -45,7 +36,7 @@ class LogManager @Inject constructor(
|
|||||||
walk.markStartAllRefs(Constants.R_REMOTES)
|
walk.markStartAllRefs(Constants.R_REMOTES)
|
||||||
walk.markStartAllRefs(Constants.R_TAGS)
|
walk.markStartAllRefs(Constants.R_TAGS)
|
||||||
|
|
||||||
if (statusManager.checkHasUncommitedChanges(git))
|
if (statusManager.hasUncommitedChanges(git))
|
||||||
commitList.addUncommitedChangesGraphCommit(logList.first())
|
commitList.addUncommitedChangesGraphCommit(logList.first())
|
||||||
|
|
||||||
commitList.source(walk)
|
commitList.source(walk)
|
||||||
@ -55,9 +46,8 @@ class LogManager @Inject constructor(
|
|||||||
ensureActive()
|
ensureActive()
|
||||||
|
|
||||||
}
|
}
|
||||||
val loadedStatus = LogStatus.Loaded(commitList, currentBranch)
|
|
||||||
|
|
||||||
_logStatus.value = loadedStatus
|
return@withContext commitList
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun checkoutCommit(git: Git, revCommit: RevCommit) = withContext(Dispatchers.IO) {
|
suspend fun checkoutCommit(git: Git, revCommit: RevCommit) = withContext(Dispatchers.IO) {
|
||||||
@ -67,19 +57,6 @@ class LogManager @Inject constructor(
|
|||||||
.call()
|
.call()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun checkoutRef(git: Git, ref: Ref) = withContext(Dispatchers.IO) {
|
|
||||||
git.checkout().apply {
|
|
||||||
setName(ref.name)
|
|
||||||
if (ref.isBranch && ref.name.startsWith("refs/remotes/")) {
|
|
||||||
setCreateBranch(true)
|
|
||||||
setName(ref.simpleName)
|
|
||||||
setStartPoint(ref.objectId.name)
|
|
||||||
setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK)
|
|
||||||
}
|
|
||||||
call()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun revertCommit(git: Git, revCommit: RevCommit) = withContext(Dispatchers.IO) {
|
suspend fun revertCommit(git: Git, revCommit: RevCommit) = withContext(Dispatchers.IO) {
|
||||||
git
|
git
|
||||||
.revert()
|
.revert()
|
||||||
@ -107,8 +84,3 @@ enum class ResetType {
|
|||||||
MIXED,
|
MIXED,
|
||||||
HARD,
|
HARD,
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class LogStatus {
|
|
||||||
object Loading : LogStatus()
|
|
||||||
class Loaded(val plotCommitList: GraphCommitList, val currentBranch: Ref?) : LogStatus()
|
|
||||||
}
|
|
@ -10,22 +10,18 @@ import org.eclipse.jgit.transport.RemoteConfig
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class RemotesManager @Inject constructor() {
|
class RemotesManager @Inject constructor() {
|
||||||
private val _remotes = MutableStateFlow<List<RemoteInfo>>(listOf())
|
|
||||||
val remotes: StateFlow<List<RemoteInfo>>
|
|
||||||
get() = _remotes
|
|
||||||
|
|
||||||
suspend fun loadRemotes(git: Git, allRemoteBranches: List<Ref>) = withContext(Dispatchers.IO) {
|
suspend fun loadRemotes(git: Git, allRemoteBranches: List<Ref>) = withContext(Dispatchers.IO) {
|
||||||
val remotes = git.remoteList()
|
val remotes = git.remoteList()
|
||||||
.call()
|
.call()
|
||||||
|
|
||||||
val remoteInfoList = remotes.map { remoteConfig ->
|
return@withContext remotes.map { remoteConfig ->
|
||||||
val remoteBranches = allRemoteBranches.filter { branch ->
|
val remoteBranches = allRemoteBranches.filter { branch ->
|
||||||
branch.name.startsWith("refs/remotes/${remoteConfig.name}")
|
branch.name.startsWith("refs/remotes/${remoteConfig.name}")
|
||||||
}
|
}
|
||||||
RemoteInfo(remoteConfig, remoteBranches)
|
RemoteInfo(remoteConfig, remoteBranches)
|
||||||
}
|
}
|
||||||
|
|
||||||
_remotes.value = remoteInfoList
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
src/main/kotlin/app/git/RepositoryManager.kt
Normal file
12
src/main/kotlin/app/git/RepositoryManager.kt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package app.git
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class RepositoryManager @Inject constructor() {
|
||||||
|
suspend fun getRepositoryState(git: Git) = withContext(Dispatchers.IO) {
|
||||||
|
return@withContext git.repository.repositoryState
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,6 @@ import app.git.diff.Hunk
|
|||||||
import app.git.diff.LineType
|
import app.git.diff.LineType
|
||||||
import app.theme.conflictFile
|
import app.theme.conflictFile
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.ensureActive
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -31,24 +30,9 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
|
|
||||||
class StatusManager @Inject constructor(
|
class StatusManager @Inject constructor(
|
||||||
private val branchesManager: BranchesManager,
|
|
||||||
private val rawFileManagerFactory: RawFileManagerFactory,
|
private val rawFileManagerFactory: RawFileManagerFactory,
|
||||||
) {
|
) {
|
||||||
private val _stageStatus = MutableStateFlow<StageStatus>(StageStatus.Loaded(listOf(), listOf()))
|
suspend fun hasUncommitedChanges(git: Git) = withContext(Dispatchers.IO) {
|
||||||
val stageStatus: StateFlow<StageStatus> = _stageStatus
|
|
||||||
|
|
||||||
private val _repositoryState = MutableStateFlow(RepositoryState.SAFE)
|
|
||||||
val repositoryState: StateFlow<RepositoryState> = _repositoryState
|
|
||||||
|
|
||||||
private val _hasUncommitedChanges = MutableStateFlow<Boolean>(false)
|
|
||||||
val hasUncommitedChanges: StateFlow<Boolean>
|
|
||||||
get() = _hasUncommitedChanges
|
|
||||||
|
|
||||||
suspend fun loadHasUncommitedChanges(git: Git) = withContext(Dispatchers.IO) {
|
|
||||||
_hasUncommitedChanges.value = checkHasUncommitedChanges(git)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun checkHasUncommitedChanges(git: Git) = withContext(Dispatchers.IO) {
|
|
||||||
val status = git
|
val status = git
|
||||||
.status()
|
.status()
|
||||||
.call()
|
.call()
|
||||||
@ -56,77 +40,6 @@ class StatusManager @Inject constructor(
|
|||||||
return@withContext status.hasUncommittedChanges() || status.hasUntrackedChanges()
|
return@withContext status.hasUncommittedChanges() || status.hasUntrackedChanges()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun loadRepositoryStatus(git: Git) = withContext(Dispatchers.IO) {
|
|
||||||
_repositoryState.value = git.repository.repositoryState
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun loadStatus(git: Git) = withContext(Dispatchers.IO) {
|
|
||||||
val previousStatus = _stageStatus.value
|
|
||||||
_stageStatus.value = StageStatus.Loading
|
|
||||||
|
|
||||||
try {
|
|
||||||
loadRepositoryStatus(git)
|
|
||||||
|
|
||||||
loadHasUncommitedChanges(git)
|
|
||||||
val currentBranch = branchesManager.currentBranchRef(git)
|
|
||||||
val repositoryState = git.repository.repositoryState
|
|
||||||
|
|
||||||
val staged = git
|
|
||||||
.diff()
|
|
||||||
.setShowNameAndStatusOnly(true).apply {
|
|
||||||
if (currentBranch == null && !repositoryState.isMerging && !repositoryState.isRebasing)
|
|
||||||
setOldTree(EmptyTreeIterator()) // Required if the repository is empty
|
|
||||||
|
|
||||||
setCached(true)
|
|
||||||
}
|
|
||||||
.call()
|
|
||||||
// TODO: Grouping and fitlering allows us to remove duplicates when conflicts appear, requires more testing (what happens in windows? /dev/null is a unix thing)
|
|
||||||
// TODO: Test if we should group by old path or new path
|
|
||||||
.groupBy {
|
|
||||||
if(it.newPath != "/dev/null")
|
|
||||||
it.newPath
|
|
||||||
else
|
|
||||||
it.oldPath
|
|
||||||
}
|
|
||||||
.map {
|
|
||||||
val entries = it.value
|
|
||||||
|
|
||||||
val hasConflicts =
|
|
||||||
(entries.count() > 1 && (repositoryState.isMerging || repositoryState.isRebasing))
|
|
||||||
|
|
||||||
StatusEntry(entries.first(), isConflict = hasConflicts)
|
|
||||||
}
|
|
||||||
|
|
||||||
ensureActive()
|
|
||||||
|
|
||||||
val unstaged = git
|
|
||||||
.diff()
|
|
||||||
.setShowNameAndStatusOnly(true)
|
|
||||||
.call()
|
|
||||||
.groupBy {
|
|
||||||
if(it.oldPath != "/dev/null")
|
|
||||||
it.oldPath
|
|
||||||
else
|
|
||||||
it.newPath
|
|
||||||
}
|
|
||||||
.map {
|
|
||||||
val entries = it.value
|
|
||||||
|
|
||||||
val hasConflicts =
|
|
||||||
(entries.count() > 1 && (repositoryState.isMerging || repositoryState.isRebasing))
|
|
||||||
|
|
||||||
StatusEntry(entries.first(), isConflict = hasConflicts)
|
|
||||||
}
|
|
||||||
|
|
||||||
ensureActive()
|
|
||||||
_stageStatus.value = StageStatus.Loaded(staged, unstaged)
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
_stageStatus.value = previousStatus
|
|
||||||
throw ex
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun stage(git: Git, diffEntry: DiffEntry) = withContext(Dispatchers.IO) {
|
suspend fun stage(git: Git, diffEntry: DiffEntry) = withContext(Dispatchers.IO) {
|
||||||
if (diffEntry.changeType == DiffEntry.ChangeType.DELETE) {
|
if (diffEntry.changeType == DiffEntry.ChangeType.DELETE) {
|
||||||
git.rm()
|
git.rm()
|
||||||
@ -137,8 +50,6 @@ class StatusManager @Inject constructor(
|
|||||||
.addFilepattern(diffEntry.filePath)
|
.addFilepattern(diffEntry.filePath)
|
||||||
.call()
|
.call()
|
||||||
}
|
}
|
||||||
|
|
||||||
loadStatus(git)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun stageHunk(git: Git, diffEntry: DiffEntry, hunk: Hunk) = withContext(Dispatchers.IO) {
|
suspend fun stageHunk(git: Git, diffEntry: DiffEntry, hunk: Hunk) = withContext(Dispatchers.IO) {
|
||||||
@ -175,7 +86,7 @@ class StatusManager @Inject constructor(
|
|||||||
|
|
||||||
completedWithErrors = false
|
completedWithErrors = false
|
||||||
|
|
||||||
loadStatus(git)
|
// loadStatus(git)
|
||||||
} finally {
|
} finally {
|
||||||
if (completedWithErrors)
|
if (completedWithErrors)
|
||||||
dirCache.unlock()
|
dirCache.unlock()
|
||||||
@ -226,7 +137,7 @@ class StatusManager @Inject constructor(
|
|||||||
|
|
||||||
completedWithErrors = false
|
completedWithErrors = false
|
||||||
|
|
||||||
loadStatus(git)
|
// loadStatus(git)
|
||||||
} finally {
|
} finally {
|
||||||
if (completedWithErrors)
|
if (completedWithErrors)
|
||||||
dirCache.unlock()
|
dirCache.unlock()
|
||||||
@ -271,8 +182,6 @@ class StatusManager @Inject constructor(
|
|||||||
git.reset()
|
git.reset()
|
||||||
.addPath(diffEntry.filePath)
|
.addPath(diffEntry.filePath)
|
||||||
.call()
|
.call()
|
||||||
|
|
||||||
loadStatus(git)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun commit(git: Git, message: String) = withContext(Dispatchers.IO) {
|
suspend fun commit(git: Git, message: String) = withContext(Dispatchers.IO) {
|
||||||
@ -280,8 +189,6 @@ class StatusManager @Inject constructor(
|
|||||||
.setMessage(message)
|
.setMessage(message)
|
||||||
.setAllowEmpty(false)
|
.setAllowEmpty(false)
|
||||||
.call()
|
.call()
|
||||||
|
|
||||||
loadStatus(git)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun reset(git: Git, diffEntry: DiffEntry, staged: Boolean) = withContext(Dispatchers.IO) {
|
suspend fun reset(git: Git, diffEntry: DiffEntry, staged: Boolean) = withContext(Dispatchers.IO) {
|
||||||
@ -297,15 +204,13 @@ class StatusManager @Inject constructor(
|
|||||||
.addPath(diffEntry.filePath)
|
.addPath(diffEntry.filePath)
|
||||||
.call()
|
.call()
|
||||||
|
|
||||||
loadStatus(git)
|
// loadStatus(git)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun unstageAll(git: Git) = withContext(Dispatchers.IO) {
|
suspend fun unstageAll(git: Git) = withContext(Dispatchers.IO) {
|
||||||
git
|
git
|
||||||
.reset()
|
.reset()
|
||||||
.call()
|
.call()
|
||||||
|
|
||||||
loadStatus(git)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun stageAll(git: Git) = withContext(Dispatchers.IO) {
|
suspend fun stageAll(git: Git) = withContext(Dispatchers.IO) {
|
||||||
@ -313,15 +218,58 @@ class StatusManager @Inject constructor(
|
|||||||
.add()
|
.add()
|
||||||
.addFilepattern(".")
|
.addFilepattern(".")
|
||||||
.call()
|
.call()
|
||||||
|
}
|
||||||
|
|
||||||
loadStatus(git)
|
suspend fun getStaged(git: Git, currentBranch: Ref?, repositoryState: RepositoryState) = withContext(Dispatchers.IO) {
|
||||||
|
return@withContext git
|
||||||
|
.diff()
|
||||||
|
.setShowNameAndStatusOnly(true).apply {
|
||||||
|
if (currentBranch == null && !repositoryState.isMerging && !repositoryState.isRebasing)
|
||||||
|
setOldTree(EmptyTreeIterator()) // Required if the repository is empty
|
||||||
|
|
||||||
|
setCached(true)
|
||||||
|
}
|
||||||
|
.call()
|
||||||
|
// TODO: Grouping and fitlering allows us to remove duplicates when conflicts appear, requires more testing (what happens in windows? /dev/null is a unix thing)
|
||||||
|
// TODO: Test if we should group by old path or new path
|
||||||
|
.groupBy {
|
||||||
|
if(it.newPath != "/dev/null")
|
||||||
|
it.newPath
|
||||||
|
else
|
||||||
|
it.oldPath
|
||||||
|
}
|
||||||
|
.map {
|
||||||
|
val entries = it.value
|
||||||
|
|
||||||
|
val hasConflicts =
|
||||||
|
(entries.count() > 1 && (repositoryState.isMerging || repositoryState.isRebasing))
|
||||||
|
|
||||||
|
StatusEntry(entries.first(), isConflict = hasConflicts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class StageStatus {
|
suspend fun getUnstaged(git: Git, repositoryState: RepositoryState) = withContext(Dispatchers.IO) {
|
||||||
object Loading : StageStatus()
|
return@withContext git
|
||||||
data class Loaded(val staged: List<StatusEntry>, val unstaged: List<StatusEntry>) : StageStatus()
|
.diff()
|
||||||
|
.setShowNameAndStatusOnly(true)
|
||||||
|
.call()
|
||||||
|
.groupBy {
|
||||||
|
if(it.oldPath != "/dev/null")
|
||||||
|
it.oldPath
|
||||||
|
else
|
||||||
|
it.newPath
|
||||||
}
|
}
|
||||||
|
.map {
|
||||||
|
val entries = it.value
|
||||||
|
|
||||||
|
val hasConflicts =
|
||||||
|
(entries.count() > 1 && (repositoryState.isMerging || repositoryState.isRebasing))
|
||||||
|
|
||||||
|
StatusEntry(entries.first(), isConflict = hasConflicts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
data class StatusEntry(val diffEntry: DiffEntry, val isConflict: Boolean) {
|
data class StatusEntry(val diffEntry: DiffEntry, val isConflict: Boolean) {
|
||||||
val icon: ImageVector
|
val icon: ImageVector
|
||||||
|
100
src/main/kotlin/app/git/TabState.kt
Normal file
100
src/main/kotlin/app/git/TabState.kt
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package app.git
|
||||||
|
|
||||||
|
import app.app.Error
|
||||||
|
import app.app.newErrorNow
|
||||||
|
import app.di.TabScope
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
|
|
||||||
|
@TabScope
|
||||||
|
class TabState @Inject constructor() {
|
||||||
|
var git: Git? = null
|
||||||
|
val safeGit: Git
|
||||||
|
get() {
|
||||||
|
val git = this.git
|
||||||
|
if (git == null) {
|
||||||
|
// _repositorySelectionStatus.value = RepositorySelectionStatus.None
|
||||||
|
throw CancellationException("Null git object")
|
||||||
|
} else
|
||||||
|
return git
|
||||||
|
}
|
||||||
|
|
||||||
|
val mutex = Mutex()
|
||||||
|
|
||||||
|
private val _refreshData = MutableSharedFlow<RefreshType>()
|
||||||
|
val refreshData: Flow<RefreshType> = _refreshData
|
||||||
|
suspend fun refreshData(refreshType: RefreshType) = _refreshData.emit(refreshType)
|
||||||
|
|
||||||
|
private val _errors = MutableSharedFlow<Error>()
|
||||||
|
val errors: Flow<Error> = _errors
|
||||||
|
val managerScope = CoroutineScope(SupervisorJob())
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property that indicates if a git operation is running
|
||||||
|
*/
|
||||||
|
@set:Synchronized
|
||||||
|
var operationRunning = false
|
||||||
|
|
||||||
|
|
||||||
|
private val _processing = MutableStateFlow(false)
|
||||||
|
val processing: StateFlow<Boolean>
|
||||||
|
get() = _processing
|
||||||
|
|
||||||
|
fun safeProcessing(showError: Boolean = true, callback: suspend (git: Git) -> RefreshType) =
|
||||||
|
managerScope.launch {
|
||||||
|
mutex.withLock {
|
||||||
|
_processing.value = true
|
||||||
|
operationRunning = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
val refreshType = callback(safeGit)
|
||||||
|
|
||||||
|
if (refreshType != RefreshType.NONE)
|
||||||
|
_refreshData.emit(refreshType)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
ex.printStackTrace()
|
||||||
|
|
||||||
|
if (showError)
|
||||||
|
_errors.emit(newErrorNow(ex, ex.localizedMessage))
|
||||||
|
} finally {
|
||||||
|
_processing.value = false
|
||||||
|
operationRunning = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runOperation(block: suspend (git: Git) -> RefreshType) = managerScope.launch {
|
||||||
|
operationRunning = true
|
||||||
|
try {
|
||||||
|
val refreshType = block(safeGit)
|
||||||
|
|
||||||
|
if (refreshType != RefreshType.NONE)
|
||||||
|
_refreshData.emit(refreshType)
|
||||||
|
} finally {
|
||||||
|
operationRunning = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class RefreshType {
|
||||||
|
NONE,
|
||||||
|
ALL_DATA,
|
||||||
|
ONLY_LOG,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requires to update the status if currently selected and update the log if there has been a change
|
||||||
|
* in the "uncommited changes" state (if there were changes before but not anymore and vice-versa)
|
||||||
|
*/
|
||||||
|
UNCOMMITED_CHANGES,
|
||||||
|
}
|
@ -10,16 +10,8 @@ import org.eclipse.jgit.revwalk.RevCommit
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TagsManager @Inject constructor() {
|
class TagsManager @Inject constructor() {
|
||||||
|
suspend fun getTags(git: Git) = withContext(Dispatchers.IO) {
|
||||||
private val _tags = MutableStateFlow<List<Ref>>(listOf())
|
return@withContext git.tagList().call()
|
||||||
val tags: StateFlow<List<Ref>>
|
|
||||||
get() = _tags
|
|
||||||
|
|
||||||
suspend fun loadTags(git: Git) = withContext(Dispatchers.IO) {
|
|
||||||
val branchList = git.tagList().call()
|
|
||||||
|
|
||||||
|
|
||||||
_tags.value = branchList
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun createTagOnCommit(git: Git, tag: String, revCommit: RevCommit) = withContext(Dispatchers.IO) {
|
suspend fun createTagOnCommit(git: Git, tag: String, revCommit: RevCommit) = withContext(Dispatchers.IO) {
|
||||||
|
@ -21,7 +21,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import app.LoadingRepository
|
import app.LoadingRepository
|
||||||
import app.credentials.CredentialsState
|
import app.credentials.CredentialsState
|
||||||
import app.git.GitManager
|
import app.git.TabViewModel
|
||||||
import app.git.RepositorySelectionStatus
|
import app.git.RepositorySelectionStatus
|
||||||
import app.ui.dialogs.PasswordDialog
|
import app.ui.dialogs.PasswordDialog
|
||||||
import app.ui.dialogs.UserPasswordDialog
|
import app.ui.dialogs.UserPasswordDialog
|
||||||
@ -30,7 +30,7 @@ import kotlinx.coroutines.delay
|
|||||||
@OptIn(ExperimentalAnimationApi::class)
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AppTab(
|
fun AppTab(
|
||||||
gitManager: GitManager,
|
gitManager: TabViewModel,
|
||||||
repositoryPath: String?,
|
repositoryPath: String?,
|
||||||
tabName: MutableState<String>
|
tabName: MutableState<String>
|
||||||
) {
|
) {
|
||||||
@ -101,7 +101,7 @@ fun AppTab(
|
|||||||
LoadingRepository()
|
LoadingRepository()
|
||||||
}
|
}
|
||||||
is RepositorySelectionStatus.Open -> {
|
is RepositorySelectionStatus.Open -> {
|
||||||
RepositoryOpenPage(gitManager = gitManager)
|
RepositoryOpenPage(tabViewModel = gitManager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,7 +158,7 @@ fun AppTab(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CredentialsDialog(gitManager: GitManager) {
|
fun CredentialsDialog(gitManager: TabViewModel) {
|
||||||
val credentialsState by gitManager.credentialsState.collectAsState()
|
val credentialsState by gitManager.credentialsState.collectAsState()
|
||||||
|
|
||||||
if (credentialsState == CredentialsState.HttpCredentialsRequested) {
|
if (credentialsState == CredentialsState.HttpCredentialsRequested) {
|
||||||
|
@ -13,23 +13,23 @@ import androidx.compose.ui.unit.dp
|
|||||||
import app.MAX_SIDE_PANEL_ITEMS_HEIGHT
|
import app.MAX_SIDE_PANEL_ITEMS_HEIGHT
|
||||||
import app.extensions.isLocal
|
import app.extensions.isLocal
|
||||||
import app.extensions.simpleName
|
import app.extensions.simpleName
|
||||||
import app.git.GitManager
|
import app.git.TabViewModel
|
||||||
import app.ui.components.ScrollableLazyColumn
|
import app.ui.components.ScrollableLazyColumn
|
||||||
import app.ui.components.SideMenuEntry
|
import app.ui.components.SideMenuEntry
|
||||||
import app.ui.components.SideMenuSubentry
|
import app.ui.components.SideMenuSubentry
|
||||||
import app.ui.components.entryHeight
|
import app.ui.components.entryHeight
|
||||||
import app.ui.context_menu.branchContextMenuItems
|
import app.ui.context_menu.branchContextMenuItems
|
||||||
import app.ui.dialogs.MergeDialog
|
import app.ui.dialogs.MergeDialog
|
||||||
|
import app.viewmodels.BranchesViewModel
|
||||||
import org.eclipse.jgit.lib.Ref
|
import org.eclipse.jgit.lib.Ref
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Branches(
|
fun Branches(
|
||||||
gitManager: GitManager,
|
branchesViewModel: BranchesViewModel,
|
||||||
onBranchClicked: (Ref) -> Unit,
|
onBranchClicked: (Ref) -> Unit,
|
||||||
|
|
||||||
) {
|
) {
|
||||||
val branches by gitManager.branches.collectAsState()
|
val branches by branchesViewModel.branches.collectAsState()
|
||||||
val currentBranch by gitManager.currentBranch.collectAsState()
|
val currentBranch by branchesViewModel.currentBranch.collectAsState()
|
||||||
val (mergeBranch, setMergeBranch) = remember { mutableStateOf<Ref?>(null) }
|
val (mergeBranch, setMergeBranch) = remember { mutableStateOf<Ref?>(null) }
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@ -48,9 +48,9 @@ fun Branches(
|
|||||||
branch = branch,
|
branch = branch,
|
||||||
isCurrentBranch = currentBranch == branch.name,
|
isCurrentBranch = currentBranch == branch.name,
|
||||||
onBranchClicked = { onBranchClicked(branch) },
|
onBranchClicked = { onBranchClicked(branch) },
|
||||||
onCheckoutBranch = { gitManager.checkoutRef(branch) },
|
onCheckoutBranch = { branchesViewModel.checkoutRef(branch) },
|
||||||
onMergeBranch = { setMergeBranch(branch) },
|
onMergeBranch = { setMergeBranch(branch) },
|
||||||
onDeleteBranch = { gitManager.deleteBranch(branch) },
|
onDeleteBranch = { branchesViewModel.deleteBranch(branch) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ fun Branches(
|
|||||||
currentBranch,
|
currentBranch,
|
||||||
mergeBranchName = mergeBranch.name,
|
mergeBranchName = mergeBranch.name,
|
||||||
onReject = { setMergeBranch(null) },
|
onReject = { setMergeBranch(null) },
|
||||||
onAccept = { ff -> gitManager.mergeBranch(mergeBranch, ff) }
|
onAccept = { ff -> branchesViewModel.mergeBranch(mergeBranch, ff) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package app.ui
|
|||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.*
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
|
||||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
import androidx.compose.material.Divider
|
import androidx.compose.material.Divider
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
@ -12,14 +11,13 @@ import androidx.compose.material.Text
|
|||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import app.extensions.*
|
import app.extensions.*
|
||||||
import app.git.GitManager
|
import app.git.TabViewModel
|
||||||
import app.theme.headerBackground
|
import app.theme.headerBackground
|
||||||
import app.theme.headerText
|
import app.theme.headerText
|
||||||
import app.theme.primaryTextColor
|
import app.theme.primaryTextColor
|
||||||
@ -32,7 +30,7 @@ import org.eclipse.jgit.revwalk.RevCommit
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CommitChanges(
|
fun CommitChanges(
|
||||||
gitManager: GitManager,
|
gitManager: TabViewModel,
|
||||||
commit: RevCommit,
|
commit: RevCommit,
|
||||||
onDiffSelected: (DiffEntry) -> Unit
|
onDiffSelected: (DiffEntry) -> Unit
|
||||||
) {
|
) {
|
||||||
|
@ -16,24 +16,25 @@ import androidx.compose.ui.text.font.FontFamily
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import app.git.DiffEntryType
|
import app.git.DiffEntryType
|
||||||
import app.git.GitManager
|
import app.git.TabViewModel
|
||||||
import app.git.diff.Hunk
|
import app.git.diff.Hunk
|
||||||
import app.git.diff.LineType
|
import app.git.diff.LineType
|
||||||
import app.theme.primaryTextColor
|
import app.theme.primaryTextColor
|
||||||
import app.ui.components.ScrollableLazyColumn
|
import app.ui.components.ScrollableLazyColumn
|
||||||
import app.ui.components.SecondaryButton
|
import app.ui.components.SecondaryButton
|
||||||
|
import app.viewmodels.DiffViewModel
|
||||||
import org.eclipse.jgit.diff.DiffEntry
|
import org.eclipse.jgit.diff.DiffEntry
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Diff(gitManager: GitManager, diffEntryType: DiffEntryType, onCloseDiffView: () -> Unit) {
|
fun Diff(
|
||||||
var text by remember { mutableStateOf(listOf<Hunk>()) }
|
diffViewModel: DiffViewModel,
|
||||||
|
onCloseDiffView: () -> Unit,
|
||||||
|
) {
|
||||||
|
val diffResultState = diffViewModel.diffResult.collectAsState()
|
||||||
|
val diffResult = diffResultState.value ?: return
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
val diffEntryType = diffResult.diffEntryType
|
||||||
text = gitManager.diffFormat(diffEntryType)
|
val hunks = diffResult.hunks
|
||||||
|
|
||||||
|
|
||||||
if (text.isEmpty()) onCloseDiffView()
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -59,7 +60,7 @@ fun Diff(gitManager: GitManager, diffEntryType: DiffEntryType, onCloseDiffView:
|
|||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
// .padding(16.dp)
|
// .padding(16.dp)
|
||||||
) {
|
) {
|
||||||
itemsIndexed(text) { index, hunk ->
|
itemsIndexed(hunks) { index, hunk ->
|
||||||
val hunksSeparation = if (index == 0)
|
val hunksSeparation = if (index == 0)
|
||||||
0.dp
|
0.dp
|
||||||
else
|
else
|
||||||
@ -96,9 +97,9 @@ fun Diff(gitManager: GitManager, diffEntryType: DiffEntryType, onCloseDiffView:
|
|||||||
backgroundButton = color,
|
backgroundButton = color,
|
||||||
onClick = {
|
onClick = {
|
||||||
if (diffEntryType is DiffEntryType.StagedDiff) {
|
if (diffEntryType is DiffEntryType.StagedDiff) {
|
||||||
gitManager.unstageHunk(diffEntryType.diffEntry, hunk)
|
diffViewModel.unstageHunk(diffEntryType.diffEntry, hunk)
|
||||||
} else {
|
} else {
|
||||||
gitManager.stageHunk(diffEntryType.diffEntry, hunk)
|
diffViewModel.stageHunk(diffEntryType.diffEntry, hunk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -12,16 +12,17 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import app.MAX_SIDE_PANEL_ITEMS_HEIGHT
|
import app.MAX_SIDE_PANEL_ITEMS_HEIGHT
|
||||||
import app.extensions.simpleVisibleName
|
import app.extensions.simpleVisibleName
|
||||||
import app.git.GitManager
|
import app.git.TabViewModel
|
||||||
import app.git.RemoteInfo
|
import app.git.RemoteInfo
|
||||||
import app.ui.components.ScrollableLazyColumn
|
import app.ui.components.ScrollableLazyColumn
|
||||||
import app.ui.components.SideMenuEntry
|
import app.ui.components.SideMenuEntry
|
||||||
import app.ui.components.SideMenuSubentry
|
import app.ui.components.SideMenuSubentry
|
||||||
import app.ui.components.entryHeight
|
import app.ui.components.entryHeight
|
||||||
|
import app.viewmodels.RemotesViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Remotes(gitManager: GitManager) {
|
fun Remotes(remotesViewModel: RemotesViewModel) {
|
||||||
val remotes by gitManager.remotes.collectAsState()
|
val remotes by remotesViewModel.remotes.collectAsState()
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
SideMenuEntry("Remotes")
|
SideMenuEntry("Remotes")
|
||||||
|
@ -6,7 +6,7 @@ import androidx.compose.runtime.*
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import app.git.DiffEntryType
|
import app.git.DiffEntryType
|
||||||
import app.git.GitManager
|
import app.git.TabViewModel
|
||||||
import app.ui.dialogs.NewBranchDialog
|
import app.ui.dialogs.NewBranchDialog
|
||||||
import app.ui.log.Log
|
import app.ui.log.Log
|
||||||
import openRepositoryDialog
|
import openRepositoryDialog
|
||||||
@ -18,15 +18,22 @@ import org.jetbrains.compose.splitpane.rememberSplitPaneState
|
|||||||
|
|
||||||
@OptIn(ExperimentalSplitPaneApi::class, androidx.compose.ui.ExperimentalComposeUiApi::class)
|
@OptIn(ExperimentalSplitPaneApi::class, androidx.compose.ui.ExperimentalComposeUiApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun RepositoryOpenPage(gitManager: GitManager) {
|
fun RepositoryOpenPage(tabViewModel: TabViewModel) {
|
||||||
|
val repositoryState by tabViewModel.repositoryState.collectAsState()
|
||||||
|
|
||||||
var diffSelected by remember {
|
var diffSelected by remember {
|
||||||
mutableStateOf<DiffEntryType?>(null)
|
mutableStateOf<DiffEntryType?>(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(diffSelected) {
|
||||||
|
diffSelected?.let { safeDiffSelected ->
|
||||||
|
tabViewModel.updatedDiffEntry(safeDiffSelected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var showNewBranchDialog by remember { mutableStateOf(false) }
|
var showNewBranchDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val (selectedItem, setSelectedItem) = remember { mutableStateOf<SelectedItem>(SelectedItem.None) }
|
val (selectedItem, setSelectedItem) = remember { mutableStateOf<SelectedItem>(SelectedItem.None) }
|
||||||
|
|
||||||
LaunchedEffect(selectedItem) {
|
LaunchedEffect(selectedItem) {
|
||||||
diffSelected = null
|
diffSelected = null
|
||||||
}
|
}
|
||||||
@ -37,7 +44,7 @@ fun RepositoryOpenPage(gitManager: GitManager) {
|
|||||||
showNewBranchDialog = false
|
showNewBranchDialog = false
|
||||||
},
|
},
|
||||||
onAccept = { branchName ->
|
onAccept = { branchName ->
|
||||||
gitManager.createBranch(branchName)
|
tabViewModel.branchesViewModel.createBranch(branchName)
|
||||||
showNewBranchDialog = false
|
showNewBranchDialog = false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -46,12 +53,12 @@ fun RepositoryOpenPage(gitManager: GitManager) {
|
|||||||
Column {
|
Column {
|
||||||
GMenu(
|
GMenu(
|
||||||
onRepositoryOpen = {
|
onRepositoryOpen = {
|
||||||
openRepositoryDialog(gitManager = gitManager)
|
openRepositoryDialog(gitManager = tabViewModel)
|
||||||
},
|
},
|
||||||
onPull = { gitManager.pull() },
|
onPull = { tabViewModel.pull() },
|
||||||
onPush = { gitManager.push() },
|
onPush = { tabViewModel.push() },
|
||||||
onStash = { gitManager.stash() },
|
onStash = { tabViewModel.stash() },
|
||||||
onPopStash = { gitManager.popStash() },
|
onPopStash = { tabViewModel.popStash() },
|
||||||
onCreateBranch = { showNewBranchDialog = true }
|
onCreateBranch = { showNewBranchDialog = true }
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -65,22 +72,22 @@ fun RepositoryOpenPage(gitManager: GitManager) {
|
|||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
) {
|
) {
|
||||||
Branches(
|
Branches(
|
||||||
gitManager = gitManager,
|
branchesViewModel = tabViewModel.branchesViewModel,
|
||||||
onBranchClicked = {
|
onBranchClicked = {
|
||||||
val commit = gitManager.findCommit(it.objectId)
|
val commit = tabViewModel.findCommit(it.objectId)
|
||||||
setSelectedItem(SelectedItem.Ref(commit))
|
setSelectedItem(SelectedItem.Ref(commit))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Remotes(gitManager = gitManager)
|
Remotes(remotesViewModel = tabViewModel.remotesViewModel)
|
||||||
Tags(
|
Tags(
|
||||||
gitManager = gitManager,
|
tagsViewModel = tabViewModel.tagsViewModel,
|
||||||
onTagClicked = {
|
onTagClicked = {
|
||||||
val commit = gitManager.findCommit(it.objectId)
|
val commit = tabViewModel.findCommit(it.objectId)
|
||||||
setSelectedItem(SelectedItem.Ref(commit))
|
setSelectedItem(SelectedItem.Ref(commit))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Stashes(
|
Stashes(
|
||||||
gitManager = gitManager,
|
gitManager = tabViewModel,
|
||||||
onStashSelected = { stash ->
|
onStashSelected = { stash ->
|
||||||
setSelectedItem(SelectedItem.Stash(stash))
|
setSelectedItem(SelectedItem.Stash(stash))
|
||||||
}
|
}
|
||||||
@ -97,11 +104,13 @@ fun RepositoryOpenPage(gitManager: GitManager) {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
Crossfade(targetState = diffSelected) { diffEntry ->
|
// Crossfade(targetState = diffSelected) { diffEntry ->
|
||||||
when (diffEntry) {
|
when (diffSelected) {
|
||||||
null -> {
|
null -> {
|
||||||
Log(
|
Log(
|
||||||
gitManager = gitManager,
|
tabViewModel = tabViewModel,
|
||||||
|
repositoryState = repositoryState,
|
||||||
|
logViewModel = tabViewModel.logViewModel,
|
||||||
selectedItem = selectedItem,
|
selectedItem = selectedItem,
|
||||||
onItemSelected = {
|
onItemSelected = {
|
||||||
setSelectedItem(it)
|
setSelectedItem(it)
|
||||||
@ -110,12 +119,11 @@ fun RepositoryOpenPage(gitManager: GitManager) {
|
|||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
Diff(
|
Diff(
|
||||||
gitManager = gitManager,
|
diffViewModel = tabViewModel.diffViewModel,
|
||||||
diffEntryType = diffEntry,
|
|
||||||
onCloseDiffView = { diffSelected = null })
|
onCloseDiffView = { diffSelected = null })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,8 +134,9 @@ fun RepositoryOpenPage(gitManager: GitManager) {
|
|||||||
) {
|
) {
|
||||||
if (selectedItem == SelectedItem.UncommitedChanges) {
|
if (selectedItem == SelectedItem.UncommitedChanges) {
|
||||||
UncommitedChanges(
|
UncommitedChanges(
|
||||||
gitManager = gitManager,
|
statusViewModel = tabViewModel.statusViewModel,
|
||||||
selectedEntryType = diffSelected,
|
selectedEntryType = diffSelected,
|
||||||
|
repositoryState = repositoryState,
|
||||||
onStagedDiffEntrySelected = { diffEntry ->
|
onStagedDiffEntrySelected = { diffEntry ->
|
||||||
diffSelected = if (diffEntry != null)
|
diffSelected = if (diffEntry != null)
|
||||||
DiffEntryType.StagedDiff(diffEntry)
|
DiffEntryType.StagedDiff(diffEntry)
|
||||||
@ -140,7 +149,7 @@ fun RepositoryOpenPage(gitManager: GitManager) {
|
|||||||
)
|
)
|
||||||
} else if (selectedItem is SelectedItem.CommitBasedItem) {
|
} else if (selectedItem is SelectedItem.CommitBasedItem) {
|
||||||
CommitChanges(
|
CommitChanges(
|
||||||
gitManager = gitManager,
|
gitManager = tabViewModel,
|
||||||
commit = selectedItem.revCommit,
|
commit = selectedItem.revCommit,
|
||||||
onDiffSelected = { diffEntry ->
|
onDiffSelected = { diffEntry ->
|
||||||
diffSelected = DiffEntryType.CommitDiff(diffEntry)
|
diffSelected = DiffEntryType.CommitDiff(diffEntry)
|
||||||
|
@ -6,7 +6,7 @@ import androidx.compose.foundation.lazy.items
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import app.git.GitManager
|
import app.git.TabViewModel
|
||||||
import app.git.StashStatus
|
import app.git.StashStatus
|
||||||
import app.ui.components.ScrollableLazyColumn
|
import app.ui.components.ScrollableLazyColumn
|
||||||
import app.ui.components.SideMenuEntry
|
import app.ui.components.SideMenuEntry
|
||||||
@ -15,7 +15,7 @@ import org.eclipse.jgit.revwalk.RevCommit
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Stashes(
|
fun Stashes(
|
||||||
gitManager: GitManager,
|
gitManager: TabViewModel,
|
||||||
onStashSelected: (commit: RevCommit) -> Unit,
|
onStashSelected: (commit: RevCommit) -> Unit,
|
||||||
) {
|
) {
|
||||||
val stashStatusState = gitManager.stashStatus.collectAsState()
|
val stashStatusState = gitManager.stashStatus.collectAsState()
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import app.extensions.runCommand
|
import app.extensions.runCommand
|
||||||
import app.git.GitManager
|
import app.git.TabViewModel
|
||||||
import javax.swing.JFileChooser
|
import javax.swing.JFileChooser
|
||||||
|
|
||||||
|
|
||||||
fun openRepositoryDialog(gitManager: GitManager) {
|
fun openRepositoryDialog(gitManager: TabViewModel) {
|
||||||
val os = System.getProperty("os.name")
|
val os = System.getProperty("os.name")
|
||||||
val appStateManager = gitManager.appStateManager
|
val appStateManager = gitManager.appStateManager
|
||||||
val latestDirectoryOpened = appStateManager.latestOpenedRepositoryPath
|
val latestDirectoryOpened = appStateManager.latestOpenedRepositoryPath
|
||||||
@ -29,7 +29,7 @@ fun openRepositoryDialog(gitManager: GitManager) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun openRepositoryDialog(
|
private fun openRepositoryDialog(
|
||||||
gitManager: GitManager,
|
gitManager: TabViewModel,
|
||||||
latestDirectoryOpened: String
|
latestDirectoryOpened: String
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@ -13,20 +13,21 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import app.MAX_SIDE_PANEL_ITEMS_HEIGHT
|
import app.MAX_SIDE_PANEL_ITEMS_HEIGHT
|
||||||
import app.extensions.simpleName
|
import app.extensions.simpleName
|
||||||
import app.git.GitManager
|
import app.git.TabViewModel
|
||||||
import app.ui.components.ScrollableLazyColumn
|
import app.ui.components.ScrollableLazyColumn
|
||||||
import app.ui.components.SideMenuEntry
|
import app.ui.components.SideMenuEntry
|
||||||
import app.ui.components.SideMenuSubentry
|
import app.ui.components.SideMenuSubentry
|
||||||
import app.ui.components.entryHeight
|
import app.ui.components.entryHeight
|
||||||
import app.ui.context_menu.tagContextMenuItems
|
import app.ui.context_menu.tagContextMenuItems
|
||||||
|
import app.viewmodels.TagsViewModel
|
||||||
import org.eclipse.jgit.lib.Ref
|
import org.eclipse.jgit.lib.Ref
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Tags(
|
fun Tags(
|
||||||
gitManager: GitManager,
|
tagsViewModel: TagsViewModel,
|
||||||
onTagClicked: (Ref) -> Unit,
|
onTagClicked: (Ref) -> Unit,
|
||||||
) {
|
) {
|
||||||
val tagsState = gitManager.tags.collectAsState()
|
val tagsState = tagsViewModel.tags.collectAsState()
|
||||||
val tags = tagsState.value
|
val tags = tagsState.value
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@ -46,8 +47,8 @@ fun Tags(
|
|||||||
TagRow(
|
TagRow(
|
||||||
tag = tag,
|
tag = tag,
|
||||||
onTagClicked = { onTagClicked(tag) },
|
onTagClicked = { onTagClicked(tag) },
|
||||||
onCheckoutTag = { gitManager.checkoutRef(tag) },
|
onCheckoutTag = { tagsViewModel.checkoutRef(tag) },
|
||||||
onDeleteTag = { gitManager.deleteTag(tag) }
|
onDeleteTag = { tagsViewModel.deleteTag(tag) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,36 +28,30 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import app.extensions.filePath
|
import app.extensions.filePath
|
||||||
import app.extensions.icon
|
|
||||||
import app.extensions.iconColor
|
|
||||||
import app.extensions.isMerging
|
import app.extensions.isMerging
|
||||||
import app.git.DiffEntryType
|
import app.git.DiffEntryType
|
||||||
import app.git.GitManager
|
|
||||||
import app.git.StageStatus
|
|
||||||
import app.git.StatusEntry
|
import app.git.StatusEntry
|
||||||
import app.theme.headerBackground
|
import app.theme.headerBackground
|
||||||
import app.theme.headerText
|
import app.theme.headerText
|
||||||
import app.theme.primaryTextColor
|
import app.theme.primaryTextColor
|
||||||
import app.ui.components.ScrollableLazyColumn
|
import app.ui.components.ScrollableLazyColumn
|
||||||
import app.ui.components.SecondaryButton
|
import app.ui.components.SecondaryButton
|
||||||
|
import app.viewmodels.StageStatus
|
||||||
|
import app.viewmodels.StatusViewModel
|
||||||
import org.eclipse.jgit.diff.DiffEntry
|
import org.eclipse.jgit.diff.DiffEntry
|
||||||
|
import org.eclipse.jgit.lib.RepositoryState
|
||||||
|
|
||||||
@OptIn(ExperimentalAnimationApi::class, androidx.compose.ui.ExperimentalComposeUiApi::class)
|
@OptIn(ExperimentalAnimationApi::class, androidx.compose.ui.ExperimentalComposeUiApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun UncommitedChanges(
|
fun UncommitedChanges(
|
||||||
gitManager: GitManager,
|
statusViewModel: StatusViewModel,
|
||||||
selectedEntryType: DiffEntryType?,
|
selectedEntryType: DiffEntryType?,
|
||||||
|
repositoryState: RepositoryState,
|
||||||
onStagedDiffEntrySelected: (DiffEntry?) -> Unit,
|
onStagedDiffEntrySelected: (DiffEntry?) -> Unit,
|
||||||
onUnstagedDiffEntrySelected: (DiffEntry) -> Unit,
|
onUnstagedDiffEntrySelected: (DiffEntry) -> Unit,
|
||||||
) {
|
) {
|
||||||
val stageStatusState = gitManager.stageStatus.collectAsState()
|
val stageStatusState = statusViewModel.stageStatus.collectAsState()
|
||||||
val stageStatus = stageStatusState.value
|
val stageStatus = stageStatusState.value
|
||||||
val lastCheck by gitManager.lastTimeChecked.collectAsState()
|
|
||||||
val repositoryState by gitManager.repositoryState.collectAsState()
|
|
||||||
|
|
||||||
LaunchedEffect(lastCheck) {
|
|
||||||
gitManager.loadStatus()
|
|
||||||
}
|
|
||||||
|
|
||||||
val staged: List<StatusEntry>
|
val staged: List<StatusEntry>
|
||||||
val unstaged: List<StatusEntry>
|
val unstaged: List<StatusEntry>
|
||||||
@ -83,7 +77,7 @@ fun UncommitedChanges(
|
|||||||
|
|
||||||
var commitMessage by remember { mutableStateOf("") }
|
var commitMessage by remember { mutableStateOf("") }
|
||||||
val doCommit = {
|
val doCommit = {
|
||||||
gitManager.commit(commitMessage)
|
statusViewModel.commit(commitMessage)
|
||||||
onStagedDiffEntrySelected(null)
|
onStagedDiffEntrySelected(null)
|
||||||
commitMessage = ""
|
commitMessage = ""
|
||||||
}
|
}
|
||||||
@ -111,13 +105,13 @@ fun UncommitedChanges(
|
|||||||
diffEntries = staged,
|
diffEntries = staged,
|
||||||
onDiffEntrySelected = onStagedDiffEntrySelected,
|
onDiffEntrySelected = onStagedDiffEntrySelected,
|
||||||
onDiffEntryOptionSelected = {
|
onDiffEntryOptionSelected = {
|
||||||
gitManager.unstage(it)
|
statusViewModel.unstage(it)
|
||||||
},
|
},
|
||||||
onReset = { diffEntry ->
|
onReset = { diffEntry ->
|
||||||
gitManager.resetStaged(diffEntry)
|
statusViewModel.resetStaged(diffEntry)
|
||||||
},
|
},
|
||||||
onAllAction = {
|
onAllAction = {
|
||||||
gitManager.unstageAll()
|
statusViewModel.unstageAll()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -132,13 +126,13 @@ fun UncommitedChanges(
|
|||||||
diffEntries = unstaged,
|
diffEntries = unstaged,
|
||||||
onDiffEntrySelected = onUnstagedDiffEntrySelected,
|
onDiffEntrySelected = onUnstagedDiffEntrySelected,
|
||||||
onDiffEntryOptionSelected = {
|
onDiffEntryOptionSelected = {
|
||||||
gitManager.stage(it)
|
statusViewModel.stage(it)
|
||||||
},
|
},
|
||||||
onReset = { diffEntry ->
|
onReset = { diffEntry ->
|
||||||
gitManager.resetUnstaged(diffEntry)
|
statusViewModel.resetUnstaged(diffEntry)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
gitManager.stageAll()
|
statusViewModel.stageAll()
|
||||||
},
|
},
|
||||||
allActionTitle = "Stage all"
|
allActionTitle = "Stage all"
|
||||||
)
|
)
|
||||||
|
@ -20,7 +20,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import app.extensions.dirName
|
import app.extensions.dirName
|
||||||
import app.extensions.dirPath
|
import app.extensions.dirPath
|
||||||
import app.git.GitManager
|
import app.git.TabViewModel
|
||||||
import app.theme.primaryTextColor
|
import app.theme.primaryTextColor
|
||||||
import app.theme.secondaryTextColor
|
import app.theme.secondaryTextColor
|
||||||
import app.ui.dialogs.CloneDialog
|
import app.ui.dialogs.CloneDialog
|
||||||
@ -33,7 +33,7 @@ import java.net.URI
|
|||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun WelcomePage(
|
fun WelcomePage(
|
||||||
gitManager: GitManager,
|
gitManager: TabViewModel,
|
||||||
) {
|
) {
|
||||||
val appStateManager = gitManager.appStateManager
|
val appStateManager = gitManager.appStateManager
|
||||||
var showCloneView by remember { mutableStateOf(false) }
|
var showCloneView by remember { mutableStateOf(false) }
|
||||||
|
@ -20,8 +20,14 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import app.AppStateManager
|
||||||
|
import app.di.AppComponent
|
||||||
|
import app.di.DaggerTabComponent
|
||||||
|
import app.git.TabViewModel
|
||||||
import app.theme.tabColorActive
|
import app.theme.tabColorActive
|
||||||
import app.theme.tabColorInactive
|
import app.theme.tabColorInactive
|
||||||
|
import app.ui.AppTab
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -48,7 +54,7 @@ fun RepositoriesTabPanel(
|
|||||||
) {
|
) {
|
||||||
items(items = tabs) { tab ->
|
items(items = tabs) { tab ->
|
||||||
Tab(
|
Tab(
|
||||||
title = tab.title,
|
title = tab.tabName,
|
||||||
selected = tab.key == selectedTabKey,
|
selected = tab.key == selectedTabKey,
|
||||||
onClick = {
|
onClick = {
|
||||||
onTabSelected(tab.key)
|
onTabSelected(tab.key)
|
||||||
@ -154,7 +160,33 @@ fun Tab(title: MutableState<String>, selected: Boolean, onClick: () -> Unit, onC
|
|||||||
}
|
}
|
||||||
|
|
||||||
class TabInformation(
|
class TabInformation(
|
||||||
val title: MutableState<String>,
|
val tabName: MutableState<String>,
|
||||||
val key: Int,
|
val key: Int,
|
||||||
|
val path: String?,
|
||||||
|
appComponent: AppComponent,
|
||||||
|
) {
|
||||||
|
@Inject
|
||||||
|
lateinit var gitManager: TabViewModel
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var appStateManager: AppStateManager
|
||||||
|
|
||||||
val content: @Composable (TabInformation) -> Unit
|
val content: @Composable (TabInformation) -> Unit
|
||||||
)
|
|
||||||
|
init {
|
||||||
|
val tabComponent = DaggerTabComponent.builder()
|
||||||
|
.appComponent(appComponent)
|
||||||
|
.build()
|
||||||
|
tabComponent.inject(this)
|
||||||
|
|
||||||
|
gitManager.onRepositoryChanged = { path ->
|
||||||
|
if (path == null) {
|
||||||
|
appStateManager.repositoryTabRemoved(key)
|
||||||
|
} else
|
||||||
|
appStateManager.repositoryTabChanged(key, path)
|
||||||
|
}
|
||||||
|
content = {
|
||||||
|
AppTab(gitManager, path, tabName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,13 +12,13 @@ import androidx.compose.ui.text.TextStyle
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import app.git.CloneStatus
|
import app.git.CloneStatus
|
||||||
import app.git.GitManager
|
import app.git.TabViewModel
|
||||||
import app.theme.primaryTextColor
|
import app.theme.primaryTextColor
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CloneDialog(
|
fun CloneDialog(
|
||||||
gitManager: GitManager,
|
gitManager: TabViewModel,
|
||||||
onClose: () -> Unit
|
onClose: () -> Unit
|
||||||
) {
|
) {
|
||||||
val cloneStatus = gitManager.cloneStatus.collectAsState()
|
val cloneStatus = gitManager.cloneStatus.collectAsState()
|
||||||
|
@ -34,8 +34,7 @@ import androidx.compose.ui.unit.Dp
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import app.extensions.*
|
import app.extensions.*
|
||||||
import app.git.GitManager
|
import app.git.TabViewModel
|
||||||
import app.git.LogStatus
|
|
||||||
import app.git.graph.GraphNode
|
import app.git.graph.GraphNode
|
||||||
import app.theme.*
|
import app.theme.*
|
||||||
import app.ui.SelectedItem
|
import app.ui.SelectedItem
|
||||||
@ -47,6 +46,8 @@ import app.ui.dialogs.MergeDialog
|
|||||||
import app.ui.dialogs.NewBranchDialog
|
import app.ui.dialogs.NewBranchDialog
|
||||||
import app.ui.dialogs.NewTagDialog
|
import app.ui.dialogs.NewTagDialog
|
||||||
import app.ui.dialogs.ResetBranchDialog
|
import app.ui.dialogs.ResetBranchDialog
|
||||||
|
import app.viewmodels.LogStatus
|
||||||
|
import app.viewmodels.LogViewModel
|
||||||
import org.eclipse.jgit.lib.Ref
|
import org.eclipse.jgit.lib.Ref
|
||||||
import org.eclipse.jgit.lib.RepositoryState
|
import org.eclipse.jgit.lib.RepositoryState
|
||||||
import org.eclipse.jgit.revwalk.RevCommit
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
@ -70,13 +71,14 @@ private const val CANVAS_MIN_WIDTH = 100
|
|||||||
)
|
)
|
||||||
@Composable
|
@Composable
|
||||||
fun Log(
|
fun Log(
|
||||||
gitManager: GitManager,
|
tabViewModel: TabViewModel,
|
||||||
|
logViewModel: LogViewModel,
|
||||||
selectedItem: SelectedItem,
|
selectedItem: SelectedItem,
|
||||||
onItemSelected: (SelectedItem) -> Unit,
|
onItemSelected: (SelectedItem) -> Unit,
|
||||||
|
repositoryState: RepositoryState,
|
||||||
) {
|
) {
|
||||||
val logStatusState = gitManager.logStatus.collectAsState()
|
val logStatusState = logViewModel.logStatus.collectAsState()
|
||||||
val logStatus = logStatusState.value
|
val logStatus = logStatusState.value
|
||||||
val repositoryState by gitManager.repositoryState.collectAsState()
|
|
||||||
val showLogDialog = remember { mutableStateOf<LogDialog>(LogDialog.None) }
|
val showLogDialog = remember { mutableStateOf<LogDialog>(LogDialog.None) }
|
||||||
|
|
||||||
val selectedCommit = if (selectedItem is SelectedItem.CommitBasedItem) {
|
val selectedCommit = if (selectedItem is SelectedItem.CommitBasedItem) {
|
||||||
@ -86,6 +88,7 @@ fun Log(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (logStatus is LogStatus.Loaded) {
|
if (logStatus is LogStatus.Loaded) {
|
||||||
|
val hasUncommitedChanges = logStatus.hasUncommitedChanges
|
||||||
val commitList = logStatus.plotCommitList
|
val commitList = logStatus.plotCommitList
|
||||||
val scrollState = rememberLazyListState()
|
val scrollState = rememberLazyListState()
|
||||||
|
|
||||||
@ -102,7 +105,7 @@ fun Log(
|
|||||||
}
|
}
|
||||||
|
|
||||||
LogDialogs(
|
LogDialogs(
|
||||||
gitManager,
|
logViewModel,
|
||||||
currentBranch = logStatus.currentBranch,
|
currentBranch = logStatus.currentBranch,
|
||||||
onResetShowLogDialog = { showLogDialog.value = LogDialog.None },
|
onResetShowLogDialog = { showLogDialog.value = LogDialog.None },
|
||||||
showLogDialog = showLogDialog.value,
|
showLogDialog = showLogDialog.value,
|
||||||
@ -114,7 +117,7 @@ fun Log(
|
|||||||
.background(MaterialTheme.colors.background)
|
.background(MaterialTheme.colors.background)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
val hasUncommitedChanges by gitManager.hasUncommitedChanges.collectAsState()
|
// val hasUncommitedChanges by tabViewModel.hasUncommitedChanges.collectAsState()
|
||||||
val weightMod = remember { mutableStateOf(0f) }
|
val weightMod = remember { mutableStateOf(0f) }
|
||||||
var graphWidth = (CANVAS_MIN_WIDTH + weightMod.value).dp
|
var graphWidth = (CANVAS_MIN_WIDTH + weightMod.value).dp
|
||||||
|
|
||||||
@ -131,11 +134,12 @@ fun Log(
|
|||||||
.background(MaterialTheme.colors.background)
|
.background(MaterialTheme.colors.background)
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
|
//TODO: Shouldn't this be an item of the graph?
|
||||||
if (hasUncommitedChanges)
|
if (hasUncommitedChanges)
|
||||||
item {
|
item {
|
||||||
UncommitedChangesLine(
|
UncommitedChangesLine(
|
||||||
selected = selectedItem == SelectedItem.UncommitedChanges,
|
selected = selectedItem == SelectedItem.UncommitedChanges,
|
||||||
hasPreviousCommits = commitList.count() > 0,
|
hasPreviousCommits = commitList.isNotEmpty(),
|
||||||
graphWidth = graphWidth,
|
graphWidth = graphWidth,
|
||||||
weightMod = weightMod,
|
weightMod = weightMod,
|
||||||
repositoryState = repositoryState,
|
repositoryState = repositoryState,
|
||||||
@ -146,7 +150,7 @@ fun Log(
|
|||||||
}
|
}
|
||||||
items(items = commitList) { graphNode ->
|
items(items = commitList) { graphNode ->
|
||||||
CommitLine(
|
CommitLine(
|
||||||
gitManager = gitManager,
|
logViewModel = logViewModel,
|
||||||
graphNode = graphNode,
|
graphNode = graphNode,
|
||||||
selected = selectedCommit?.name == graphNode.name,
|
selected = selectedCommit?.name == graphNode.name,
|
||||||
weightMod = weightMod,
|
weightMod = weightMod,
|
||||||
@ -169,7 +173,7 @@ fun Log(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LogDialogs(
|
fun LogDialogs(
|
||||||
gitManager: GitManager,
|
logViewModel: LogViewModel,
|
||||||
onResetShowLogDialog: () -> Unit,
|
onResetShowLogDialog: () -> Unit,
|
||||||
showLogDialog: LogDialog,
|
showLogDialog: LogDialog,
|
||||||
currentBranch: Ref?,
|
currentBranch: Ref?,
|
||||||
@ -179,7 +183,7 @@ fun LogDialogs(
|
|||||||
NewBranchDialog(
|
NewBranchDialog(
|
||||||
onReject = onResetShowLogDialog,
|
onReject = onResetShowLogDialog,
|
||||||
onAccept = { branchName ->
|
onAccept = { branchName ->
|
||||||
gitManager.createBranchOnCommit(branchName, showLogDialog.graphNode)
|
logViewModel.createBranchOnCommit(branchName, showLogDialog.graphNode)
|
||||||
onResetShowLogDialog()
|
onResetShowLogDialog()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -188,7 +192,7 @@ fun LogDialogs(
|
|||||||
NewTagDialog(
|
NewTagDialog(
|
||||||
onReject = onResetShowLogDialog,
|
onReject = onResetShowLogDialog,
|
||||||
onAccept = { tagName ->
|
onAccept = { tagName ->
|
||||||
gitManager.createTagOnCommit(tagName, showLogDialog.graphNode)
|
logViewModel.createTagOnCommit(tagName, showLogDialog.graphNode)
|
||||||
onResetShowLogDialog()
|
onResetShowLogDialog()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -200,7 +204,7 @@ fun LogDialogs(
|
|||||||
mergeBranchName = showLogDialog.ref.simpleName,
|
mergeBranchName = showLogDialog.ref.simpleName,
|
||||||
onReject = onResetShowLogDialog,
|
onReject = onResetShowLogDialog,
|
||||||
onAccept = { ff ->
|
onAccept = { ff ->
|
||||||
gitManager.mergeBranch(showLogDialog.ref, ff)
|
logViewModel.mergeBranch(showLogDialog.ref, ff)
|
||||||
onResetShowLogDialog()
|
onResetShowLogDialog()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -208,7 +212,7 @@ fun LogDialogs(
|
|||||||
is LogDialog.ResetBranch -> ResetBranchDialog(
|
is LogDialog.ResetBranch -> ResetBranchDialog(
|
||||||
onReject = onResetShowLogDialog,
|
onReject = onResetShowLogDialog,
|
||||||
onAccept = { resetType ->
|
onAccept = { resetType ->
|
||||||
gitManager.resetToCommit(showLogDialog.graphNode, resetType)
|
logViewModel.resetToCommit(showLogDialog.graphNode, resetType)
|
||||||
onResetShowLogDialog()
|
onResetShowLogDialog()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -324,7 +328,7 @@ fun UncommitedChangesLine(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CommitLine(
|
fun CommitLine(
|
||||||
gitManager: GitManager,
|
logViewModel: LogViewModel,
|
||||||
graphNode: GraphNode,
|
graphNode: GraphNode,
|
||||||
selected: Boolean,
|
selected: Boolean,
|
||||||
weightMod: MutableState<Float>,
|
weightMod: MutableState<Float>,
|
||||||
@ -348,9 +352,7 @@ fun CommitLine(
|
|||||||
listOf(
|
listOf(
|
||||||
ContextMenuItem(
|
ContextMenuItem(
|
||||||
label = "Checkout commit",
|
label = "Checkout commit",
|
||||||
onClick = {
|
onClick = { logViewModel.checkoutCommit(graphNode) }),
|
||||||
gitManager.checkoutCommit(graphNode)
|
|
||||||
}),
|
|
||||||
ContextMenuItem(
|
ContextMenuItem(
|
||||||
label = "Create branch",
|
label = "Create branch",
|
||||||
onClick = showCreateNewBranch
|
onClick = showCreateNewBranch
|
||||||
@ -361,7 +363,7 @@ fun CommitLine(
|
|||||||
),
|
),
|
||||||
ContextMenuItem(
|
ContextMenuItem(
|
||||||
label = "Revert commit",
|
label = "Revert commit",
|
||||||
onClick = { gitManager.revertCommit(graphNode) }
|
onClick = { logViewModel.revertCommit(graphNode) }
|
||||||
),
|
),
|
||||||
|
|
||||||
ContextMenuItem(
|
ContextMenuItem(
|
||||||
@ -403,10 +405,10 @@ fun CommitLine(
|
|||||||
refs = commitRefs,
|
refs = commitRefs,
|
||||||
nodeColor = nodeColor,
|
nodeColor = nodeColor,
|
||||||
currentBranch = currentBranch,
|
currentBranch = currentBranch,
|
||||||
onCheckoutRef = { ref -> gitManager.checkoutRef(ref) },
|
onCheckoutRef = { ref -> logViewModel.checkoutRef(ref) },
|
||||||
onMergeBranch = { ref -> onMergeBranch(ref) },
|
onMergeBranch = { ref -> onMergeBranch(ref) },
|
||||||
onDeleteBranch = { ref -> gitManager.deleteBranch(ref) },
|
onDeleteBranch = { ref -> logViewModel.deleteBranch(ref) },
|
||||||
onDeleteTag = { ref -> gitManager.deleteTag(ref) },
|
onDeleteTag = { ref -> logViewModel.deleteTag(ref) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
60
src/main/kotlin/app/viewmodels/BranchesViewModel.kt
Normal file
60
src/main/kotlin/app/viewmodels/BranchesViewModel.kt
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package app.viewmodels
|
||||||
|
|
||||||
|
import app.git.BranchesManager
|
||||||
|
import app.git.RefreshType
|
||||||
|
import app.git.TabState
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.lib.Ref
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class BranchesViewModel @Inject constructor(
|
||||||
|
private val branchesManager: BranchesManager,
|
||||||
|
private val tabState: TabState,
|
||||||
|
) {
|
||||||
|
private val _branches = MutableStateFlow<List<Ref>>(listOf())
|
||||||
|
val branches: StateFlow<List<Ref>>
|
||||||
|
get() = _branches
|
||||||
|
|
||||||
|
private val _currentBranch = MutableStateFlow<String>("")
|
||||||
|
val currentBranch: StateFlow<String>
|
||||||
|
get() = _currentBranch
|
||||||
|
|
||||||
|
suspend fun loadBranches(git: Git) {
|
||||||
|
val branchesList = branchesManager.getBranches(git)
|
||||||
|
|
||||||
|
_branches.value = branchesList
|
||||||
|
_currentBranch.value = branchesManager.currentBranchRef(git)?.name ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createBranch(branchName: String) = tabState.safeProcessing { git ->
|
||||||
|
branchesManager.createBranch(git, branchName)
|
||||||
|
this.loadBranches(git)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mergeBranch(ref: Ref, fastForward: Boolean) = tabState.safeProcessing { git ->
|
||||||
|
branchesManager.mergeBranch(git, ref, fastForward)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ALL_DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteBranch(branch: Ref) =tabState.safeProcessing { git ->
|
||||||
|
branchesManager.deleteBranch(git, branch)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ALL_DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkoutRef(ref: Ref) = tabState.safeProcessing { git ->
|
||||||
|
branchesManager.checkoutRef(git, ref)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ALL_DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun refresh(git: Git) {
|
||||||
|
loadBranches(git)
|
||||||
|
}
|
||||||
|
}
|
44
src/main/kotlin/app/viewmodels/DiffViewModel.kt
Normal file
44
src/main/kotlin/app/viewmodels/DiffViewModel.kt
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package app.viewmodels
|
||||||
|
|
||||||
|
import app.git.*
|
||||||
|
import app.git.diff.Hunk
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.diff.DiffEntry
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class DiffViewModel @Inject constructor(
|
||||||
|
private val tabState: TabState,
|
||||||
|
private val diffManager: DiffManager,
|
||||||
|
private val statusManager: StatusManager,
|
||||||
|
) {
|
||||||
|
// TODO Maybe use a sealed class instead of a null to represent that a diff is not selected?
|
||||||
|
private val _diffResult = MutableStateFlow<DiffResult?>(null)
|
||||||
|
val diffResult: StateFlow<DiffResult?> = _diffResult
|
||||||
|
|
||||||
|
suspend fun updateDiff(git: Git, diffEntryType: DiffEntryType) = withContext(Dispatchers.IO) {
|
||||||
|
_diffResult.value = null
|
||||||
|
|
||||||
|
val hunks = diffManager.diffFormat(git, diffEntryType)
|
||||||
|
|
||||||
|
_diffResult.value = DiffResult(diffEntryType, hunks)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stageHunk(diffEntry: DiffEntry, hunk: Hunk) = tabState.runOperation { git ->
|
||||||
|
statusManager.stageHunk(git, diffEntry, hunk)
|
||||||
|
|
||||||
|
return@runOperation RefreshType.UNCOMMITED_CHANGES
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unstageHunk(diffEntry: DiffEntry, hunk: Hunk) = tabState.runOperation { git ->
|
||||||
|
statusManager.unstageHunk(git, diffEntry, hunk)
|
||||||
|
|
||||||
|
return@runOperation RefreshType.UNCOMMITED_CHANGES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class DiffResult(val diffEntryType: DiffEntryType, val hunks: List<Hunk>)
|
97
src/main/kotlin/app/viewmodels/LogViewModel.kt
Normal file
97
src/main/kotlin/app/viewmodels/LogViewModel.kt
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package app.viewmodels
|
||||||
|
|
||||||
|
import app.git.*
|
||||||
|
import app.git.graph.GraphCommitList
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.lib.Ref
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class LogViewModel @Inject constructor(
|
||||||
|
private val logManager: LogManager,
|
||||||
|
private val statusManager: StatusManager,
|
||||||
|
private val branchesManager: BranchesManager,
|
||||||
|
private val tagsManager: TagsManager,
|
||||||
|
private val tabState: TabState,
|
||||||
|
) {
|
||||||
|
private val _logStatus = MutableStateFlow<LogStatus>(LogStatus.Loading)
|
||||||
|
|
||||||
|
val logStatus: StateFlow<LogStatus>
|
||||||
|
get() = _logStatus
|
||||||
|
|
||||||
|
suspend fun loadLog(git: Git) {
|
||||||
|
_logStatus.value = LogStatus.Loading
|
||||||
|
|
||||||
|
val currentBranch = branchesManager.currentBranchRef(git)
|
||||||
|
val log = logManager.loadLog(git, currentBranch)
|
||||||
|
val hasUncommitedChanges = statusManager.hasUncommitedChanges(git)
|
||||||
|
_logStatus.value = LogStatus.Loaded(hasUncommitedChanges, log, currentBranch)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkoutCommit(revCommit: RevCommit) = tabState.safeProcessing { git ->
|
||||||
|
logManager.checkoutCommit(git, revCommit)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ALL_DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
fun revertCommit(revCommit: RevCommit) = tabState.safeProcessing { git ->
|
||||||
|
logManager.revertCommit(git, revCommit)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ALL_DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetToCommit(revCommit: RevCommit, resetType: ResetType) = tabState.safeProcessing { git ->
|
||||||
|
logManager.resetToCommit(git, revCommit, resetType = resetType)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ALL_DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkoutRef(ref: Ref) = tabState.safeProcessing { git ->
|
||||||
|
branchesManager.checkoutRef(git, ref)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ALL_DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun createBranchOnCommit(branch: String, revCommit: RevCommit) = tabState.safeProcessing { git ->
|
||||||
|
branchesManager.createBranchOnCommit(git, branch, revCommit)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ALL_DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createTagOnCommit(tag: String, revCommit: RevCommit) = tabState.safeProcessing { git ->
|
||||||
|
tagsManager.createTagOnCommit(git, tag, revCommit)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ALL_DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mergeBranch(ref: Ref, fastForward: Boolean) = tabState.safeProcessing { git ->
|
||||||
|
branchesManager.mergeBranch(git, ref, fastForward)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ALL_DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteBranch(branch: Ref) =tabState.safeProcessing { git ->
|
||||||
|
branchesManager.deleteBranch(git, branch)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ALL_DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteTag(tag: Ref) = tabState.safeProcessing { git ->
|
||||||
|
tagsManager.deleteTag(git, tag)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ALL_DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun refresh(git: Git) {
|
||||||
|
loadLog(git)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class LogStatus {
|
||||||
|
object Loading : LogStatus()
|
||||||
|
class Loaded(val hasUncommitedChanges: Boolean, val plotCommitList: GraphCommitList, val currentBranch: Ref?) : LogStatus()
|
||||||
|
}
|
44
src/main/kotlin/app/viewmodels/RemotesViewModel.kt
Normal file
44
src/main/kotlin/app/viewmodels/RemotesViewModel.kt
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package app.viewmodels
|
||||||
|
|
||||||
|
import app.git.BranchesManager
|
||||||
|
import app.git.RemoteInfo
|
||||||
|
import app.git.RemotesManager
|
||||||
|
import app.git.TabState
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.lib.Ref
|
||||||
|
import org.eclipse.jgit.transport.RemoteConfig
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class RemotesViewModel @Inject constructor(
|
||||||
|
private val remotesManager: RemotesManager,
|
||||||
|
private val branchesManager: BranchesManager,
|
||||||
|
) {
|
||||||
|
private val _remotes = MutableStateFlow<List<RemoteInfo>>(listOf())
|
||||||
|
val remotes: StateFlow<List<RemoteInfo>>
|
||||||
|
get() = _remotes
|
||||||
|
|
||||||
|
suspend fun loadRemotes(git: Git) = withContext(Dispatchers.IO) {
|
||||||
|
val remotes = git.remoteList()
|
||||||
|
.call()
|
||||||
|
val allRemoteBranches = branchesManager.remoteBranches(git)
|
||||||
|
|
||||||
|
remotesManager.loadRemotes(git, allRemoteBranches)
|
||||||
|
val remoteInfoList = remotes.map { remoteConfig ->
|
||||||
|
val remoteBranches = allRemoteBranches.filter { branch ->
|
||||||
|
branch.name.startsWith("refs/remotes/${remoteConfig.name}")
|
||||||
|
}
|
||||||
|
RemoteInfo(remoteConfig, remoteBranches)
|
||||||
|
}
|
||||||
|
|
||||||
|
_remotes.value = remoteInfoList
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun refresh(git: Git) = withContext(Dispatchers.IO) {
|
||||||
|
loadRemotes(git)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
124
src/main/kotlin/app/viewmodels/StatusViewModel.kt
Normal file
124
src/main/kotlin/app/viewmodels/StatusViewModel.kt
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
package app.viewmodels
|
||||||
|
|
||||||
|
import app.git.*
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.diff.DiffEntry
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class StatusViewModel @Inject constructor(
|
||||||
|
private val tabState: TabState,
|
||||||
|
private val statusManager: StatusManager,
|
||||||
|
private val branchesManager: BranchesManager,
|
||||||
|
private val repositoryManager: RepositoryManager,
|
||||||
|
) {
|
||||||
|
private val _stageStatus = MutableStateFlow<StageStatus>(StageStatus.Loaded(listOf(), listOf()))
|
||||||
|
val stageStatus: StateFlow<StageStatus> = _stageStatus
|
||||||
|
|
||||||
|
|
||||||
|
private val _hasUncommitedChanges = MutableStateFlow<Boolean>(false)
|
||||||
|
val hasUncommitedChanges: StateFlow<Boolean>
|
||||||
|
get() = _hasUncommitedChanges
|
||||||
|
|
||||||
|
fun stage(diffEntry: DiffEntry) = tabState.runOperation { git ->
|
||||||
|
statusManager.stage(git, diffEntry)
|
||||||
|
loadStatus(git)
|
||||||
|
|
||||||
|
return@runOperation RefreshType.NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unstage(diffEntry: DiffEntry) = tabState.runOperation { git ->
|
||||||
|
statusManager.unstage(git, diffEntry)
|
||||||
|
loadStatus(git)
|
||||||
|
|
||||||
|
return@runOperation RefreshType.NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun unstageAll() = tabState.safeProcessing { git ->
|
||||||
|
statusManager.unstageAll(git)
|
||||||
|
loadStatus(git)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stageAll() = tabState.safeProcessing { git ->
|
||||||
|
statusManager.stageAll(git)
|
||||||
|
loadStatus(git)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun resetStaged(diffEntry: DiffEntry) = tabState.runOperation { git ->
|
||||||
|
statusManager.reset(git, diffEntry, staged = true)
|
||||||
|
|
||||||
|
return@runOperation RefreshType.UNCOMMITED_CHANGES
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetUnstaged(diffEntry: DiffEntry) =tabState.runOperation { git ->
|
||||||
|
statusManager.reset(git, diffEntry, staged = false)
|
||||||
|
|
||||||
|
return@runOperation RefreshType.UNCOMMITED_CHANGES
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun loadStatus(git: Git) {
|
||||||
|
val previousStatus = _stageStatus.value
|
||||||
|
|
||||||
|
try {
|
||||||
|
_stageStatus.value = StageStatus.Loading
|
||||||
|
val repositoryState = repositoryManager.getRepositoryState(git)
|
||||||
|
val currentBranchRef = branchesManager.currentBranchRef(git)
|
||||||
|
val staged = statusManager.getStaged(git, currentBranchRef, repositoryState)
|
||||||
|
val unstaged = statusManager.getUnstaged(git, repositoryState)
|
||||||
|
|
||||||
|
_stageStatus.value = StageStatus.Loaded(staged, unstaged)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
_stageStatus.value = previousStatus
|
||||||
|
throw ex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun loadHasUncommitedChanges(git: Git) = withContext(Dispatchers.IO) {
|
||||||
|
_hasUncommitedChanges.value = statusManager.hasUncommitedChanges(git)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun commit(message: String) = tabState.safeProcessing { git ->
|
||||||
|
statusManager.commit(git, message)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ALL_DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun refresh(git: Git) = withContext(Dispatchers.IO) {
|
||||||
|
loadStatus(git)
|
||||||
|
loadHasUncommitedChanges(git)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if there are uncommited changes and returns if the state has changed (
|
||||||
|
*/
|
||||||
|
suspend fun updateHasUncommitedChanges(git: Git): Boolean {
|
||||||
|
val hadUncommitedChanges = hasUncommitedChanges.value
|
||||||
|
|
||||||
|
loadStatus(git)
|
||||||
|
|
||||||
|
val hasNowUncommitedChanges = hasUncommitedChanges.value
|
||||||
|
|
||||||
|
// Return true to update the log only if the uncommitedChanges status has changed
|
||||||
|
return (hasNowUncommitedChanges != hadUncommitedChanges)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class StageStatus {
|
||||||
|
object Loading : StageStatus()
|
||||||
|
data class Loaded(val staged: List<StatusEntry>, val unstaged: List<StatusEntry>) : StageStatus()
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ import app.app.newErrorNow
|
|||||||
import app.credentials.CredentialsState
|
import app.credentials.CredentialsState
|
||||||
import app.credentials.CredentialsStateManager
|
import app.credentials.CredentialsStateManager
|
||||||
import app.git.diff.Hunk
|
import app.git.diff.Hunk
|
||||||
|
import app.viewmodels.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
@ -13,7 +14,6 @@ import kotlinx.coroutines.flow.collect
|
|||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.diff.DiffEntry
|
import org.eclipse.jgit.diff.DiffEntry
|
||||||
import org.eclipse.jgit.lib.ObjectId
|
import org.eclipse.jgit.lib.ObjectId
|
||||||
import org.eclipse.jgit.lib.Ref
|
|
||||||
import org.eclipse.jgit.lib.Repository
|
import org.eclipse.jgit.lib.Repository
|
||||||
import org.eclipse.jgit.lib.RepositoryState
|
import org.eclipse.jgit.lib.RepositoryState
|
||||||
import org.eclipse.jgit.revwalk.RevCommit
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
@ -22,15 +22,18 @@ import java.io.File
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
class GitManager @Inject constructor(
|
class TabViewModel @Inject constructor(
|
||||||
private val statusManager: StatusManager,
|
val logViewModel: LogViewModel,
|
||||||
private val logManager: LogManager,
|
val branchesViewModel: BranchesViewModel,
|
||||||
|
val tagsViewModel: TagsViewModel,
|
||||||
|
val remotesViewModel: RemotesViewModel,
|
||||||
|
val statusViewModel: StatusViewModel,
|
||||||
|
val diffViewModel: DiffViewModel,
|
||||||
|
private val repositoryManager: RepositoryManager,
|
||||||
private val remoteOperationsManager: RemoteOperationsManager,
|
private val remoteOperationsManager: RemoteOperationsManager,
|
||||||
private val branchesManager: BranchesManager,
|
|
||||||
private val stashManager: StashManager,
|
private val stashManager: StashManager,
|
||||||
private val diffManager: DiffManager,
|
private val diffManager: DiffManager,
|
||||||
private val tagsManager: TagsManager,
|
private val tabState: TabState,
|
||||||
private val remotesManager: RemotesManager,
|
|
||||||
val errorsManager: ErrorsManager,
|
val errorsManager: ErrorsManager,
|
||||||
val appStateManager: AppStateManager,
|
val appStateManager: AppStateManager,
|
||||||
private val fileChangesWatcher: FileChangesWatcher,
|
private val fileChangesWatcher: FileChangesWatcher,
|
||||||
@ -50,22 +53,31 @@ class GitManager @Inject constructor(
|
|||||||
val processing: StateFlow<Boolean>
|
val processing: StateFlow<Boolean>
|
||||||
get() = _processing
|
get() = _processing
|
||||||
|
|
||||||
private val _lastTimeChecked = MutableStateFlow(System.currentTimeMillis())
|
|
||||||
val lastTimeChecked: StateFlow<Long>
|
|
||||||
get() = _lastTimeChecked
|
|
||||||
|
|
||||||
val stageStatus: StateFlow<StageStatus> = statusManager.stageStatus
|
|
||||||
val repositoryState: StateFlow<RepositoryState> = statusManager.repositoryState
|
|
||||||
val logStatus: StateFlow<LogStatus> = logManager.logStatus
|
|
||||||
val branches: StateFlow<List<Ref>> = branchesManager.branches
|
|
||||||
val tags: StateFlow<List<Ref>> = tagsManager.tags
|
|
||||||
val currentBranch: StateFlow<String> = branchesManager.currentBranch
|
|
||||||
val stashStatus: StateFlow<StashStatus> = stashManager.stashStatus
|
val stashStatus: StateFlow<StashStatus> = stashManager.stashStatus
|
||||||
val credentialsState: StateFlow<CredentialsState> = credentialsStateManager.credentialsState
|
val credentialsState: StateFlow<CredentialsState> = credentialsStateManager.credentialsState
|
||||||
val cloneStatus: StateFlow<CloneStatus> = remoteOperationsManager.cloneStatus
|
val cloneStatus: StateFlow<CloneStatus> = remoteOperationsManager.cloneStatus
|
||||||
val remotes: StateFlow<List<RemoteInfo>> = remotesManager.remotes
|
|
||||||
|
|
||||||
private var git: Git? = null
|
private val _repositoryState = MutableStateFlow(RepositoryState.SAFE)
|
||||||
|
val repositoryState: StateFlow<RepositoryState> = _repositoryState
|
||||||
|
|
||||||
|
init {
|
||||||
|
managerScope.launch {
|
||||||
|
tabState.refreshData.collect { refreshType ->
|
||||||
|
when (refreshType) {
|
||||||
|
RefreshType.NONE -> println("Not refreshing...")
|
||||||
|
RefreshType.ALL_DATA -> refreshRepositoryInfo()
|
||||||
|
RefreshType.ONLY_LOG -> refreshLog()
|
||||||
|
RefreshType.UNCOMMITED_CHANGES -> checkUncommitedChanges()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshLog() = tabState.runOperation { git ->
|
||||||
|
logViewModel.refresh(git)
|
||||||
|
|
||||||
|
return@runOperation RefreshType.NONE
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Property that indicates if a git operation is running
|
* Property that indicates if a git operation is running
|
||||||
@ -74,7 +86,7 @@ class GitManager @Inject constructor(
|
|||||||
|
|
||||||
private val safeGit: Git
|
private val safeGit: Git
|
||||||
get() {
|
get() {
|
||||||
val git = this.git
|
val git = this.tabState.git
|
||||||
if (git == null) {
|
if (git == null) {
|
||||||
_repositorySelectionStatus.value = RepositorySelectionStatus.None
|
_repositorySelectionStatus.value = RepositorySelectionStatus.None
|
||||||
throw CancellationException()
|
throw CancellationException()
|
||||||
@ -110,13 +122,15 @@ class GitManager @Inject constructor(
|
|||||||
try {
|
try {
|
||||||
repository.workTree // test if repository is valid
|
repository.workTree // test if repository is valid
|
||||||
_repositorySelectionStatus.value = RepositorySelectionStatus.Open(repository)
|
_repositorySelectionStatus.value = RepositorySelectionStatus.Open(repository)
|
||||||
git = Git(repository)
|
tabState.git = Git(repository)
|
||||||
|
|
||||||
onRepositoryChanged(repository.directory.parent)
|
onRepositoryChanged(repository.directory.parent)
|
||||||
refreshRepositoryInfo()
|
refreshRepositoryInfo()
|
||||||
launch {
|
launch {
|
||||||
watchRepositoryChanges()
|
watchRepositoryChanges()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println("AppStateManagerReference $appStateManager")
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
ex.printStackTrace()
|
ex.printStackTrace()
|
||||||
onRepositoryChanged(null)
|
onRepositoryChanged(null)
|
||||||
@ -125,6 +139,10 @@ class GitManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun loadRepositoryState(git: Git) = withContext(Dispatchers.IO) {
|
||||||
|
_repositoryState.value = repositoryManager.getRepositoryState(git)
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun watchRepositoryChanges() {
|
private suspend fun watchRepositoryChanges() {
|
||||||
val ignored = safeGit.status().call().ignoredNotInIndex.toList()
|
val ignored = safeGit.status().call().ignoredNotInIndex.toList()
|
||||||
|
|
||||||
@ -135,78 +153,35 @@ class GitManager @Inject constructor(
|
|||||||
if (!operationRunning) { // Only update if there isn't any process running
|
if (!operationRunning) { // Only update if there isn't any process running
|
||||||
safeProcessing(showError = false) {
|
safeProcessing(showError = false) {
|
||||||
println("Changes detected, loading status")
|
println("Changes detected, loading status")
|
||||||
val hasUncommitedChanges = statusManager.hasUncommitedChanges.value
|
// val hasUncommitedChanges = statusManager.hasUncommitedChanges.value
|
||||||
statusManager.loadHasUncommitedChanges(safeGit)
|
// statusManager.loadHasUncommitedChanges(safeGit)
|
||||||
statusManager.loadStatus(safeGit)
|
// statusManager.loadStatus(safeGit)
|
||||||
|
|
||||||
if(!hasUncommitedChanges) {
|
statusViewModel.refresh(safeGit)
|
||||||
logManager.loadLog(safeGit)
|
checkUncommitedChanges()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadLog() = managerScope.launch {
|
private suspend fun loadLog() {
|
||||||
coLoadLog()
|
logViewModel.loadLog(safeGit)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun coLoadLog() {
|
suspend fun checkUncommitedChanges() {
|
||||||
logManager.loadLog(safeGit)
|
val uncommitedChangesStateChanged = statusViewModel.updateHasUncommitedChanges(safeGit)
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun loadStatus() {
|
|
||||||
val hadUncommitedChanges = statusManager.hasUncommitedChanges.value
|
|
||||||
|
|
||||||
statusManager.loadStatus(safeGit)
|
|
||||||
|
|
||||||
val hasNowUncommitedChanges = statusManager.hasUncommitedChanges.value
|
|
||||||
|
|
||||||
// Update the log only if the uncommitedChanges status has changed
|
// Update the log only if the uncommitedChanges status has changed
|
||||||
if (hasNowUncommitedChanges != hadUncommitedChanges)
|
if (uncommitedChangesStateChanged)
|
||||||
coLoadLog()
|
loadLog()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stage(diffEntry: DiffEntry) = managerScope.launch {
|
|
||||||
runOperation {
|
|
||||||
statusManager.stage(safeGit, diffEntry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stageHunk(diffEntry: DiffEntry, hunk: Hunk) = managerScope.launch {
|
|
||||||
runOperation {
|
|
||||||
statusManager.stageHunk(safeGit, diffEntry, hunk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unstageHunk(diffEntry: DiffEntry, hunk: Hunk) = managerScope.launch {
|
|
||||||
runOperation {
|
|
||||||
statusManager.unstageHunk(safeGit, diffEntry, hunk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unstage(diffEntry: DiffEntry) = managerScope.launch {
|
|
||||||
runOperation {
|
|
||||||
statusManager.unstage(safeGit, diffEntry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun commit(message: String) = managerScope.launch {
|
|
||||||
safeProcessing {
|
|
||||||
statusManager.commit(safeGit, message)
|
|
||||||
refreshRepositoryInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val hasUncommitedChanges: StateFlow<Boolean>
|
|
||||||
get() = statusManager.hasUncommitedChanges
|
|
||||||
|
|
||||||
suspend fun diffFormat(diffEntryType: DiffEntryType): List<Hunk> {
|
suspend fun diffFormat(diffEntryType: DiffEntryType): List<Hunk> {
|
||||||
try {
|
try {
|
||||||
return diffManager.diffFormat(safeGit, diffEntryType)
|
return diffManager.diffFormat(safeGit, diffEntryType)
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
ex.printStackTrace()
|
ex.printStackTrace()
|
||||||
loadStatus()
|
checkUncommitedChanges()
|
||||||
return listOf()
|
return listOf()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,7 +189,7 @@ class GitManager @Inject constructor(
|
|||||||
fun pull() = managerScope.launch {
|
fun pull() = managerScope.launch {
|
||||||
safeProcessing {
|
safeProcessing {
|
||||||
remoteOperationsManager.pull(safeGit)
|
remoteOperationsManager.pull(safeGit)
|
||||||
coLoadLog()
|
loadLog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,26 +198,27 @@ class GitManager @Inject constructor(
|
|||||||
try {
|
try {
|
||||||
remoteOperationsManager.push(safeGit)
|
remoteOperationsManager.push(safeGit)
|
||||||
} finally {
|
} finally {
|
||||||
coLoadLog()
|
loadLog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun refreshRepositoryInfo() {
|
private suspend fun refreshRepositoryInfo() {
|
||||||
statusManager.loadRepositoryStatus(safeGit)
|
logViewModel.refresh(safeGit)
|
||||||
statusManager.loadHasUncommitedChanges(safeGit)
|
branchesViewModel.refresh(safeGit)
|
||||||
statusManager.loadStatus(safeGit)
|
remotesViewModel.refresh(safeGit)
|
||||||
branchesManager.loadBranches(safeGit)
|
tagsViewModel.refresh(safeGit)
|
||||||
remotesManager.loadRemotes(safeGit, branchesManager.remoteBranches(safeGit))
|
statusViewModel.refresh(safeGit)
|
||||||
tagsManager.loadTags(safeGit)
|
loadRepositoryState(safeGit)
|
||||||
|
|
||||||
stashManager.loadStashList(safeGit)
|
stashManager.loadStashList(safeGit)
|
||||||
coLoadLog()
|
loadLog()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stash() = managerScope.launch {
|
fun stash() = managerScope.launch {
|
||||||
safeProcessing {
|
safeProcessing {
|
||||||
stashManager.stash(safeGit)
|
stashManager.stash(safeGit)
|
||||||
loadStatus()
|
checkUncommitedChanges()
|
||||||
loadLog()
|
loadLog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -250,42 +226,11 @@ class GitManager @Inject constructor(
|
|||||||
fun popStash() = managerScope.launch {
|
fun popStash() = managerScope.launch {
|
||||||
safeProcessing {
|
safeProcessing {
|
||||||
stashManager.popStash(safeGit)
|
stashManager.popStash(safeGit)
|
||||||
loadStatus()
|
checkUncommitedChanges()
|
||||||
loadLog()
|
loadLog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createBranch(branchName: String) = managerScope.launch {
|
|
||||||
safeProcessing {
|
|
||||||
branchesManager.createBranch(safeGit, branchName)
|
|
||||||
coLoadLog()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun deleteBranch(branch: Ref) = managerScope.launch {
|
|
||||||
safeProcessing {
|
|
||||||
branchesManager.deleteBranch(safeGit, branch)
|
|
||||||
refreshRepositoryInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun deleteTag(tag: Ref) = managerScope.launch {
|
|
||||||
safeProcessing {
|
|
||||||
tagsManager.deleteTag(safeGit, tag)
|
|
||||||
refreshRepositoryInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun resetStaged(diffEntry: DiffEntry) = managerScope.launch {
|
|
||||||
statusManager.reset(safeGit, diffEntry, staged = true)
|
|
||||||
loadLog()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun resetUnstaged(diffEntry: DiffEntry) = managerScope.launch {
|
|
||||||
statusManager.reset(safeGit, diffEntry, staged = false)
|
|
||||||
loadLog()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun credentialsDenied() {
|
fun credentialsDenied() {
|
||||||
credentialsStateManager.updateState(CredentialsState.CredentialsDenied)
|
credentialsStateManager.updateState(CredentialsState.CredentialsDenied)
|
||||||
}
|
}
|
||||||
@ -302,68 +247,8 @@ class GitManager @Inject constructor(
|
|||||||
return diffManager.commitDiffEntries(safeGit, commit)
|
return diffManager.commitDiffEntries(safeGit, commit)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unstageAll() = managerScope.launch {
|
|
||||||
safeProcessing {
|
|
||||||
statusManager.unstageAll(safeGit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stageAll() = managerScope.launch {
|
|
||||||
safeProcessing {
|
|
||||||
statusManager.stageAll(safeGit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun checkoutCommit(revCommit: RevCommit) = managerScope.launch {
|
|
||||||
safeProcessing {
|
|
||||||
logManager.checkoutCommit(safeGit, revCommit)
|
|
||||||
refreshRepositoryInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun revertCommit(revCommit: RevCommit) = managerScope.launch {
|
|
||||||
safeProcessing {
|
|
||||||
logManager.revertCommit(safeGit, revCommit)
|
|
||||||
refreshRepositoryInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun resetToCommit(revCommit: RevCommit, resetType: ResetType) = managerScope.launch {
|
|
||||||
safeProcessing {
|
|
||||||
logManager.resetToCommit(safeGit, revCommit, resetType = resetType)
|
|
||||||
refreshRepositoryInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createBranchOnCommit(branch: String, revCommit: RevCommit) = managerScope.launch {
|
|
||||||
safeProcessing {
|
|
||||||
branchesManager.createBranchOnCommit(safeGit, branch, revCommit)
|
|
||||||
refreshRepositoryInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createTagOnCommit(tag: String, revCommit: RevCommit) = managerScope.launch {
|
|
||||||
safeProcessing {
|
|
||||||
tagsManager.createTagOnCommit(safeGit, tag, revCommit)
|
|
||||||
refreshRepositoryInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var onRepositoryChanged: (path: String?) -> Unit = {}
|
var onRepositoryChanged: (path: String?) -> Unit = {}
|
||||||
|
|
||||||
fun checkoutRef(ref: Ref) = managerScope.launch {
|
|
||||||
safeProcessing {
|
|
||||||
logManager.checkoutRef(safeGit, ref)
|
|
||||||
refreshRepositoryInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun mergeBranch(ref: Ref, fastForward: Boolean) = managerScope.launch {
|
|
||||||
safeProcessing {
|
|
||||||
branchesManager.mergeBranch(safeGit, ref, fastForward)
|
|
||||||
refreshRepositoryInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun dispose() {
|
fun dispose() {
|
||||||
managerScope.cancel()
|
managerScope.cancel()
|
||||||
@ -377,7 +262,6 @@ class GitManager @Inject constructor(
|
|||||||
return safeGit.repository.parseCommit(objectId)
|
return safeGit.repository.parseCommit(objectId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
private suspend fun safeProcessing(showError: Boolean = true, callback: suspend () -> Unit) {
|
private suspend fun safeProcessing(showError: Boolean = true, callback: suspend () -> Unit) {
|
||||||
_processing.value = true
|
_processing.value = true
|
||||||
operationRunning = true
|
operationRunning = true
|
||||||
@ -395,13 +279,10 @@ class GitManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun runOperation(block: () -> Unit) {
|
fun updatedDiffEntry(diffSelected: DiffEntryType) = tabState.runOperation { git ->
|
||||||
operationRunning = true
|
diffViewModel.updateDiff(git , diffSelected)
|
||||||
try {
|
|
||||||
block()
|
return@runOperation RefreshType.NONE
|
||||||
} finally {
|
|
||||||
operationRunning = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
45
src/main/kotlin/app/viewmodels/TagsViewModel.kt
Normal file
45
src/main/kotlin/app/viewmodels/TagsViewModel.kt
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package app.viewmodels
|
||||||
|
|
||||||
|
import app.git.BranchesManager
|
||||||
|
import app.git.RefreshType
|
||||||
|
import app.git.TabState
|
||||||
|
import app.git.TagsManager
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
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 TagsViewModel @Inject constructor(
|
||||||
|
private val tabState: TabState,
|
||||||
|
private val branchesManager: BranchesManager,
|
||||||
|
private val tagsManager: TagsManager,
|
||||||
|
) {
|
||||||
|
private val _tags = MutableStateFlow<List<Ref>>(listOf())
|
||||||
|
val tags: StateFlow<List<Ref>>
|
||||||
|
get() = _tags
|
||||||
|
|
||||||
|
suspend fun loadTags(git: Git) = withContext(Dispatchers.IO) {
|
||||||
|
val tagsList = tagsManager.getTags(git)
|
||||||
|
|
||||||
|
_tags.value = tagsList
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkoutRef(ref: Ref) = tabState.safeProcessing { git ->
|
||||||
|
branchesManager.checkoutRef(git, ref)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ALL_DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteTag(tag: Ref) = tabState.safeProcessing { git ->
|
||||||
|
tagsManager.deleteTag(git, tag)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ALL_DATA
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun refresh(git: Git) {
|
||||||
|
loadTags(git)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user