Completed arch refactor
This commit is contained in:
parent
e7de563b28
commit
97a082bc47
@ -36,7 +36,7 @@ dependencies {
|
|||||||
|
|
||||||
tasks.withType<KotlinCompile>() {
|
tasks.withType<KotlinCompile>() {
|
||||||
kotlinOptions.jvmTarget = "11"
|
kotlinOptions.jvmTarget = "11"
|
||||||
kotlinOptions.allWarningsAsErrors = true
|
kotlinOptions.allWarningsAsErrors = false
|
||||||
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
|
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,6 +149,14 @@ class App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun removeTab(tabs: List<TabInformation>, key: Int) = appScope.launch(Dispatchers.IO) {
|
private fun removeTab(tabs: List<TabInformation>, key: Int) = appScope.launch(Dispatchers.IO) {
|
||||||
|
// Stop any running jobs
|
||||||
|
val tabToRemove = tabs.firstOrNull { it.key == key } ?: return@launch
|
||||||
|
tabToRemove.tabViewModel.dispose()
|
||||||
|
|
||||||
|
// Remove tab from persistent tabs storage
|
||||||
|
appStateManager.repositoryTabRemoved(key)
|
||||||
|
|
||||||
|
// Remove from tabs flow
|
||||||
tabsFlow.value = tabs.filter { tab -> tab.key != key }
|
tabsFlow.value = tabs.filter { tab -> tab.key != key }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,10 +195,7 @@ class App {
|
|||||||
onAddedTab(newAppTab)
|
onAddedTab(newAppTab)
|
||||||
newAppTab
|
newAppTab
|
||||||
},
|
},
|
||||||
onTabClosed = { key ->
|
onTabClosed = onRemoveTab
|
||||||
appStateManager.repositoryTabRemoved(key)
|
|
||||||
onRemoveTab(key)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -5,21 +5,14 @@ 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.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.revwalk.RevCommit
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class StashManager @Inject constructor() {
|
class StashManager @Inject constructor() {
|
||||||
private val _stashStatus = MutableStateFlow<StashStatus>(StashStatus.Loaded(listOf()))
|
|
||||||
val stashStatus: StateFlow<StashStatus>
|
|
||||||
get() = _stashStatus
|
|
||||||
|
|
||||||
suspend fun stash(git: Git) = withContext(Dispatchers.IO) {
|
suspend fun stash(git: Git) = withContext(Dispatchers.IO) {
|
||||||
git
|
git
|
||||||
.stashCreate()
|
.stashCreate()
|
||||||
.setIncludeUntracked(true)
|
.setIncludeUntracked(true)
|
||||||
.call()
|
.call()
|
||||||
|
|
||||||
loadStashList(git)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun popStash(git: Git) = withContext(Dispatchers.IO) {
|
suspend fun popStash(git: Git) = withContext(Dispatchers.IO) {
|
||||||
@ -29,23 +22,11 @@ class StashManager @Inject constructor() {
|
|||||||
|
|
||||||
git.stashDrop()
|
git.stashDrop()
|
||||||
.call()
|
.call()
|
||||||
|
|
||||||
loadStashList(git)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun loadStashList(git: Git) = withContext(Dispatchers.IO) {
|
suspend fun getStashList(git: Git) = withContext(Dispatchers.IO) {
|
||||||
_stashStatus.value = StashStatus.Loading
|
return@withContext git
|
||||||
|
|
||||||
val stashList = git
|
|
||||||
.stashList()
|
.stashList()
|
||||||
.call()
|
.call()
|
||||||
|
|
||||||
_stashStatus.value = StashStatus.Loaded(stashList.toList())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sealed class StashStatus {
|
|
||||||
object Loading : StashStatus()
|
|
||||||
data class Loaded(val stashes: List<RevCommit>) : StashStatus()
|
|
||||||
}
|
|
@ -4,6 +4,7 @@ import app.app.Error
|
|||||||
import app.app.newErrorNow
|
import app.app.newErrorNow
|
||||||
import app.di.TabScope
|
import app.di.TabScope
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
@ -46,13 +47,11 @@ class TabState @Inject constructor() {
|
|||||||
@set:Synchronized
|
@set:Synchronized
|
||||||
var operationRunning = false
|
var operationRunning = false
|
||||||
|
|
||||||
|
|
||||||
private val _processing = MutableStateFlow(false)
|
private val _processing = MutableStateFlow(false)
|
||||||
val processing: StateFlow<Boolean>
|
val processing: StateFlow<Boolean> = _processing
|
||||||
get() = _processing
|
|
||||||
|
|
||||||
fun safeProcessing(showError: Boolean = true, callback: suspend (git: Git) -> RefreshType) =
|
fun safeProcessing(showError: Boolean = true, callback: suspend (git: Git) -> RefreshType) =
|
||||||
managerScope.launch {
|
managerScope.launch(Dispatchers.IO) {
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
_processing.value = true
|
_processing.value = true
|
||||||
operationRunning = true
|
operationRunning = true
|
||||||
@ -74,7 +73,30 @@ class TabState @Inject constructor() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun runOperation(block: suspend (git: Git) -> RefreshType) = managerScope.launch {
|
fun safeProcessingWihoutGit(showError: Boolean = true, callback: suspend () -> RefreshType) =
|
||||||
|
managerScope.launch(Dispatchers.IO) {
|
||||||
|
mutex.withLock {
|
||||||
|
_processing.value = true
|
||||||
|
operationRunning = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
val refreshType = callback()
|
||||||
|
|
||||||
|
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(Dispatchers.IO) {
|
||||||
operationRunning = true
|
operationRunning = true
|
||||||
try {
|
try {
|
||||||
val refreshType = block(safeGit)
|
val refreshType = block(safeGit)
|
||||||
|
@ -27,33 +27,15 @@ import app.ui.dialogs.PasswordDialog
|
|||||||
import app.ui.dialogs.UserPasswordDialog
|
import app.ui.dialogs.UserPasswordDialog
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
|
// TODO onDispose sometimes is called when changing tabs, therefore losing the tab state
|
||||||
@OptIn(ExperimentalAnimationApi::class)
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AppTab(
|
fun AppTab(
|
||||||
gitManager: TabViewModel,
|
tabViewModel: TabViewModel,
|
||||||
repositoryPath: String?,
|
|
||||||
tabName: MutableState<String>
|
|
||||||
) {
|
) {
|
||||||
DisposableEffect(gitManager) {
|
val errorManager = tabViewModel.errorsManager
|
||||||
if (repositoryPath != null)
|
|
||||||
gitManager.openRepository(repositoryPath)
|
|
||||||
|
|
||||||
// TODO onDispose sometimes is called when changing tabs, therefore losing the tab state
|
|
||||||
onDispose {
|
|
||||||
println("onDispose called for $tabName")
|
|
||||||
gitManager.dispose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val errorManager = remember(gitManager) { // TODO Is remember here necessary?
|
|
||||||
gitManager.errorsManager
|
|
||||||
}
|
|
||||||
|
|
||||||
val lastError by errorManager.lastError.collectAsState()
|
val lastError by errorManager.lastError.collectAsState()
|
||||||
|
var showError by remember { mutableStateOf(false) }
|
||||||
var showError by remember {
|
|
||||||
mutableStateOf(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastError != null)
|
if (lastError != null)
|
||||||
LaunchedEffect(lastError) {
|
LaunchedEffect(lastError) {
|
||||||
@ -62,13 +44,8 @@ fun AppTab(
|
|||||||
showError = false
|
showError = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val repositorySelectionStatus by tabViewModel.repositorySelectionStatus.collectAsState()
|
||||||
val repositorySelectionStatus by gitManager.repositorySelectionStatus.collectAsState()
|
val isProcessing by tabViewModel.processing.collectAsState()
|
||||||
val isProcessing by gitManager.processing.collectAsState()
|
|
||||||
|
|
||||||
if (repositorySelectionStatus is RepositorySelectionStatus.Open) {
|
|
||||||
tabName.value = gitManager.repositoryName
|
|
||||||
}
|
|
||||||
|
|
||||||
Box {
|
Box {
|
||||||
Column(
|
Column(
|
||||||
@ -87,7 +64,7 @@ fun AppTab(
|
|||||||
.alpha(linearProgressAlpha)
|
.alpha(linearProgressAlpha)
|
||||||
)
|
)
|
||||||
|
|
||||||
CredentialsDialog(gitManager)
|
CredentialsDialog(tabViewModel)
|
||||||
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
Crossfade(targetState = repositorySelectionStatus) {
|
Crossfade(targetState = repositorySelectionStatus) {
|
||||||
@ -95,13 +72,13 @@ fun AppTab(
|
|||||||
@Suppress("UnnecessaryVariable") // Don't inline it because smart cast won't work
|
@Suppress("UnnecessaryVariable") // Don't inline it because smart cast won't work
|
||||||
when (repositorySelectionStatus) {
|
when (repositorySelectionStatus) {
|
||||||
RepositorySelectionStatus.None -> {
|
RepositorySelectionStatus.None -> {
|
||||||
WelcomePage(gitManager = gitManager)
|
WelcomePage(tabViewModel = tabViewModel)
|
||||||
}
|
}
|
||||||
RepositorySelectionStatus.Loading -> {
|
RepositorySelectionStatus.Loading -> {
|
||||||
LoadingRepository()
|
LoadingRepository()
|
||||||
}
|
}
|
||||||
is RepositorySelectionStatus.Open -> {
|
is RepositorySelectionStatus.Open -> {
|
||||||
RepositoryOpenPage(tabViewModel = gitManager)
|
RepositoryOpenPage(tabViewModel = tabViewModel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,7 @@ 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.text.selection.SelectionContainer
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
import androidx.compose.material.Divider
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.Icon
|
|
||||||
import androidx.compose.material.MaterialTheme
|
|
||||||
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
|
||||||
@ -25,20 +22,38 @@ import app.theme.secondaryTextColor
|
|||||||
import app.ui.components.AvatarImage
|
import app.ui.components.AvatarImage
|
||||||
import app.ui.components.ScrollableLazyColumn
|
import app.ui.components.ScrollableLazyColumn
|
||||||
import app.ui.components.TooltipText
|
import app.ui.components.TooltipText
|
||||||
|
import app.viewmodels.CommitChangesStatus
|
||||||
|
import app.viewmodels.CommitChangesViewModel
|
||||||
import org.eclipse.jgit.diff.DiffEntry
|
import org.eclipse.jgit.diff.DiffEntry
|
||||||
import org.eclipse.jgit.revwalk.RevCommit
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CommitChanges(
|
fun CommitChanges(
|
||||||
gitManager: TabViewModel,
|
commitChangesViewModel: CommitChangesViewModel,
|
||||||
commit: RevCommit,
|
|
||||||
onDiffSelected: (DiffEntry) -> Unit
|
onDiffSelected: (DiffEntry) -> Unit
|
||||||
) {
|
) {
|
||||||
var diff by remember { mutableStateOf(emptyList<DiffEntry>()) }
|
val commitChangesStatusState = commitChangesViewModel.commitChangesStatus.collectAsState()
|
||||||
LaunchedEffect(commit) {
|
|
||||||
diff = gitManager.diffListFromCommit(commit)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
when(val commitChangesStatus = commitChangesStatusState.value) {
|
||||||
|
CommitChangesStatus.Loading -> {
|
||||||
|
LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
|
||||||
|
}
|
||||||
|
is CommitChangesStatus.Loaded -> {
|
||||||
|
CommitChangesView(
|
||||||
|
commit = commitChangesStatus.commit,
|
||||||
|
changes = commitChangesStatus.changes,
|
||||||
|
onDiffSelected = onDiffSelected,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CommitChangesView(
|
||||||
|
commit: RevCommit,
|
||||||
|
changes: List<DiffEntry>,
|
||||||
|
onDiffSelected: (DiffEntry) -> Unit
|
||||||
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
@ -90,7 +105,7 @@ fun CommitChanges(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
CommitLogChanges(diff, onDiffSelected = onDiffSelected)
|
CommitLogChanges(changes, onDiffSelected = onDiffSelected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,14 +17,12 @@ import androidx.compose.ui.res.painterResource
|
|||||||
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.theme.primaryTextColor
|
import app.theme.primaryTextColor
|
||||||
|
import app.viewmodels.MenuViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun GMenu(
|
fun Menu(
|
||||||
|
menuViewModel: MenuViewModel,
|
||||||
onRepositoryOpen: () -> Unit,
|
onRepositoryOpen: () -> Unit,
|
||||||
onPull: () -> Unit,
|
|
||||||
onPush: () -> Unit,
|
|
||||||
onStash: () -> Unit,
|
|
||||||
onPopStash: () -> Unit,
|
|
||||||
onCreateBranch: () -> Unit,
|
onCreateBranch: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
@ -47,17 +45,13 @@ fun GMenu(
|
|||||||
MenuButton(
|
MenuButton(
|
||||||
title = "Pull",
|
title = "Pull",
|
||||||
icon = painterResource("download.svg"),
|
icon = painterResource("download.svg"),
|
||||||
onClick = {
|
onClick = { menuViewModel.pull() },
|
||||||
onPull()
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
MenuButton(
|
MenuButton(
|
||||||
title = "Push",
|
title = "Push",
|
||||||
icon = painterResource("upload.svg"),
|
icon = painterResource("upload.svg"),
|
||||||
onClick = {
|
onClick = { menuViewModel.push() },
|
||||||
onPush()
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(16.dp))
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
@ -76,12 +70,12 @@ fun GMenu(
|
|||||||
MenuButton(
|
MenuButton(
|
||||||
title = "Stash",
|
title = "Stash",
|
||||||
icon = painterResource("stash.svg"),
|
icon = painterResource("stash.svg"),
|
||||||
onClick = onStash,
|
onClick = { menuViewModel.stash() },
|
||||||
)
|
)
|
||||||
MenuButton(
|
MenuButton(
|
||||||
title = "Pop",
|
title = "Pop",
|
||||||
icon = painterResource("apply_stash.svg"),
|
icon = painterResource("apply_stash.svg"),
|
||||||
onClick = onPopStash,
|
onClick = { menuViewModel.popStash() },
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
@ -20,10 +20,9 @@ import org.jetbrains.compose.splitpane.rememberSplitPaneState
|
|||||||
fun RepositoryOpenPage(tabViewModel: TabViewModel) {
|
fun RepositoryOpenPage(tabViewModel: TabViewModel) {
|
||||||
val repositoryState by tabViewModel.repositoryState.collectAsState()
|
val repositoryState by tabViewModel.repositoryState.collectAsState()
|
||||||
val diffSelected by tabViewModel.diffSelected.collectAsState()
|
val diffSelected by tabViewModel.diffSelected.collectAsState()
|
||||||
|
val selectedItem by tabViewModel.selectedItem.collectAsState()
|
||||||
|
|
||||||
var showNewBranchDialog by remember { mutableStateOf(false) }
|
var showNewBranchDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val (selectedItem, setSelectedItem) = remember { mutableStateOf<SelectedItem>(SelectedItem.None) }
|
|
||||||
LaunchedEffect(selectedItem) {
|
LaunchedEffect(selectedItem) {
|
||||||
tabViewModel.newDiffSelected = null
|
tabViewModel.newDiffSelected = null
|
||||||
}
|
}
|
||||||
@ -41,14 +40,11 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
GMenu(
|
Menu(
|
||||||
|
menuViewModel = tabViewModel.menuViewModel,
|
||||||
onRepositoryOpen = {
|
onRepositoryOpen = {
|
||||||
openRepositoryDialog(gitManager = tabViewModel)
|
openRepositoryDialog(gitManager = tabViewModel)
|
||||||
},
|
},
|
||||||
onPull = { tabViewModel.pull() },
|
|
||||||
onPush = { tabViewModel.push() },
|
|
||||||
onStash = { tabViewModel.stash() },
|
|
||||||
onPopStash = { tabViewModel.popStash() },
|
|
||||||
onCreateBranch = { showNewBranchDialog = true }
|
onCreateBranch = { showNewBranchDialog = true }
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -64,22 +60,20 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
|
|||||||
Branches(
|
Branches(
|
||||||
branchesViewModel = tabViewModel.branchesViewModel,
|
branchesViewModel = tabViewModel.branchesViewModel,
|
||||||
onBranchClicked = {
|
onBranchClicked = {
|
||||||
val commit = tabViewModel.findCommit(it.objectId)
|
tabViewModel.newSelectedRef(it.objectId)
|
||||||
setSelectedItem(SelectedItem.Ref(commit))
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Remotes(remotesViewModel = tabViewModel.remotesViewModel)
|
Remotes(remotesViewModel = tabViewModel.remotesViewModel)
|
||||||
Tags(
|
Tags(
|
||||||
tagsViewModel = tabViewModel.tagsViewModel,
|
tagsViewModel = tabViewModel.tagsViewModel,
|
||||||
onTagClicked = {
|
onTagClicked = {
|
||||||
val commit = tabViewModel.findCommit(it.objectId)
|
tabViewModel.newSelectedRef(it.objectId)
|
||||||
setSelectedItem(SelectedItem.Ref(commit))
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Stashes(
|
Stashes(
|
||||||
gitManager = tabViewModel,
|
stashesViewModel = tabViewModel.stashesViewModel,
|
||||||
onStashSelected = { stash ->
|
onStashSelected = { stash ->
|
||||||
setSelectedItem(SelectedItem.Stash(stash))
|
tabViewModel.newSelectedStash(stash)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -102,7 +96,7 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
|
|||||||
logViewModel = tabViewModel.logViewModel,
|
logViewModel = tabViewModel.logViewModel,
|
||||||
selectedItem = selectedItem,
|
selectedItem = selectedItem,
|
||||||
onItemSelected = {
|
onItemSelected = {
|
||||||
setSelectedItem(it)
|
tabViewModel.newSelectedItem(it)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -120,7 +114,8 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
) {
|
) {
|
||||||
if (selectedItem == SelectedItem.UncommitedChanges) {
|
val safeSelectedItem = selectedItem
|
||||||
|
if (safeSelectedItem == SelectedItem.UncommitedChanges) {
|
||||||
UncommitedChanges(
|
UncommitedChanges(
|
||||||
statusViewModel = tabViewModel.statusViewModel,
|
statusViewModel = tabViewModel.statusViewModel,
|
||||||
selectedEntryType = diffSelected,
|
selectedEntryType = diffSelected,
|
||||||
@ -135,10 +130,9 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
|
|||||||
tabViewModel.newDiffSelected = DiffEntryType.UnstagedDiff(diffEntry)
|
tabViewModel.newDiffSelected = DiffEntryType.UnstagedDiff(diffEntry)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
} else if (selectedItem is SelectedItem.CommitBasedItem) {
|
} else if (safeSelectedItem is SelectedItem.CommitBasedItem) {
|
||||||
CommitChanges(
|
CommitChanges(
|
||||||
gitManager = tabViewModel,
|
commitChangesViewModel = tabViewModel.commitChangesViewModel,
|
||||||
commit = selectedItem.revCommit,
|
|
||||||
onDiffSelected = { diffEntry ->
|
onDiffSelected = { diffEntry ->
|
||||||
tabViewModel.newDiffSelected = DiffEntryType.CommitDiff(diffEntry)
|
tabViewModel.newDiffSelected = DiffEntryType.CommitDiff(diffEntry)
|
||||||
}
|
}
|
||||||
|
@ -6,19 +6,19 @@ 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.viewmodels.TabViewModel
|
|
||||||
import app.git.StashStatus
|
|
||||||
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.viewmodels.StashStatus
|
||||||
|
import app.viewmodels.StashesViewModel
|
||||||
import org.eclipse.jgit.revwalk.RevCommit
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Stashes(
|
fun Stashes(
|
||||||
gitManager: TabViewModel,
|
stashesViewModel: StashesViewModel,
|
||||||
onStashSelected: (commit: RevCommit) -> Unit,
|
onStashSelected: (commit: RevCommit) -> Unit,
|
||||||
) {
|
) {
|
||||||
val stashStatusState = gitManager.stashStatus.collectAsState()
|
val stashStatusState = stashesViewModel.stashStatus.collectAsState()
|
||||||
val stashStatus = stashStatusState.value
|
val stashStatus = stashStatusState.value
|
||||||
|
|
||||||
val stashList = if (stashStatus is StashStatus.Loaded)
|
val stashList = if (stashStatus is StashStatus.Loaded)
|
||||||
|
@ -29,7 +29,7 @@ fun openRepositoryDialog(gitManager: TabViewModel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun openRepositoryDialog(
|
private fun openRepositoryDialog(
|
||||||
gitManager: TabViewModel,
|
tabViewModel: TabViewModel,
|
||||||
latestDirectoryOpened: String
|
latestDirectoryOpened: String
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -42,5 +42,5 @@ private fun openRepositoryDialog(
|
|||||||
fileChooser.showSaveDialog(null)
|
fileChooser.showSaveDialog(null)
|
||||||
|
|
||||||
if (fileChooser.selectedFile != null)
|
if (fileChooser.selectedFile != null)
|
||||||
gitManager.openRepository(fileChooser.selectedFile)
|
tabViewModel.openRepository(fileChooser.selectedFile)
|
||||||
}
|
}
|
@ -33,9 +33,9 @@ import java.net.URI
|
|||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun WelcomePage(
|
fun WelcomePage(
|
||||||
gitManager: TabViewModel,
|
tabViewModel: TabViewModel,
|
||||||
) {
|
) {
|
||||||
val appStateManager = gitManager.appStateManager
|
val appStateManager = tabViewModel.appStateManager
|
||||||
var showCloneView by remember { mutableStateOf(false) }
|
var showCloneView by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
// Crossfade(showCloneView) {
|
// Crossfade(showCloneView) {
|
||||||
@ -69,7 +69,7 @@ fun WelcomePage(
|
|||||||
.padding(bottom = 8.dp),
|
.padding(bottom = 8.dp),
|
||||||
title = "Open a repository",
|
title = "Open a repository",
|
||||||
painter = painterResource("open.svg"),
|
painter = painterResource("open.svg"),
|
||||||
onClick = { openRepositoryDialog(gitManager) }
|
onClick = { openRepositoryDialog(tabViewModel) }
|
||||||
)
|
)
|
||||||
|
|
||||||
ButtonTile(
|
ButtonTile(
|
||||||
@ -136,7 +136,7 @@ fun WelcomePage(
|
|||||||
) {
|
) {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
gitManager.openRepository(repo)
|
tabViewModel.openRepository(repo)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
@ -161,7 +161,7 @@ fun WelcomePage(
|
|||||||
|
|
||||||
if (showCloneView)
|
if (showCloneView)
|
||||||
MaterialDialog {
|
MaterialDialog {
|
||||||
CloneDialog(gitManager, onClose = { showCloneView = false })
|
CloneDialog(tabViewModel, onClose = { showCloneView = false })
|
||||||
}
|
}
|
||||||
// Popup(focusable = true, onDismissRequest = { showCloneView = false }, alignment = Alignment.Center) {
|
// Popup(focusable = true, onDismissRequest = { showCloneView = false }, alignment = Alignment.Center) {
|
||||||
//
|
//
|
||||||
|
@ -28,6 +28,8 @@ import app.theme.tabColorActive
|
|||||||
import app.theme.tabColorInactive
|
import app.theme.tabColorInactive
|
||||||
import app.ui.AppTab
|
import app.ui.AppTab
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.name
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -166,7 +168,7 @@ class TabInformation(
|
|||||||
appComponent: AppComponent,
|
appComponent: AppComponent,
|
||||||
) {
|
) {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var gitManager: TabViewModel
|
lateinit var tabViewModel: TabViewModel
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var appStateManager: AppStateManager
|
lateinit var appStateManager: AppStateManager
|
||||||
@ -180,14 +182,18 @@ class TabInformation(
|
|||||||
tabComponent.inject(this)
|
tabComponent.inject(this)
|
||||||
|
|
||||||
//TODO: This shouldn't be here, should be in the parent method
|
//TODO: This shouldn't be here, should be in the parent method
|
||||||
gitManager.onRepositoryChanged = { path ->
|
tabViewModel.onRepositoryChanged = { path ->
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
appStateManager.repositoryTabRemoved(key)
|
appStateManager.repositoryTabRemoved(key)
|
||||||
} else
|
} else {
|
||||||
|
tabName.value = Path(path).name
|
||||||
appStateManager.repositoryTabChanged(key, path)
|
appStateManager.repositoryTabChanged(key, path)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if(path != null)
|
||||||
|
tabViewModel.openRepository(path)
|
||||||
content = {
|
content = {
|
||||||
AppTab(gitManager, path, tabName)
|
AppTab(tabViewModel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
35
src/main/kotlin/app/viewmodels/CommitChangesViewModel.kt
Normal file
35
src/main/kotlin/app/viewmodels/CommitChangesViewModel.kt
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package app.viewmodels
|
||||||
|
|
||||||
|
import app.git.DiffManager
|
||||||
|
import app.git.RefreshType
|
||||||
|
import app.git.TabState
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import org.eclipse.jgit.diff.DiffEntry
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class CommitChangesViewModel @Inject constructor(
|
||||||
|
private val tabState: TabState,
|
||||||
|
private val diffManager: DiffManager,
|
||||||
|
) {
|
||||||
|
private val _commitChangesStatus = MutableStateFlow<CommitChangesStatus>(CommitChangesStatus.Loading)
|
||||||
|
val commitChangesStatus: StateFlow<CommitChangesStatus> = _commitChangesStatus
|
||||||
|
|
||||||
|
fun loadChanges(commit: RevCommit) = tabState.runOperation { git ->
|
||||||
|
_commitChangesStatus.value = CommitChangesStatus.Loading
|
||||||
|
|
||||||
|
val changes = diffManager.commitDiffEntries(git, commit)
|
||||||
|
|
||||||
|
_commitChangesStatus.value = CommitChangesStatus.Loaded(commit, changes)
|
||||||
|
|
||||||
|
return@runOperation RefreshType.NONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sealed class CommitChangesStatus {
|
||||||
|
object Loading : CommitChangesStatus()
|
||||||
|
data class Loaded(val commit: RevCommit, val changes: List<DiffEntry>) : CommitChangesStatus()
|
||||||
|
}
|
||||||
|
|
@ -1,17 +1,10 @@
|
|||||||
package app.viewmodels
|
package app.viewmodels
|
||||||
|
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import app.git.*
|
import app.git.*
|
||||||
import app.git.diff.Hunk
|
import app.git.diff.Hunk
|
||||||
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.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.eclipse.jgit.api.Git
|
|
||||||
import org.eclipse.jgit.diff.DiffEntry
|
import org.eclipse.jgit.diff.DiffEntry
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -31,7 +24,7 @@ class DiffViewModel @Inject constructor(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun updateDiff(git: Git, diffEntryType: DiffEntryType) = withContext(Dispatchers.IO) {
|
fun updateDiff(diffEntryType: DiffEntryType) = tabState.runOperation { git ->
|
||||||
val oldDiffEntryType = _diffResult.value?.diffEntryType
|
val oldDiffEntryType = _diffResult.value?.diffEntryType
|
||||||
|
|
||||||
_diffResult.value = null
|
_diffResult.value = null
|
||||||
@ -51,6 +44,8 @@ class DiffViewModel @Inject constructor(
|
|||||||
val hunks = diffManager.diffFormat(git, diffEntryType)
|
val hunks = diffManager.diffFormat(git, diffEntryType)
|
||||||
|
|
||||||
_diffResult.value = DiffResult(diffEntryType, hunks)
|
_diffResult.value = DiffResult(diffEntryType, hunks)
|
||||||
|
|
||||||
|
return@runOperation RefreshType.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stageHunk(diffEntry: DiffEntry, hunk: Hunk) = tabState.runOperation { git ->
|
fun stageHunk(diffEntry: DiffEntry, hunk: Hunk) = tabState.runOperation { git ->
|
||||||
|
41
src/main/kotlin/app/viewmodels/MenuViewModel.kt
Normal file
41
src/main/kotlin/app/viewmodels/MenuViewModel.kt
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package app.viewmodels
|
||||||
|
|
||||||
|
import app.git.RefreshType
|
||||||
|
import app.git.RemoteOperationsManager
|
||||||
|
import app.git.StashManager
|
||||||
|
import app.git.TabState
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class MenuViewModel @Inject constructor(
|
||||||
|
private val tabState: TabState,
|
||||||
|
private val remoteOperationsManager: RemoteOperationsManager,
|
||||||
|
private val stashManager: StashManager,
|
||||||
|
) {
|
||||||
|
fun pull() = tabState.safeProcessing { git ->
|
||||||
|
remoteOperationsManager.pull(git)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ONLY_LOG
|
||||||
|
}
|
||||||
|
|
||||||
|
fun push() = tabState.safeProcessing { git ->
|
||||||
|
try {
|
||||||
|
remoteOperationsManager.push(git)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
ex.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.ONLY_LOG
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stash() = tabState.safeProcessing { git ->
|
||||||
|
stashManager.stash(git)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.UNCOMMITED_CHANGES
|
||||||
|
}
|
||||||
|
|
||||||
|
fun popStash() = tabState.safeProcessing { git ->
|
||||||
|
stashManager.popStash(git)
|
||||||
|
|
||||||
|
return@safeProcessing RefreshType.UNCOMMITED_CHANGES
|
||||||
|
}
|
||||||
|
}
|
32
src/main/kotlin/app/viewmodels/StashesViewModel.kt
Normal file
32
src/main/kotlin/app/viewmodels/StashesViewModel.kt
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package app.viewmodels
|
||||||
|
|
||||||
|
import app.git.StashManager
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class StashesViewModel @Inject constructor(
|
||||||
|
private val stashManager: StashManager,
|
||||||
|
) {
|
||||||
|
private val _stashStatus = MutableStateFlow<StashStatus>(StashStatus.Loaded(listOf()))
|
||||||
|
val stashStatus: StateFlow<StashStatus>
|
||||||
|
get() = _stashStatus
|
||||||
|
|
||||||
|
suspend fun loadStashes(git: Git) {
|
||||||
|
_stashStatus.value = StashStatus.Loading
|
||||||
|
val stashList = stashManager.getStashList(git)
|
||||||
|
_stashStatus.value = StashStatus.Loaded(stashList.toList()) // TODO: Is the list cast necessary?
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun refresh(git: Git) {
|
||||||
|
loadStashes(git)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sealed class StashStatus {
|
||||||
|
object Loading : StashStatus()
|
||||||
|
data class Loaded(val stashes: List<RevCommit>) : StashStatus()
|
||||||
|
}
|
@ -26,9 +26,7 @@ class StatusViewModel @Inject constructor(
|
|||||||
_commitMessage.value = value
|
_commitMessage.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _hasUncommitedChanges = MutableStateFlow<Boolean>(false)
|
private var lastUncommitedChangesState = false
|
||||||
val hasUncommitedChanges: StateFlow<Boolean>
|
|
||||||
get() = _hasUncommitedChanges
|
|
||||||
|
|
||||||
fun stage(diffEntry: DiffEntry) = tabState.runOperation { git ->
|
fun stage(diffEntry: DiffEntry) = tabState.runOperation { git ->
|
||||||
statusManager.stage(git, diffEntry)
|
statusManager.stage(git, diffEntry)
|
||||||
@ -68,7 +66,7 @@ class StatusViewModel @Inject constructor(
|
|||||||
return@runOperation RefreshType.UNCOMMITED_CHANGES
|
return@runOperation RefreshType.UNCOMMITED_CHANGES
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun loadStatus(git: Git) {
|
private suspend fun loadStatus(git: Git) {
|
||||||
val previousStatus = _stageStatus.value
|
val previousStatus = _stageStatus.value
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -85,8 +83,8 @@ class StatusViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun loadHasUncommitedChanges(git: Git) = withContext(Dispatchers.IO) {
|
private suspend fun loadHasUncommitedChanges(git: Git) = withContext(Dispatchers.IO) {
|
||||||
_hasUncommitedChanges.value = statusManager.hasUncommitedChanges(git)
|
lastUncommitedChangesState = statusManager.hasUncommitedChanges(git)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun commit(message: String) = tabState.safeProcessing { git ->
|
fun commit(message: String) = tabState.safeProcessing { git ->
|
||||||
@ -95,7 +93,6 @@ class StatusViewModel @Inject constructor(
|
|||||||
return@safeProcessing RefreshType.ALL_DATA
|
return@safeProcessing RefreshType.ALL_DATA
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
suspend fun refresh(git: Git) = withContext(Dispatchers.IO) {
|
suspend fun refresh(git: Git) = withContext(Dispatchers.IO) {
|
||||||
loadStatus(git)
|
loadStatus(git)
|
||||||
loadHasUncommitedChanges(git)
|
loadHasUncommitedChanges(git)
|
||||||
@ -105,11 +102,12 @@ class StatusViewModel @Inject constructor(
|
|||||||
* Checks if there are uncommited changes and returns if the state has changed (
|
* Checks if there are uncommited changes and returns if the state has changed (
|
||||||
*/
|
*/
|
||||||
suspend fun updateHasUncommitedChanges(git: Git): Boolean {
|
suspend fun updateHasUncommitedChanges(git: Git): Boolean {
|
||||||
val hadUncommitedChanges = hasUncommitedChanges.value
|
val hadUncommitedChanges = this.lastUncommitedChangesState
|
||||||
|
|
||||||
loadStatus(git)
|
loadStatus(git)
|
||||||
|
loadHasUncommitedChanges(git)
|
||||||
|
|
||||||
val hasNowUncommitedChanges = hasUncommitedChanges.value
|
val hasNowUncommitedChanges = this.lastUncommitedChangesState
|
||||||
|
|
||||||
// Return true to update the log only if the uncommitedChanges status has changed
|
// Return true to update the log only if the uncommitedChanges status has changed
|
||||||
return (hasNowUncommitedChanges != hadUncommitedChanges)
|
return (hasNowUncommitedChanges != hadUncommitedChanges)
|
||||||
|
@ -6,12 +6,12 @@ import app.app.newErrorNow
|
|||||||
import app.credentials.CredentialsState
|
import app.credentials.CredentialsState
|
||||||
import app.credentials.CredentialsStateManager
|
import app.credentials.CredentialsStateManager
|
||||||
import app.git.*
|
import app.git.*
|
||||||
|
import app.ui.SelectedItem
|
||||||
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
|
||||||
import kotlinx.coroutines.flow.collect
|
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.lib.ObjectId
|
import org.eclipse.jgit.lib.ObjectId
|
||||||
import org.eclipse.jgit.lib.Repository
|
import org.eclipse.jgit.lib.Repository
|
||||||
import org.eclipse.jgit.lib.RepositoryState
|
import org.eclipse.jgit.lib.RepositoryState
|
||||||
@ -28,40 +28,35 @@ class TabViewModel @Inject constructor(
|
|||||||
val remotesViewModel: RemotesViewModel,
|
val remotesViewModel: RemotesViewModel,
|
||||||
val statusViewModel: StatusViewModel,
|
val statusViewModel: StatusViewModel,
|
||||||
val diffViewModel: DiffViewModel,
|
val diffViewModel: DiffViewModel,
|
||||||
|
val menuViewModel: MenuViewModel,
|
||||||
|
val stashesViewModel: StashesViewModel,
|
||||||
|
val commitChangesViewModel: CommitChangesViewModel,
|
||||||
private val repositoryManager: RepositoryManager,
|
private val repositoryManager: RepositoryManager,
|
||||||
private val remoteOperationsManager: RemoteOperationsManager,
|
private val remoteOperationsManager: RemoteOperationsManager,
|
||||||
private val stashManager: StashManager,
|
|
||||||
private val diffManager: DiffManager,
|
|
||||||
private val tabState: TabState,
|
private val tabState: TabState,
|
||||||
val errorsManager: ErrorsManager,
|
val errorsManager: ErrorsManager,
|
||||||
val appStateManager: AppStateManager,
|
val appStateManager: AppStateManager,
|
||||||
private val fileChangesWatcher: FileChangesWatcher,
|
private val fileChangesWatcher: FileChangesWatcher,
|
||||||
) {
|
) {
|
||||||
|
private val _selectedItem = MutableStateFlow<SelectedItem>(SelectedItem.None)
|
||||||
val repositoryName: String
|
val selectedItem: StateFlow<SelectedItem> = _selectedItem
|
||||||
get() = safeGit.repository.directory.parentFile.name
|
|
||||||
|
|
||||||
private val credentialsStateManager = CredentialsStateManager
|
private val credentialsStateManager = CredentialsStateManager
|
||||||
|
|
||||||
private val managerScope = CoroutineScope(SupervisorJob())
|
|
||||||
|
|
||||||
private val _repositorySelectionStatus = MutableStateFlow<RepositorySelectionStatus>(RepositorySelectionStatus.None)
|
private val _repositorySelectionStatus = MutableStateFlow<RepositorySelectionStatus>(RepositorySelectionStatus.None)
|
||||||
val repositorySelectionStatus: StateFlow<RepositorySelectionStatus>
|
val repositorySelectionStatus: StateFlow<RepositorySelectionStatus>
|
||||||
get() = _repositorySelectionStatus
|
get() = _repositorySelectionStatus
|
||||||
|
|
||||||
private val _processing = MutableStateFlow(false)
|
val processing: StateFlow<Boolean> = tabState.processing
|
||||||
val processing: StateFlow<Boolean>
|
|
||||||
get() = _processing
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
private val _diffSelected = MutableStateFlow<DiffEntryType?>(null)
|
private val _diffSelected = MutableStateFlow<DiffEntryType?>(null)
|
||||||
val diffSelected : StateFlow<DiffEntryType?> = _diffSelected
|
val diffSelected: StateFlow<DiffEntryType?> = _diffSelected
|
||||||
var newDiffSelected: DiffEntryType?
|
var newDiffSelected: DiffEntryType?
|
||||||
get() = diffSelected.value
|
get() = diffSelected.value
|
||||||
set(value){
|
set(value) {
|
||||||
_diffSelected.value = value
|
_diffSelected.value = value
|
||||||
|
|
||||||
updateDiffEntry()
|
updateDiffEntry()
|
||||||
@ -71,13 +66,13 @@ class TabViewModel @Inject constructor(
|
|||||||
val repositoryState: StateFlow<RepositoryState> = _repositoryState
|
val repositoryState: StateFlow<RepositoryState> = _repositoryState
|
||||||
|
|
||||||
init {
|
init {
|
||||||
managerScope.launch {
|
tabState.managerScope.launch {
|
||||||
tabState.refreshData.collect { refreshType ->
|
tabState.refreshData.collect { refreshType ->
|
||||||
when (refreshType) {
|
when (refreshType) {
|
||||||
RefreshType.NONE -> println("Not refreshing...")
|
RefreshType.NONE -> println("Not refreshing...")
|
||||||
RefreshType.ALL_DATA -> refreshRepositoryInfo()
|
RefreshType.ALL_DATA -> refreshRepositoryInfo()
|
||||||
RefreshType.ONLY_LOG -> refreshLog()
|
RefreshType.ONLY_LOG -> refreshLog()
|
||||||
RefreshType.UNCOMMITED_CHANGES -> checkUncommitedChanges()
|
RefreshType.UNCOMMITED_CHANGES -> checkUncommitedChanges(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,27 +84,11 @@ class TabViewModel @Inject constructor(
|
|||||||
return@runOperation RefreshType.NONE
|
return@runOperation RefreshType.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Property that indicates if a git operation is running
|
|
||||||
*/
|
|
||||||
@set:Synchronized private var operationRunning = false
|
|
||||||
|
|
||||||
private val safeGit: Git
|
|
||||||
get() {
|
|
||||||
val git = this.tabState.git
|
|
||||||
if (git == null) {
|
|
||||||
_repositorySelectionStatus.value = RepositorySelectionStatus.None
|
|
||||||
throw CancellationException()
|
|
||||||
} else
|
|
||||||
return git
|
|
||||||
}
|
|
||||||
|
|
||||||
fun openRepository(directory: String) {
|
fun openRepository(directory: String) {
|
||||||
openRepository(File(directory))
|
openRepository(File(directory))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openRepository(directory: File) = managerScope.launch(Dispatchers.IO) {
|
fun openRepository(directory: File) = tabState.safeProcessingWihoutGit {
|
||||||
safeProcessing {
|
|
||||||
println("Trying to open repository ${directory.absoluteFile}")
|
println("Trying to open repository ${directory.absoluteFile}")
|
||||||
|
|
||||||
val gitDirectory = if (directory.name == ".git") {
|
val gitDirectory = if (directory.name == ".git") {
|
||||||
@ -120,7 +99,6 @@ class TabViewModel @Inject constructor(
|
|||||||
gitDir
|
gitDir
|
||||||
} else
|
} else
|
||||||
directory
|
directory
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val builder = FileRepositoryBuilder()
|
val builder = FileRepositoryBuilder()
|
||||||
@ -132,103 +110,68 @@ class TabViewModel @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)
|
||||||
tabState.git = Git(repository)
|
val git = Git(repository)
|
||||||
|
tabState.git = git
|
||||||
|
|
||||||
onRepositoryChanged(repository.directory.parent)
|
onRepositoryChanged(repository.directory.parent)
|
||||||
refreshRepositoryInfo()
|
refreshRepositoryInfo()
|
||||||
launch {
|
|
||||||
watchRepositoryChanges()
|
|
||||||
}
|
|
||||||
|
|
||||||
println("AppStateManagerReference $appStateManager")
|
watchRepositoryChanges(git)
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
ex.printStackTrace()
|
ex.printStackTrace()
|
||||||
onRepositoryChanged(null)
|
onRepositoryChanged(null)
|
||||||
errorsManager.addError(newErrorNow(ex, ex.localizedMessage))
|
errorsManager.addError(newErrorNow(ex, ex.localizedMessage))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return@safeProcessingWihoutGit RefreshType.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun loadRepositoryState(git: Git) = withContext(Dispatchers.IO) {
|
private suspend fun loadRepositoryState(git: Git) = withContext(Dispatchers.IO) {
|
||||||
_repositoryState.value = repositoryManager.getRepositoryState(git)
|
_repositoryState.value = repositoryManager.getRepositoryState(git)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun watchRepositoryChanges() {
|
private suspend fun watchRepositoryChanges(git: Git) = tabState.managerScope.launch(Dispatchers.IO) {
|
||||||
val ignored = safeGit.status().call().ignoredNotInIndex.toList()
|
val ignored = git.status().call().ignoredNotInIndex.toList()
|
||||||
|
|
||||||
fileChangesWatcher.watchDirectoryPath(
|
fileChangesWatcher.watchDirectoryPath(
|
||||||
pathStr = safeGit.repository.directory.parent,
|
pathStr = git.repository.directory.parent,
|
||||||
ignoredDirsPath = ignored,
|
ignoredDirsPath = ignored,
|
||||||
).collect {
|
).collect {
|
||||||
if (!operationRunning) { // Only update if there isn't any process running
|
if (!tabState.operationRunning) { // Only update if there isn't any process running
|
||||||
safeProcessing(showError = false) {
|
|
||||||
println("Changes detected, loading status")
|
println("Changes detected, loading status")
|
||||||
statusViewModel.refresh(safeGit)
|
checkUncommitedChanges(isFsChange = true)
|
||||||
checkUncommitedChanges()
|
|
||||||
|
|
||||||
updateDiffEntry()
|
updateDiffEntry()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun loadLog() {
|
private suspend fun checkUncommitedChanges(isFsChange: Boolean = false) = tabState.runOperation { git ->
|
||||||
logViewModel.loadLog(safeGit)
|
val uncommitedChangesStateChanged = statusViewModel.updateHasUncommitedChanges(git)
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun checkUncommitedChanges() {
|
|
||||||
val uncommitedChangesStateChanged = statusViewModel.updateHasUncommitedChanges(safeGit)
|
|
||||||
|
|
||||||
// Update the log only if the uncommitedChanges status has changed
|
// Update the log only if the uncommitedChanges status has changed
|
||||||
if (uncommitedChangesStateChanged)
|
if ((uncommitedChangesStateChanged && isFsChange) || !isFsChange)
|
||||||
loadLog()
|
logViewModel.refresh(git)
|
||||||
|
|
||||||
updateDiffEntry()
|
updateDiffEntry()
|
||||||
|
|
||||||
|
// Stashes list should only be updated if we are doing a stash operation, however it's a small operation
|
||||||
|
// that we can afford to do when doing other operations
|
||||||
|
stashesViewModel.refresh(git)
|
||||||
|
|
||||||
|
return@runOperation RefreshType.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pull() = managerScope.launch {
|
private suspend fun refreshRepositoryInfo() = tabState.safeProcessing { git ->
|
||||||
safeProcessing {
|
logViewModel.refresh(git)
|
||||||
remoteOperationsManager.pull(safeGit)
|
branchesViewModel.refresh(git)
|
||||||
loadLog()
|
remotesViewModel.refresh(git)
|
||||||
}
|
tagsViewModel.refresh(git)
|
||||||
}
|
statusViewModel.refresh(git)
|
||||||
|
stashesViewModel.refresh(git)
|
||||||
|
loadRepositoryState(git)
|
||||||
|
|
||||||
fun push() = managerScope.launch {
|
return@safeProcessing RefreshType.NONE
|
||||||
safeProcessing {
|
|
||||||
try {
|
|
||||||
remoteOperationsManager.push(safeGit)
|
|
||||||
} finally {
|
|
||||||
loadLog()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun refreshRepositoryInfo() {
|
|
||||||
logViewModel.refresh(safeGit)
|
|
||||||
branchesViewModel.refresh(safeGit)
|
|
||||||
remotesViewModel.refresh(safeGit)
|
|
||||||
tagsViewModel.refresh(safeGit)
|
|
||||||
statusViewModel.refresh(safeGit)
|
|
||||||
loadRepositoryState(safeGit)
|
|
||||||
|
|
||||||
stashManager.loadStashList(safeGit)
|
|
||||||
loadLog()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stash() = managerScope.launch {
|
|
||||||
safeProcessing {
|
|
||||||
stashManager.stash(safeGit)
|
|
||||||
checkUncommitedChanges()
|
|
||||||
loadLog()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun popStash() = managerScope.launch {
|
|
||||||
safeProcessing {
|
|
||||||
stashManager.popStash(safeGit)
|
|
||||||
checkUncommitedChanges()
|
|
||||||
loadLog()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun credentialsDenied() {
|
fun credentialsDenied() {
|
||||||
@ -243,51 +186,54 @@ class TabViewModel @Inject constructor(
|
|||||||
credentialsStateManager.updateState(CredentialsState.SshCredentialsAccepted(password))
|
credentialsStateManager.updateState(CredentialsState.SshCredentialsAccepted(password))
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun diffListFromCommit(commit: RevCommit): List<DiffEntry> {
|
|
||||||
return diffManager.commitDiffEntries(safeGit, commit)
|
|
||||||
}
|
|
||||||
|
|
||||||
var onRepositoryChanged: (path: String?) -> Unit = {}
|
var onRepositoryChanged: (path: String?) -> Unit = {}
|
||||||
|
|
||||||
|
|
||||||
fun dispose() {
|
fun dispose() {
|
||||||
managerScope.cancel()
|
tabState.managerScope.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clone(directory: File, url: String) = managerScope.launch {
|
fun clone(directory: File, url: String) = tabState.safeProcessingWihoutGit {
|
||||||
remoteOperationsManager.clone(directory, url)
|
remoteOperationsManager.clone(directory, url)
|
||||||
|
|
||||||
|
return@safeProcessingWihoutGit RefreshType.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findCommit(objectId: ObjectId): RevCommit {
|
private fun findCommit(git: Git, objectId: ObjectId): RevCommit {
|
||||||
return safeGit.repository.parseCommit(objectId)
|
return git.repository.parseCommit(objectId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun safeProcessing(showError: Boolean = true, callback: suspend () -> Unit) {
|
private fun updateDiffEntry() {
|
||||||
_processing.value = true
|
|
||||||
operationRunning = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
callback()
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
ex.printStackTrace()
|
|
||||||
|
|
||||||
if (showError)
|
|
||||||
errorsManager.addError(newErrorNow(ex, ex.localizedMessage))
|
|
||||||
} finally {
|
|
||||||
_processing.value = false
|
|
||||||
operationRunning = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateDiffEntry() = tabState.runOperation { git ->
|
|
||||||
val diffSelected = diffSelected.value
|
val diffSelected = diffSelected.value
|
||||||
|
|
||||||
if(diffSelected != null) {
|
if (diffSelected != null) {
|
||||||
diffViewModel.updateDiff(git, diffSelected)
|
diffViewModel.updateDiff(diffSelected)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun newSelectedRef(objectId: ObjectId?) = tabState.runOperation { git ->
|
||||||
|
if(objectId == null) {
|
||||||
|
newSelectedItem(SelectedItem.None)
|
||||||
return@runOperation RefreshType.NONE
|
return@runOperation RefreshType.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val commit = findCommit(git, objectId)
|
||||||
|
newSelectedItem(SelectedItem.Ref(commit))
|
||||||
|
|
||||||
|
return@runOperation RefreshType.NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newSelectedStash(stash: RevCommit) {
|
||||||
|
newSelectedItem(SelectedItem.Stash(stash))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newSelectedItem(selectedItem: SelectedItem) {
|
||||||
|
_selectedItem.value = selectedItem
|
||||||
|
|
||||||
|
if(selectedItem is SelectedItem.CommitBasedItem) {
|
||||||
|
commitChangesViewModel.loadChanges(selectedItem.revCommit)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ class TagsViewModel @Inject constructor(
|
|||||||
val tags: StateFlow<List<Ref>>
|
val tags: StateFlow<List<Ref>>
|
||||||
get() = _tags
|
get() = _tags
|
||||||
|
|
||||||
suspend fun loadTags(git: Git) = withContext(Dispatchers.IO) {
|
private suspend fun loadTags(git: Git) = withContext(Dispatchers.IO) {
|
||||||
val tagsList = tagsManager.getTags(git)
|
val tagsList = tagsManager.getTags(git)
|
||||||
|
|
||||||
_tags.value = tagsList
|
_tags.value = tagsList
|
||||||
|
Loading…
Reference in New Issue
Block a user