210 lines
7.7 KiB
Kotlin
210 lines
7.7 KiB
Kotlin
package com.jetpackduba.gitnuro.ui
|
|
|
|
import androidx.compose.animation.Crossfade
|
|
import androidx.compose.foundation.background
|
|
import androidx.compose.foundation.clickable
|
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
import androidx.compose.foundation.layout.*
|
|
import androidx.compose.material.LinearProgressIndicator
|
|
import androidx.compose.material.MaterialTheme
|
|
import androidx.compose.material.Text
|
|
import androidx.compose.runtime.*
|
|
import androidx.compose.ui.Alignment
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.input.key.onPreviewKeyEvent
|
|
import androidx.compose.ui.unit.dp
|
|
import com.jetpackduba.gitnuro.LoadingRepository
|
|
import com.jetpackduba.gitnuro.credentials.CredentialsAccepted
|
|
import com.jetpackduba.gitnuro.credentials.CredentialsRequested
|
|
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.settings.SettingsDialog
|
|
import com.jetpackduba.gitnuro.viewmodels.RepositorySelectionStatus
|
|
import com.jetpackduba.gitnuro.viewmodels.TabViewModel
|
|
|
|
@Composable
|
|
fun AppTab(
|
|
tabViewModel: TabViewModel,
|
|
) {
|
|
val errorManager = tabViewModel.errorsManager
|
|
val lastError by errorManager.error.collectAsState(null)
|
|
val showError by tabViewModel.showError.collectAsState()
|
|
|
|
val repositorySelectionStatus = tabViewModel.repositorySelectionStatus.collectAsState()
|
|
val repositorySelectionStatusValue = repositorySelectionStatus.value
|
|
val processingState = tabViewModel.processing.collectAsState().value
|
|
|
|
LaunchedEffect(tabViewModel) {
|
|
// Init the tab content when the tab is selected and also remove the "initialPath" to avoid opening the
|
|
// repository everytime the user changes between tabs
|
|
val initialPath = tabViewModel.initialPath
|
|
tabViewModel.initialPath = null
|
|
|
|
if (initialPath != null) {
|
|
tabViewModel.openRepository(initialPath)
|
|
}
|
|
}
|
|
|
|
Box {
|
|
Column(
|
|
modifier = Modifier
|
|
.background(MaterialTheme.colors.surface)
|
|
.fillMaxSize()
|
|
) {
|
|
|
|
CredentialsDialog(tabViewModel)
|
|
|
|
var showSettingsDialog by remember { mutableStateOf(false) }
|
|
if (showSettingsDialog) {
|
|
SettingsDialog(
|
|
onDismiss = { showSettingsDialog = false }
|
|
)
|
|
}
|
|
|
|
var showCloneDialog by remember { mutableStateOf(false) }
|
|
|
|
if (showCloneDialog) {
|
|
CloneDialog(
|
|
onClose = {
|
|
showCloneDialog = false
|
|
},
|
|
onOpenRepository = { dir ->
|
|
tabViewModel.openRepository(dir)
|
|
},
|
|
)
|
|
}
|
|
|
|
Box(modifier = Modifier.fillMaxSize()) {
|
|
Crossfade(targetState = repositorySelectionStatus) {
|
|
when (repositorySelectionStatusValue) {
|
|
RepositorySelectionStatus.None -> {
|
|
WelcomePage(
|
|
tabViewModel = tabViewModel,
|
|
onShowCloneDialog = { showCloneDialog = true },
|
|
onShowSettings = { showSettingsDialog = true }
|
|
)
|
|
}
|
|
|
|
is RepositorySelectionStatus.Opening -> {
|
|
LoadingRepository(repositorySelectionStatusValue.path)
|
|
}
|
|
|
|
is RepositorySelectionStatus.Open -> {
|
|
RepositoryOpenPage(
|
|
tabViewModel = tabViewModel,
|
|
onShowSettingsDialog = { showSettingsDialog = true },
|
|
onShowCloneDialog = { showCloneDialog = true },
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (processingState is ProcessingState.Processing) {
|
|
Box(
|
|
modifier = Modifier
|
|
.fillMaxSize()
|
|
.background(MaterialTheme.colors.surface)
|
|
.onPreviewKeyEvent { true } // Disable all keyboard events
|
|
.clickable(
|
|
interactionSource = remember { MutableInteractionSource() },
|
|
indication = null,
|
|
onClick = {},
|
|
),
|
|
contentAlignment = Alignment.Center,
|
|
) {
|
|
Column(
|
|
horizontalAlignment = Alignment.CenterHorizontally,
|
|
) {
|
|
if (processingState.title.isNotEmpty()) {
|
|
Text(
|
|
processingState.title,
|
|
style = MaterialTheme.typography.h3,
|
|
color = MaterialTheme.colors.onBackground,
|
|
modifier = Modifier.padding(bottom = 8.dp),
|
|
)
|
|
}
|
|
|
|
if (processingState.subtitle.isNotEmpty()) {
|
|
Text(
|
|
processingState.subtitle,
|
|
style = MaterialTheme.typography.body1,
|
|
color = MaterialTheme.colors.onBackground,
|
|
modifier = Modifier.padding(bottom = 32.dp),
|
|
)
|
|
}
|
|
|
|
LinearProgressIndicator(
|
|
modifier = Modifier.width(280.dp)
|
|
.padding(bottom = 32.dp),
|
|
color = MaterialTheme.colors.secondary,
|
|
)
|
|
|
|
if (processingState.isCancellable) {
|
|
PrimaryButton(
|
|
text = "Cancel",
|
|
onClick = { tabViewModel.cancelOngoingTask() }
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
val safeLastError = lastError
|
|
if (safeLastError != null && showError) {
|
|
ErrorDialog(
|
|
error = safeLastError,
|
|
onAccept = { tabViewModel.showError.value = false }
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun CredentialsDialog(gitManager: TabViewModel) {
|
|
val credentialsState = gitManager.credentialsState.collectAsState()
|
|
|
|
when (val credentialsStateValue = credentialsState.value) {
|
|
CredentialsRequested.HttpCredentialsRequested -> {
|
|
UserPasswordDialog(
|
|
onReject = {
|
|
gitManager.credentialsDenied()
|
|
},
|
|
onAccept = { user, password ->
|
|
gitManager.httpCredentialsAccepted(user, password)
|
|
}
|
|
)
|
|
}
|
|
|
|
CredentialsRequested.SshCredentialsRequested -> {
|
|
SshPasswordDialog(
|
|
onReject = {
|
|
gitManager.credentialsDenied()
|
|
},
|
|
onAccept = { password ->
|
|
gitManager.sshCredentialsAccepted(password)
|
|
}
|
|
)
|
|
}
|
|
|
|
is CredentialsRequested.GpgCredentialsRequested -> {
|
|
GpgPasswordDialog(
|
|
gpgCredentialsRequested = credentialsStateValue,
|
|
onReject = {
|
|
gitManager.credentialsDenied()
|
|
},
|
|
onAccept = { password ->
|
|
gitManager.gpgCredentialsAccepted(password)
|
|
}
|
|
)
|
|
}
|
|
|
|
is CredentialsAccepted, CredentialsState.None, CredentialsState.CredentialsDenied -> { /* Nothing to do */
|
|
}
|
|
}
|
|
}
|