Added dialog to edit author info

This commit is contained in:
Abdelilah El Aissaoui 2022-06-22 00:53:10 +02:00
parent 43330eb3c4
commit ab8e8c7dbe
8 changed files with 315 additions and 23 deletions

View File

@ -40,3 +40,6 @@ val String.lineDelimiter: String?
else
null
}
val String.nullIfEmpty: String?
get() = this.ifBlank { null }

View File

@ -0,0 +1,61 @@
package app.git
import app.models.AuthorInfo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.storage.file.FileBasedConfig
import javax.inject.Inject
class AuthorManager @Inject constructor() {
suspend fun loadAuthor(git: Git) = withContext(Dispatchers.IO) {
val config = git.repository.config
val globalConfig = git.repository.config.baseConfig
val repoConfig = FileBasedConfig((config as FileBasedConfig).file, git.repository.fs)
repoConfig.load()
val globalName = globalConfig.getString("user", null, "name")
val globalEmail = globalConfig.getString("user", null, "email")
val name = repoConfig.getString("user", null, "name")
val email = repoConfig.getString("user", null, "email")
return@withContext AuthorInfo(
globalName,
globalEmail,
name,
email,
)
}
suspend fun saveAuthorInfo(git: Git, newAuthorInfo: AuthorInfo) = withContext(Dispatchers.IO) {
val config = git.repository.config
val globalConfig = git.repository.config.baseConfig
val repoConfig = FileBasedConfig((config as FileBasedConfig).file, git.repository.fs)
repoConfig.load()
if (globalConfig is FileBasedConfig) {
globalConfig.setStringProperty("user", null, "name", newAuthorInfo.globalName)
globalConfig.setStringProperty("user", null, "email", newAuthorInfo.globalEmail)
globalConfig.save()
}
config.setStringProperty("user", null, "name", newAuthorInfo.name)
config.setStringProperty("user", null, "email", newAuthorInfo.email)
config.save()
}
private fun FileBasedConfig.setStringProperty(
section: String,
subsection: String?,
name: String,
value: String?,
) {
if(value == null) {
unset(section, subsection, name)
} else {
setString(section, subsection, name, value)
}
}
}

View File

@ -0,0 +1,8 @@
package app.models
data class AuthorInfo(
val globalName: String?,
val globalEmail: String?,
val name: String?,
val email: String?,
)

View File

@ -1,6 +1,6 @@
package app.git
package app.models
data class UserInfo(
data class AuthorInfoSimple(
val name: String?,
val email: String?,
)

View File

@ -12,10 +12,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import app.extensions.handMouseClickable
import app.git.DiffEntryType
import app.theme.*
import app.ui.dialogs.AuthorDialog
import app.ui.dialogs.NewBranchDialog
import app.ui.dialogs.RebaseInteractive
import app.ui.dialogs.StashWithMessageDialog
@ -39,7 +42,8 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
val selectedItem by tabViewModel.selectedItem.collectAsState()
val blameState by tabViewModel.blameState.collectAsState()
val showHistory by tabViewModel.showHistory.collectAsState()
val userInfo by tabViewModel.userInfo.collectAsState()
val userInfo by tabViewModel.authorInfoSimple.collectAsState()
val showAuthorInfo by tabViewModel.showAuthorInfo.collectAsState()
var showNewBranchDialog by remember { mutableStateOf(false) }
var showStashWithMessageDialog by remember { mutableStateOf(false) }
@ -64,6 +68,16 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
showStashWithMessageDialog = false
}
)
} else if (showAuthorInfo) {
val authorViewModel = tabViewModel.authorViewModel
if (authorViewModel != null) {
AuthorDialog(
authorViewModel = authorViewModel,
onClose = {
tabViewModel.closeAuthorInfoDialog()
}
)
}
}
Column {
@ -107,6 +121,12 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
.background(MaterialTheme.colors.surface)
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Box(
modifier = Modifier
.fillMaxHeight()
.handMouseClickable { tabViewModel.showAuthorInfoDialog() },
contentAlignment = Alignment.Center,
) {
Text(
text = "${userInfo.name ?: "Name not set"} <${userInfo.email ?: "Email not set"}>",
@ -114,6 +134,7 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
fontSize = 12.sp,
)
}
}
}
}

