Processing tasks now can show a title, subtitle and an option to cancel it.
Right now only fetch implements such features but every feature will be migrated gradually.
This commit is contained in:
parent
05a894a1cb
commit
0d91ec747a
@ -11,10 +11,23 @@ import org.eclipse.jgit.api.Git
|
|||||||
import org.eclipse.jgit.lib.ObjectId
|
import org.eclipse.jgit.lib.ObjectId
|
||||||
import org.eclipse.jgit.revwalk.RevCommit
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.coroutines.cancellation.CancellationException
|
|
||||||
|
|
||||||
private const val TAG = "TabState"
|
private const val TAG = "TabState"
|
||||||
|
|
||||||
|
interface ProcessingInfo {
|
||||||
|
fun changeSubtitle(newSubtitle: String)
|
||||||
|
fun changeIsCancellable(newIsCancellable: Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface ProcessingState {
|
||||||
|
object None : ProcessingState
|
||||||
|
data class Processing(
|
||||||
|
val title: String,
|
||||||
|
val subtitle: String,
|
||||||
|
val isCancellable: Boolean,
|
||||||
|
) : ProcessingState
|
||||||
|
}
|
||||||
|
|
||||||
@TabScope
|
@TabScope
|
||||||
class TabState @Inject constructor(
|
class TabState @Inject constructor(
|
||||||
val errorsManager: ErrorsManager,
|
val errorsManager: ErrorsManager,
|
||||||
@ -44,8 +57,10 @@ class TabState @Inject constructor(
|
|||||||
@set:Synchronized
|
@set:Synchronized
|
||||||
var operationRunning = false
|
var operationRunning = false
|
||||||
|
|
||||||
private val _processing = MutableStateFlow(false)
|
var currentJob: Job? = null
|
||||||
val processing: StateFlow<Boolean> = _processing
|
|
||||||
|
private val _processing = MutableStateFlow<ProcessingState>(ProcessingState.None)
|
||||||
|
val processing: StateFlow<ProcessingState> = _processing
|
||||||
|
|
||||||
fun initGit(git: Git) {
|
fun initGit(git: Git) {
|
||||||
this.unsafeGit = git
|
this.unsafeGit = git
|
||||||
@ -54,23 +69,64 @@ class TabState @Inject constructor(
|
|||||||
fun safeProcessing(
|
fun safeProcessing(
|
||||||
showError: Boolean = true,
|
showError: Boolean = true,
|
||||||
refreshType: RefreshType,
|
refreshType: RefreshType,
|
||||||
|
// TODO Eventually the title and subtitles should be mandatory but for now the default it's empty to slowly
|
||||||
|
// migrate the code that uses this function
|
||||||
|
title: String = "",
|
||||||
|
subtitle: String = "",
|
||||||
|
isCancellable: Boolean = false,
|
||||||
refreshEvenIfCrashes: Boolean = false,
|
refreshEvenIfCrashes: Boolean = false,
|
||||||
refreshEvenIfCrashesInteractive: ((Exception) -> Boolean)? = null,
|
refreshEvenIfCrashesInteractive: ((Exception) -> Boolean)? = null,
|
||||||
callback: suspend (git: Git) -> Unit
|
callback: suspend ProcessingInfo.(git: Git) -> Unit
|
||||||
) =
|
): Job {
|
||||||
scope.launch(Dispatchers.IO) {
|
val job = scope.launch(Dispatchers.IO) {
|
||||||
var hasProcessFailed = false
|
var hasProcessFailed = false
|
||||||
var refreshEvenIfCrashesInteractiveResult = false
|
var refreshEvenIfCrashesInteractiveResult = false
|
||||||
operationRunning = true
|
operationRunning = true
|
||||||
|
|
||||||
|
val processingInfo: ProcessingInfo = object : ProcessingInfo {
|
||||||
|
override fun changeSubtitle(newSubtitle: String) {
|
||||||
|
_processing.update { processingState ->
|
||||||
|
if (processingState is ProcessingState.Processing) {
|
||||||
|
processingState.copy(subtitle = newSubtitle)
|
||||||
|
} else {
|
||||||
|
ProcessingState.Processing(
|
||||||
|
title = title,
|
||||||
|
isCancellable = isCancellable,
|
||||||
|
subtitle = newSubtitle
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun changeIsCancellable(newIsCancellable: Boolean) {
|
||||||
|
_processing.update { processingState ->
|
||||||
|
if (processingState is ProcessingState.Processing) {
|
||||||
|
processingState.copy(isCancellable = newIsCancellable)
|
||||||
|
} else {
|
||||||
|
ProcessingState.Processing(
|
||||||
|
title = title,
|
||||||
|
isCancellable = newIsCancellable,
|
||||||
|
subtitle = subtitle
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
delayedStateChange(
|
delayedStateChange(
|
||||||
delayMs = 300,
|
delayMs = 300,
|
||||||
onDelayTriggered = {
|
onDelayTriggered = {
|
||||||
_processing.value = true
|
_processing.update { processingState ->
|
||||||
|
if(processingState is ProcessingState.None) {
|
||||||
|
ProcessingState.Processing(title, subtitle, isCancellable)
|
||||||
|
} else {
|
||||||
|
processingState
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
callback(git)
|
processingInfo.callback(git)
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
hasProcessFailed = true
|
hasProcessFailed = true
|
||||||
@ -83,14 +139,18 @@ class TabState @Inject constructor(
|
|||||||
if (showError && !containsCancellation)
|
if (showError && !containsCancellation)
|
||||||
errorsManager.addError(newErrorNow(ex, ex.message.orEmpty()))
|
errorsManager.addError(newErrorNow(ex, ex.message.orEmpty()))
|
||||||
} finally {
|
} finally {
|
||||||
_processing.value = false
|
_processing.value = ProcessingState.None
|
||||||
operationRunning = false
|
operationRunning = false
|
||||||
|
|
||||||
if (refreshType != RefreshType.NONE && (!hasProcessFailed || refreshEvenIfCrashes || refreshEvenIfCrashesInteractiveResult)) {
|
if (refreshType != RefreshType.NONE && (!hasProcessFailed || refreshEvenIfCrashes || refreshEvenIfCrashesInteractiveResult)) {
|
||||||
_refreshData.emit(refreshType)
|
_refreshData.emit(refreshType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentJob = job
|
||||||
|
|
||||||
|
return job
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun exceptionContainsCancellation(ex: Throwable?): Boolean {
|
private fun exceptionContainsCancellation(ex: Throwable?): Boolean {
|
||||||
@ -102,9 +162,17 @@ class TabState @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun safeProcessingWithoutGit(showError: Boolean = true, callback: suspend CoroutineScope.() -> Unit) =
|
fun safeProcessingWithoutGit(
|
||||||
scope.launch(Dispatchers.IO) {
|
showError: Boolean = true,
|
||||||
_processing.value = true
|
// TODO Eventually the title and subtitles should be mandatory but for now the default it's empty to slowly
|
||||||
|
// migrate the code that uses this function
|
||||||
|
title: String = "",
|
||||||
|
subtitle: String = "",
|
||||||
|
isCancellable: Boolean = false,
|
||||||
|
callback: suspend CoroutineScope.() -> Unit
|
||||||
|
): Job {
|
||||||
|
val job = scope.launch(Dispatchers.IO) {
|
||||||
|
_processing.value = ProcessingState.Processing(title, subtitle, isCancellable)
|
||||||
operationRunning = true
|
operationRunning = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -112,14 +180,21 @@ class TabState @Inject constructor(
|
|||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
ex.printStackTrace()
|
ex.printStackTrace()
|
||||||
|
|
||||||
if (showError)
|
val containsCancellation = exceptionContainsCancellation(ex)
|
||||||
|
|
||||||
|
if (showError && !containsCancellation)
|
||||||
errorsManager.addError(newErrorNow(ex, ex.localizedMessage))
|
errorsManager.addError(newErrorNow(ex, ex.localizedMessage))
|
||||||
} finally {
|
} finally {
|
||||||
_processing.value = false
|
_processing.value = ProcessingState.None
|
||||||
operationRunning = false
|
operationRunning = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.currentJob = job
|
||||||
|
|
||||||
|
return job
|
||||||
|
}
|
||||||
|
|
||||||
fun runOperation(
|
fun runOperation(
|
||||||
showError: Boolean = false,
|
showError: Boolean = false,
|
||||||
refreshType: RefreshType,
|
refreshType: RefreshType,
|
||||||
@ -201,6 +276,10 @@ class TabState @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cancelCurrentTask() {
|
||||||
|
currentJob?.cancel()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class RefreshType {
|
enum class RefreshType {
|
||||||
@ -214,7 +293,3 @@ enum class RefreshType {
|
|||||||
UNCOMMITED_CHANGES_AND_LOG,
|
UNCOMMITED_CHANGES_AND_LOG,
|
||||||
REMOTES,
|
REMOTES,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Processing {
|
|
||||||
|
|
||||||
}
|
|
@ -1,8 +1,11 @@
|
|||||||
package com.jetpackduba.gitnuro.git.remote_operations
|
package com.jetpackduba.gitnuro.git.remote_operations
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.lib.ProgressMonitor
|
||||||
import org.eclipse.jgit.transport.CredentialsProvider
|
import org.eclipse.jgit.transport.CredentialsProvider
|
||||||
import org.eclipse.jgit.transport.RefSpec
|
import org.eclipse.jgit.transport.RefSpec
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -13,6 +16,8 @@ class FetchAllBranchesUseCase @Inject constructor(
|
|||||||
suspend operator fun invoke(git: Git) = withContext(Dispatchers.IO) {
|
suspend operator fun invoke(git: Git) = withContext(Dispatchers.IO) {
|
||||||
val remotes = git.remoteList().call()
|
val remotes = git.remoteList().call()
|
||||||
|
|
||||||
|
delay(4000)
|
||||||
|
|
||||||
for (remote in remotes) {
|
for (remote in remotes) {
|
||||||
val refSpecs = remote.fetchRefSpecs.ifEmpty {
|
val refSpecs = remote.fetchRefSpecs.ifEmpty {
|
||||||
listOf(RefSpec("refs/heads/*:refs/remotes/${remote.name}/*"))
|
listOf(RefSpec("refs/heads/*:refs/remotes/${remote.name}/*"))
|
||||||
@ -24,6 +29,17 @@ class FetchAllBranchesUseCase @Inject constructor(
|
|||||||
.setRemoveDeletedRefs(true)
|
.setRemoveDeletedRefs(true)
|
||||||
.setTransportConfigCallback { handleTransportUseCase(it, git) }
|
.setTransportConfigCallback { handleTransportUseCase(it, git) }
|
||||||
.setCredentialsProvider(CredentialsProvider.getDefault())
|
.setCredentialsProvider(CredentialsProvider.getDefault())
|
||||||
|
.setProgressMonitor(object: ProgressMonitor {
|
||||||
|
override fun start(totalTasks: Int) {}
|
||||||
|
|
||||||
|
override fun beginTask(title: String?, totalWork: Int) {}
|
||||||
|
|
||||||
|
override fun update(completed: Int) {}
|
||||||
|
|
||||||
|
override fun endTask() {}
|
||||||
|
|
||||||
|
override fun isCancelled(): Boolean = isActive
|
||||||
|
})
|
||||||
.call()
|
.call()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,10 @@ package com.jetpackduba.gitnuro.ui
|
|||||||
|
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.material.LinearProgressIndicator
|
import androidx.compose.material.LinearProgressIndicator
|
||||||
import androidx.compose.material.MaterialTheme
|
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
|
||||||
@ -17,6 +15,8 @@ import com.jetpackduba.gitnuro.LoadingRepository
|
|||||||
import com.jetpackduba.gitnuro.credentials.CredentialsAccepted
|
import com.jetpackduba.gitnuro.credentials.CredentialsAccepted
|
||||||
import com.jetpackduba.gitnuro.credentials.CredentialsRequested
|
import com.jetpackduba.gitnuro.credentials.CredentialsRequested
|
||||||
import com.jetpackduba.gitnuro.credentials.CredentialsState
|
import com.jetpackduba.gitnuro.credentials.CredentialsState
|
||||||
|
import com.jetpackduba.gitnuro.git.ProcessingState
|
||||||
|
import com.jetpackduba.gitnuro.ui.components.PrimaryButton
|
||||||
import com.jetpackduba.gitnuro.ui.dialogs.*
|
import com.jetpackduba.gitnuro.ui.dialogs.*
|
||||||
import com.jetpackduba.gitnuro.ui.dialogs.settings.SettingsDialog
|
import com.jetpackduba.gitnuro.ui.dialogs.settings.SettingsDialog
|
||||||
import com.jetpackduba.gitnuro.viewmodels.RepositorySelectionStatus
|
import com.jetpackduba.gitnuro.viewmodels.RepositorySelectionStatus
|
||||||
@ -32,7 +32,7 @@ fun AppTab(
|
|||||||
|
|
||||||
val repositorySelectionStatus = tabViewModel.repositorySelectionStatus.collectAsState()
|
val repositorySelectionStatus = tabViewModel.repositorySelectionStatus.collectAsState()
|
||||||
val repositorySelectionStatusValue = repositorySelectionStatus.value
|
val repositorySelectionStatusValue = repositorySelectionStatus.value
|
||||||
val isProcessing by tabViewModel.processing.collectAsState()
|
val processingState = tabViewModel.processing.collectAsState().value
|
||||||
|
|
||||||
Box {
|
Box {
|
||||||
Column(
|
Column(
|
||||||
@ -90,7 +90,7 @@ fun AppTab(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isProcessing) {
|
if (processingState is ProcessingState.Processing) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@ -98,11 +98,33 @@ fun AppTab(
|
|||||||
.onPreviewKeyEvent { true }, // Disable all keyboard events
|
.onPreviewKeyEvent { true }, // Disable all keyboard events
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
Column {
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
processingState.title,
|
||||||
|
style = MaterialTheme.typography.h3,
|
||||||
|
color = MaterialTheme.colors.onBackground,
|
||||||
|
modifier = Modifier.padding(bottom = 8.dp),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
processingState.subtitle,
|
||||||
|
style = MaterialTheme.typography.body1,
|
||||||
|
color = MaterialTheme.colors.onBackground,
|
||||||
|
modifier = Modifier.padding(bottom = 32.dp),
|
||||||
|
)
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
modifier = Modifier.width(340.dp),
|
modifier = Modifier.width(280.dp)
|
||||||
|
.padding(bottom = 32.dp),
|
||||||
color = MaterialTheme.colors.secondary,
|
color = MaterialTheme.colors.secondary,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (processingState.isCancellable) {
|
||||||
|
PrimaryButton(
|
||||||
|
text = "Cancel",
|
||||||
|
onClick = { tabViewModel.cancelOngoingTask() }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -203,12 +203,12 @@ fun ExtendedMenuButton(
|
|||||||
.ignoreKeyEvents()
|
.ignoreKeyEvents()
|
||||||
.clip(RoundedCornerShape(4.dp))
|
.clip(RoundedCornerShape(4.dp))
|
||||||
.background(MaterialTheme.colors.surface)
|
.background(MaterialTheme.colors.surface)
|
||||||
|
.handMouseClickable { if (enabled) onClick() }
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
.weight(1f)
|
.weight(1f),
|
||||||
.handMouseClickable { if (enabled) onClick() },
|
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
|
@ -22,6 +22,8 @@ import androidx.compose.ui.unit.*
|
|||||||
import androidx.compose.ui.window.Popup
|
import androidx.compose.ui.window.Popup
|
||||||
import androidx.compose.ui.window.PopupPositionProvider
|
import androidx.compose.ui.window.PopupPositionProvider
|
||||||
import com.jetpackduba.gitnuro.extensions.awaitFirstDownEvent
|
import com.jetpackduba.gitnuro.extensions.awaitFirstDownEvent
|
||||||
|
import com.jetpackduba.gitnuro.extensions.handMouseClickable
|
||||||
|
import com.jetpackduba.gitnuro.extensions.handOnHover
|
||||||
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
|
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
|
||||||
import com.jetpackduba.gitnuro.keybindings.matchesBinding
|
import com.jetpackduba.gitnuro.keybindings.matchesBinding
|
||||||
import com.jetpackduba.gitnuro.theme.onBackgroundSecondary
|
import com.jetpackduba.gitnuro.theme.onBackgroundSecondary
|
||||||
@ -85,7 +87,7 @@ private fun Modifier.contextMenu(items: () -> List<ContextMenuElement>): Modifie
|
|||||||
@OptIn(ExperimentalComposeUiApi::class)
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun Modifier.dropdownMenu(items: () -> List<ContextMenuElement>): Modifier {
|
private fun Modifier.dropdownMenu(items: () -> List<ContextMenuElement>): Modifier {
|
||||||
val (lastMouseEventState, setLastMouseEventState) = remember { mutableStateOf<MouseEvent?>(null) }
|
val (isClicked, setIsClicked) = remember { mutableStateOf(false) }
|
||||||
val (offset, setOffset) = remember { mutableStateOf<Offset?>(null) }
|
val (offset, setOffset) = remember { mutableStateOf<Offset?>(null) }
|
||||||
val mod = this
|
val mod = this
|
||||||
.onGloballyPositioned { layoutCoordinates ->
|
.onGloballyPositioned { layoutCoordinates ->
|
||||||
@ -95,32 +97,16 @@ private fun Modifier.dropdownMenu(items: () -> List<ContextMenuElement>): Modifi
|
|||||||
val offsetToBottomOfComponent = offsetToRoot.copy(y = offsetToRoot.y + layoutCoordinates.size.height)
|
val offsetToBottomOfComponent = offsetToRoot.copy(y = offsetToRoot.y + layoutCoordinates.size.height)
|
||||||
setOffset(offsetToBottomOfComponent)
|
setOffset(offsetToBottomOfComponent)
|
||||||
}
|
}
|
||||||
.pointerInput(Unit) {
|
.handMouseClickable {
|
||||||
while (true) {
|
setIsClicked(true)
|
||||||
val lastMouseEvent = awaitPointerEventScope { awaitFirstDownEvent() }
|
|
||||||
val mouseEvent = lastMouseEvent.awtEventOrNull
|
|
||||||
|
|
||||||
if (mouseEvent != null) {
|
|
||||||
if (lastMouseEvent.button.isPrimary) {
|
|
||||||
val currentCheck = System.currentTimeMillis()
|
|
||||||
if (lastCheck != 0L && currentCheck - lastCheck < MIN_TIME_BETWEEN_POPUPS) {
|
|
||||||
println("IGNORE POPUP TRIGGERED!")
|
|
||||||
} else {
|
|
||||||
lastCheck = currentCheck
|
|
||||||
|
|
||||||
setLastMouseEventState(mouseEvent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offset != null && lastMouseEventState != null) {
|
if (offset != null && isClicked) {
|
||||||
showPopup(
|
showPopup(
|
||||||
offset.x.toInt(),
|
offset.x.toInt(),
|
||||||
offset.y.toInt(),
|
offset.y.toInt(),
|
||||||
items(),
|
items(),
|
||||||
onDismissRequest = { setLastMouseEventState(null) })
|
onDismissRequest = { setIsClicked(false) })
|
||||||
}
|
}
|
||||||
|
|
||||||
return mod
|
return mod
|
||||||
|
@ -77,7 +77,7 @@ class HistoryViewModel @Inject constructor(
|
|||||||
fun fileHistory(filePath: String) = tabState.safeProcessing(
|
fun fileHistory(filePath: String) = tabState.safeProcessing(
|
||||||
refreshType = RefreshType.NONE,
|
refreshType = RefreshType.NONE,
|
||||||
) { git ->
|
) { git ->
|
||||||
this.filePath = filePath
|
this@HistoryViewModel.filePath = filePath
|
||||||
_historyState.value = HistoryState.Loading(filePath)
|
_historyState.value = HistoryState.Loading(filePath)
|
||||||
|
|
||||||
val log = git.log()
|
val log = git.log()
|
||||||
|
@ -36,6 +36,9 @@ class MenuViewModel @Inject constructor(
|
|||||||
fun fetchAll() = tabState.safeProcessing(
|
fun fetchAll() = tabState.safeProcessing(
|
||||||
refreshType = RefreshType.ALL_DATA,
|
refreshType = RefreshType.ALL_DATA,
|
||||||
refreshEvenIfCrashes = true,
|
refreshEvenIfCrashes = true,
|
||||||
|
title = "Fetching",
|
||||||
|
subtitle = "Updating references from the remote repositories...",
|
||||||
|
isCancellable = true
|
||||||
) { git ->
|
) { git ->
|
||||||
fetchAllBranchesUseCase(git)
|
fetchAllBranchesUseCase(git)
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ class TabViewModel @Inject constructor(
|
|||||||
val repositorySelectionStatus: StateFlow<RepositorySelectionStatus>
|
val repositorySelectionStatus: StateFlow<RepositorySelectionStatus>
|
||||||
get() = _repositorySelectionStatus
|
get() = _repositorySelectionStatus
|
||||||
|
|
||||||
val processing: StateFlow<Boolean> = tabState.processing
|
val processing: StateFlow<ProcessingState> = tabState.processing
|
||||||
|
|
||||||
val credentialsState: StateFlow<CredentialsState> = credentialsStateManager.credentialsState
|
val credentialsState: StateFlow<CredentialsState> = credentialsStateManager.credentialsState
|
||||||
|
|
||||||
@ -464,6 +464,10 @@ class TabViewModel @Inject constructor(
|
|||||||
fun gpgCredentialsAccepted(password: String) {
|
fun gpgCredentialsAccepted(password: String) {
|
||||||
credentialsStateManager.updateState(CredentialsAccepted.GpgCredentialsAccepted(password))
|
credentialsStateManager.updateState(CredentialsAccepted.GpgCredentialsAccepted(password))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cancelOngoingTask() {
|
||||||
|
tabState.cancelCurrentTask()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user