View File

@ -0,0 +1,150 @@
package app.ui.dialogs
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.mouseClickable
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.isPrimaryPressed
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import app.theme.outlinedTextFieldColors
import app.theme.primaryTextColor
import app.theme.textButtonColors
import app.ui.components.PrimaryButton
import app.viewmodels.AuthorViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@Composable
fun AuthorDialog(
authorViewModel: AuthorViewModel,
onClose: () -> Unit,
) {
val authorInfo by authorViewModel.authorInfo.collectAsState()
var globalName by remember(authorInfo) { mutableStateOf(authorInfo.globalName.orEmpty()) }
var globalEmail by remember(authorInfo) { mutableStateOf(authorInfo.globalEmail.orEmpty()) }
var name by remember(authorInfo) { mutableStateOf(authorInfo.name.orEmpty()) }
var email by remember(authorInfo) { mutableStateOf(authorInfo.email.orEmpty()) }
MaterialDialog(onCloseRequested = onClose) {
Column(
modifier = Modifier
.background(MaterialTheme.colors.background)
.padding(horizontal = 8.dp),
) {
Text(
text = "Global settings",
color = MaterialTheme.colors.primaryTextColor,
fontSize = 18.sp,
)
TextInput(
title = "Name",
value = globalName,
onValueChange = { globalName = it },
)
TextInput(
title = "Email",
value = globalEmail,
onValueChange = { globalEmail = it },
)
Text(
text = "Repository settings",
color = MaterialTheme.colors.primaryTextColor,
fontSize = 18.sp,
modifier = Modifier.padding(top = 16.dp),
)
TextInput(
title = "Name",
value = name,
onValueChange = { name = it },
)
TextInput(
title = "Email",
value = email,
onValueChange = { email = it },
)
Text(
text = "Repository-level values will override global values",
color = MaterialTheme.colors.primaryTextColor,
fontSize = 14.sp,
modifier = Modifier.padding(top = 8.dp),
)
Row(
modifier = Modifier
.padding(top = 16.dp)
.align(Alignment.End)
) {
TextButton(
modifier = Modifier.padding(end = 8.dp),
colors = textButtonColors(),
onClick = {
onClose()
}
) {
Text("Cancel")
}
PrimaryButton(
onClick = {
authorViewModel.saveAuthorInfo(
globalName,
globalEmail,
name,
email,
)
onClose()
},
text = "Save"
)
}
}
}
}
@Composable
private fun TextInput(
title: String,
value: String,
enabled: Boolean = true,
onValueChange: (String) -> Unit,
) {
Row(
modifier = Modifier
.width(400.dp)
.padding(vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = title,
color = MaterialTheme.colors.primaryTextColor,
fontSize = 16.sp,
modifier = Modifier
.width(100.dp)
.padding(end = 16.dp),
)
OutlinedTextField(
value = value,
modifier = Modifier
.weight(1f),
enabled = enabled,
onValueChange = onValueChange,
colors = outlinedTextFieldColors(),
maxLines = 1,
)
}
}

View File

@ -0,0 +1,40 @@
package app.viewmodels
import app.extensions.nullIfEmpty
import app.git.AuthorManager
import app.git.RefreshType
import app.git.TabState
import app.models.AuthorInfo
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
class AuthorViewModel @Inject constructor(
private val tabState: TabState,
private val authorManager: AuthorManager,
) {
private val _authorInfo = MutableStateFlow(AuthorInfo(null, null, null, null))
val authorInfo: StateFlow<AuthorInfo> = _authorInfo
fun loadAuthorInfo() = tabState.runOperation(
refreshType = RefreshType.NONE,
showError = true,
) { git ->
_authorInfo.value = authorManager.loadAuthor(git)
}
fun saveAuthorInfo(globalName: String, globalEmail: String, name: String, email: String) = tabState.runOperation(
showError = true,
refreshType = RefreshType.REPO_STATE,
) { git ->
val newAuthorInfo = AuthorInfo(
globalName.nullIfEmpty,
globalEmail.nullIfEmpty,
name.nullIfEmpty,
email.nullIfEmpty,
)
authorManager.saveAuthorInfo(git, newAuthorInfo)
}
}

View File

@ -6,6 +6,7 @@ import app.credentials.CredentialsState
import app.credentials.CredentialsStateManager
import app.git.*
import app.logging.printLog
import app.models.AuthorInfoSimple
import app.newErrorNow
import app.ui.SelectedItem
import app.updates.Update
@ -19,7 +20,6 @@ import org.eclipse.jgit.blame.BlameResult
import org.eclipse.jgit.lib.Repository
import org.eclipse.jgit.lib.RepositoryState
import org.eclipse.jgit.revwalk.RevCommit
import org.eclipse.jgit.storage.file.FileBasedConfig
import java.io.File
import javax.inject.Inject
import javax.inject.Provider
@ -46,6 +46,7 @@ class TabViewModel @Inject constructor(
private val diffViewModelProvider: Provider<DiffViewModel>,
private val rebaseInteractiveViewModelProvider: Provider<RebaseInteractiveViewModel>,
private val historyViewModelProvider: Provider<HistoryViewModel>,
private val authorViewModelProvider: Provider<AuthorViewModel>,
private val repositoryManager: RepositoryManager,
private val tabState: TabState,
val appStateManager: AppStateManager,
@ -88,12 +89,18 @@ class TabViewModel @Inject constructor(
private val _showHistory = MutableStateFlow(false)
val showHistory: StateFlow<Boolean> = _showHistory
private val _userInfo = MutableStateFlow(UserInfo(null, null))
val userInfo: StateFlow<UserInfo> = _userInfo
private val _showAuthorInfo = MutableStateFlow(false)
val showAuthorInfo: StateFlow<Boolean> = _showAuthorInfo
private val _authorInfoSimple = MutableStateFlow(AuthorInfoSimple(null, null))
val authorInfoSimple: StateFlow<AuthorInfoSimple> = _authorInfoSimple
var historyViewModel: HistoryViewModel? = null
private set
var authorViewModel: AuthorViewModel? = null
private set
val showError = MutableStateFlow(false)
init {
@ -185,27 +192,29 @@ class TabViewModel @Inject constructor(
printLog(TAG, "Refreshing repository state $newRepoState")
_repositoryState.value = newRepoState
loadConfigInfo(git)
loadAuthorInfo(git)
onRepositoryStateChanged(newRepoState)
}
private fun loadConfigInfo(git: Git) {
private fun loadAuthorInfo(git: Git) {
val config = git.repository.config
config.load()
val userName = config.getString("user", null, "name")
val email = config.getString("user", null, "email")
// TODO Load file-specific config
// val fcfg = FileBasedConfig((config as FileBasedConfig).file, git.repository.fs)
// fcfg.load()
// val fname = fcfg.getString("user", null, "name")
// val fmail = fcfg.getString("user", null, "email")
// println("Fname $fname\nFmail $fmail")
_authorInfoSimple.value = AuthorInfoSimple(userName, email)
}
println(userName)
println(email)
_userInfo.value = UserInfo(userName, email)
fun showAuthorInfoDialog() {
authorViewModel = authorViewModelProvider.get()
authorViewModel?.loadAuthorInfo()
_showAuthorInfo.value = true
}
fun closeAuthorInfoDialog() {
_showAuthorInfo.value = false
authorViewModel = null
}
private fun onRepositoryStateChanged(newRepoState: RepositoryState) {