From ddc198a0d716319c3925564d8f33cf6558625609 Mon Sep 17 00:00:00 2001 From: Abdelilah El Aissaoui Date: Mon, 1 May 2023 16:20:11 +0200 Subject: [PATCH] Added option to sign off commits Fixes #60 --- .../kotlin/com/jetpackduba/gitnuro/Icons.kt | 1 + .../git/config/LoadSignOffConfigUseCase.kt | 35 ++++ .../git/config/LocalConfigConstants.kt | 16 ++ .../SaveLocalRepositoryConfigUseCase.kt | 35 ++++ .../gitnuro/git/workspace/DoCommitUseCase.kt | 23 ++- .../jetpackduba/gitnuro/models/AuthorInfo.kt | 10 +- .../gitnuro/models/SignOffConfig.kt | 6 + .../jetpackduba/gitnuro/ui/RepositoryOpen.kt | 8 + .../gitnuro/ui/UncommitedChanges.kt | 51 +---- .../ui/components/RepositoriesTabPanel.kt | 7 +- .../gitnuro/ui/dialogs/QuickActionsDialog.kt | 4 +- .../gitnuro/ui/dialogs/SignOffDialog.kt | 174 ++++++++++++++++++ .../viewmodels/SignOffDialogViewModel.kt | 40 ++++ .../gitnuro/viewmodels/TabViewModelsHolder.kt | 2 + src/main/resources/sign.svg | 1 + 15 files changed, 360 insertions(+), 53 deletions(-) create mode 100644 src/main/kotlin/com/jetpackduba/gitnuro/git/config/LoadSignOffConfigUseCase.kt create mode 100644 src/main/kotlin/com/jetpackduba/gitnuro/git/config/LocalConfigConstants.kt create mode 100644 src/main/kotlin/com/jetpackduba/gitnuro/git/config/SaveLocalRepositoryConfigUseCase.kt create mode 100644 src/main/kotlin/com/jetpackduba/gitnuro/models/SignOffConfig.kt create mode 100644 src/main/kotlin/com/jetpackduba/gitnuro/ui/dialogs/SignOffDialog.kt create mode 100644 src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/SignOffDialogViewModel.kt create mode 100644 src/main/resources/sign.svg diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/Icons.kt b/src/main/kotlin/com/jetpackduba/gitnuro/Icons.kt index e7a7300..b89dc8a 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/Icons.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/Icons.kt @@ -42,6 +42,7 @@ object AppIcons { const val REVERT = "revert.svg" const val SEARCH = "search.svg" const val SETTINGS = "settings.svg" + const val SIGN = "sign.svg" const val SOURCE = "source.svg" const val START = "start.svg" const val STASH = "stash.svg" diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/git/config/LoadSignOffConfigUseCase.kt b/src/main/kotlin/com/jetpackduba/gitnuro/git/config/LoadSignOffConfigUseCase.kt new file mode 100644 index 0000000..d08c04b --- /dev/null +++ b/src/main/kotlin/com/jetpackduba/gitnuro/git/config/LoadSignOffConfigUseCase.kt @@ -0,0 +1,35 @@ +package com.jetpackduba.gitnuro.git.config + +import com.jetpackduba.gitnuro.extensions.nullIfEmpty +import com.jetpackduba.gitnuro.models.SignOffConfig +import org.eclipse.jgit.lib.Repository +import org.eclipse.jgit.storage.file.FileBasedConfig +import java.io.File +import javax.inject.Inject + + +class LoadSignOffConfigUseCase @Inject constructor() { + operator fun invoke(repository: Repository): SignOffConfig { + val configFile = File(repository.directory, LocalConfigConstants.CONFIG_FILE_NAME) + configFile.createNewFile() + + val config = FileBasedConfig(configFile, repository.fs) + config.load() + + val enabled = config.getBoolean( + LocalConfigConstants.SignOff.SECTION, + null, + LocalConfigConstants.SignOff.FIELD_ENABLED, + LocalConfigConstants.SignOff.DEFAULT_SIGN_OFF_ENABLED + ) + + + val format = config.getString( + LocalConfigConstants.SignOff.SECTION, + null, + LocalConfigConstants.SignOff.FIELD_FORMAT + )?.nullIfEmpty ?: LocalConfigConstants.SignOff.DEFAULT_SIGN_OFF_FORMAT + + return SignOffConfig(enabled, format) + } +} diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/git/config/LocalConfigConstants.kt b/src/main/kotlin/com/jetpackduba/gitnuro/git/config/LocalConfigConstants.kt new file mode 100644 index 0000000..7a379de --- /dev/null +++ b/src/main/kotlin/com/jetpackduba/gitnuro/git/config/LocalConfigConstants.kt @@ -0,0 +1,16 @@ +package com.jetpackduba.gitnuro.git.config + +object LocalConfigConstants { + const val CONFIG_FILE_NAME = "gitnuro" + + object SignOff { + const val SECTION = "signoff" + const val FIELD_ENABLED = "enabled" + const val FIELD_FORMAT = "format" + + const val DEFAULT_SIGN_OFF_FORMAT_USER = "%user" + const val DEFAULT_SIGN_OFF_FORMAT_EMAIL = "%email" + const val DEFAULT_SIGN_OFF_FORMAT = "Signed-off-by: $DEFAULT_SIGN_OFF_FORMAT_USER <$DEFAULT_SIGN_OFF_FORMAT_EMAIL>" + const val DEFAULT_SIGN_OFF_ENABLED = false + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/git/config/SaveLocalRepositoryConfigUseCase.kt b/src/main/kotlin/com/jetpackduba/gitnuro/git/config/SaveLocalRepositoryConfigUseCase.kt new file mode 100644 index 0000000..5da914f --- /dev/null +++ b/src/main/kotlin/com/jetpackduba/gitnuro/git/config/SaveLocalRepositoryConfigUseCase.kt @@ -0,0 +1,35 @@ +package com.jetpackduba.gitnuro.git.config + +import com.jetpackduba.gitnuro.models.SignOffConfig +import org.eclipse.jgit.lib.Repository +import org.eclipse.jgit.storage.file.FileBasedConfig +import java.io.File +import javax.inject.Inject + +class SaveLocalRepositoryConfigUseCase @Inject constructor() { + operator fun invoke( + repository: Repository, + signOffConfig: SignOffConfig, + ) { + val configFile = File(repository.directory, LocalConfigConstants.CONFIG_FILE_NAME) + configFile.createNewFile() + + val config = FileBasedConfig(configFile, repository.fs) + + config.setBoolean( + LocalConfigConstants.SignOff.SECTION, + null, + LocalConfigConstants.SignOff.FIELD_ENABLED, + signOffConfig.isEnabled + ) + + config.setString( + LocalConfigConstants.SignOff.SECTION, + null, + LocalConfigConstants.SignOff.FIELD_FORMAT, + signOffConfig.format + ) + + config.save() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/git/workspace/DoCommitUseCase.kt b/src/main/kotlin/com/jetpackduba/gitnuro/git/workspace/DoCommitUseCase.kt index 58497eb..51eef3b 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/git/workspace/DoCommitUseCase.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/git/workspace/DoCommitUseCase.kt @@ -1,5 +1,8 @@ package com.jetpackduba.gitnuro.git.workspace +import com.jetpackduba.gitnuro.git.author.LoadAuthorUseCase +import com.jetpackduba.gitnuro.git.config.LoadSignOffConfigUseCase +import com.jetpackduba.gitnuro.git.config.LocalConfigConstants import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.eclipse.jgit.api.Git @@ -7,15 +10,31 @@ import org.eclipse.jgit.lib.PersonIdent import org.eclipse.jgit.revwalk.RevCommit import javax.inject.Inject -class DoCommitUseCase @Inject constructor() { +class DoCommitUseCase @Inject constructor( + private val loadSignOffConfigUseCase: LoadSignOffConfigUseCase, + private val loadAuthorUseCase: LoadAuthorUseCase, +) { suspend operator fun invoke( git: Git, message: String, amend: Boolean, author: PersonIdent?, ): RevCommit = withContext(Dispatchers.IO) { + val signOffConfig = loadSignOffConfigUseCase(git.repository) + + val finalMessage = if(signOffConfig.isEnabled) { + val authorToSign = author ?: loadAuthorUseCase(git).toPersonIdent() + + val signature = signOffConfig.format + .replace(LocalConfigConstants.SignOff.DEFAULT_SIGN_OFF_FORMAT_USER, authorToSign.name) + .replace(LocalConfigConstants.SignOff.DEFAULT_SIGN_OFF_FORMAT_EMAIL, authorToSign.emailAddress) + + "$message\n\n$signature" + } else + message + git.commit() - .setMessage(message) + .setMessage(finalMessage) .setAllowEmpty(amend) // Only allow empty commits when amending .setAmend(amend) .setAuthor(author) diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/models/AuthorInfo.kt b/src/main/kotlin/com/jetpackduba/gitnuro/models/AuthorInfo.kt index 74e35f1..cf1d38d 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/models/AuthorInfo.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/models/AuthorInfo.kt @@ -1,8 +1,16 @@ package com.jetpackduba.gitnuro.models +import org.eclipse.jgit.lib.PersonIdent + data class AuthorInfo( val globalName: String?, val globalEmail: String?, val name: String?, val email: String?, -) \ No newline at end of file +) { + fun toPersonIdent() = PersonIdent( + name ?: globalName ?: "", + email ?: globalEmail ?: "", + ) +} + diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/models/SignOffConfig.kt b/src/main/kotlin/com/jetpackduba/gitnuro/models/SignOffConfig.kt new file mode 100644 index 0000000..7cfec4f --- /dev/null +++ b/src/main/kotlin/com/jetpackduba/gitnuro/models/SignOffConfig.kt @@ -0,0 +1,6 @@ +package com.jetpackduba.gitnuro.models + +data class SignOffConfig( + val isEnabled: Boolean, + val format: String, +) diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt index 92180be..fb959d8 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt @@ -24,6 +24,7 @@ import com.jetpackduba.gitnuro.git.DiffEntryType import com.jetpackduba.gitnuro.keybindings.KeybindingOption import com.jetpackduba.gitnuro.keybindings.matchesBinding import com.jetpackduba.gitnuro.ui.components.PrimaryButton +import com.jetpackduba.gitnuro.ui.components.gitnuroDynamicViewModel import com.jetpackduba.gitnuro.ui.dialogs.* import com.jetpackduba.gitnuro.ui.diff.Diff import com.jetpackduba.gitnuro.ui.log.Log @@ -53,6 +54,7 @@ fun RepositoryOpenPage( var showNewBranchDialog by remember { mutableStateOf(false) } var showStashWithMessageDialog by remember { mutableStateOf(false) } var showQuickActionsDialog by remember { mutableStateOf(false) } + var showSignOffDialog by remember { mutableStateOf(false) } if (showNewBranchDialog) { NewBranchDialog( @@ -93,9 +95,15 @@ fun RepositoryOpenPage( QuickActionType.OPEN_DIR_IN_FILE_MANAGER -> tabViewModel.openFolderInFileExplorer() QuickActionType.CLONE -> onShowCloneDialog() QuickActionType.REFRESH -> tabViewModel.refreshAll() + QuickActionType.SIGN_OFF -> showSignOffDialog = true } }, ) + } else if (showSignOffDialog) { + SignOffDialog( + viewModel = gitnuroDynamicViewModel(), + onClose = { showSignOffDialog = false }, + ) } val focusRequester = remember { FocusRequester() } diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt index 2114c4a..e145272 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CornerSize +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowDropDown @@ -43,10 +44,7 @@ import com.jetpackduba.gitnuro.keybindings.KeybindingOption import com.jetpackduba.gitnuro.keybindings.matchesBinding import com.jetpackduba.gitnuro.theme.* import com.jetpackduba.gitnuro.ui.components.* -import com.jetpackduba.gitnuro.ui.context_menu.ContextMenu -import com.jetpackduba.gitnuro.ui.context_menu.ContextMenuElement -import com.jetpackduba.gitnuro.ui.context_menu.EntryType -import com.jetpackduba.gitnuro.ui.context_menu.statusEntriesContextMenuItems +import com.jetpackduba.gitnuro.ui.context_menu.* import com.jetpackduba.gitnuro.ui.dialogs.CommitAuthorDialog import com.jetpackduba.gitnuro.viewmodels.CommitterDataRequestState import com.jetpackduba.gitnuro.viewmodels.StageState @@ -306,7 +304,7 @@ fun UncommitedChanges( onAmendChecked = { isAmend -> statusViewModel.amend(isAmend) }, - onCommit = { doCommit() }, + onCommit = doCommit, ) } } @@ -322,8 +320,6 @@ fun UncommitedChangesButtons( onAmendChecked: (Boolean) -> Unit, onCommit: () -> Unit ) { - var showDropDownMenu by remember { mutableStateOf(false) } - val buttonText = if (isAmend) "Amend" else @@ -368,47 +364,8 @@ fun UncommitedChangesButtons( onCommit() }, enabled = canCommit || (canAmend && isAmend), - shape = MaterialTheme.shapes.small.copy(topEnd = CornerSize(0.dp), bottomEnd = CornerSize(0.dp)) + shape = RoundedCornerShape(4.dp) ) - Spacer( - modifier = Modifier - .width(1.dp) - .height(36.dp), - ) - - Box( - modifier = Modifier - .height(36.dp) - .clip(MaterialTheme.shapes.small.copy(topStart = CornerSize(0.dp), bottomStart = CornerSize(0.dp))) - .background(MaterialTheme.colors.primary) - .handMouseClickable { showDropDownMenu = true } - ) { - Icon( - Icons.Default.ArrowDropDown, - contentDescription = null, - tint = Color.White, - modifier = Modifier - .padding(horizontal = 8.dp) - .align(Alignment.Center), - ) - DropdownMenu( - onDismissRequest = { - showDropDownMenu = false - }, - content = { - /*DropDownContent( - enabled = canAmend, - dropDownContentData = DropDownContentData( - label = "Amend previous commit", - icon = null, - onClick = onCommit - ), - onDismiss = { showDropDownMenu = false } - )*/ - }, - expanded = showDropDownMenu, - ) - } } } } diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/components/RepositoriesTabPanel.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/components/RepositoriesTabPanel.kt index 1b19c72..2cbd7a0 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/components/RepositoriesTabPanel.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/components/RepositoriesTabPanel.kt @@ -26,13 +26,13 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.jetpackduba.gitnuro.AppIcons -import com.jetpackduba.gitnuro.managers.AppStateManager import com.jetpackduba.gitnuro.LocalTabScope import com.jetpackduba.gitnuro.di.AppComponent import com.jetpackduba.gitnuro.di.DaggerTabComponent import com.jetpackduba.gitnuro.di.TabComponent import com.jetpackduba.gitnuro.extensions.handMouseClickable import com.jetpackduba.gitnuro.extensions.handOnHover +import com.jetpackduba.gitnuro.managers.AppStateManager import com.jetpackduba.gitnuro.viewmodels.TabViewModel import com.jetpackduba.gitnuro.viewmodels.TabViewModelsHolder import kotlinx.coroutines.delay @@ -264,9 +264,12 @@ inline fun gitnuroViewModel(): T { tab.tabViewModelsHolder.viewModels[T::class] as T } } + @Composable inline fun gitnuroDynamicViewModel(): T { val tab = LocalTabScope.current - return tab.tabViewModelsHolder.dynamicViewModel(T::class) as T + return remember(tab) { + tab.tabViewModelsHolder.dynamicViewModel(T::class) as T + } } \ No newline at end of file diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/dialogs/QuickActionsDialog.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/dialogs/QuickActionsDialog.kt index 837307e..604503e 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/dialogs/QuickActionsDialog.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/dialogs/QuickActionsDialog.kt @@ -36,6 +36,7 @@ fun QuickActionsDialog( QuickAction(AppIcons.CODE, "Open repository in file manager", QuickActionType.OPEN_DIR_IN_FILE_MANAGER), QuickAction(AppIcons.DOWNLOAD, "Clone new repository", QuickActionType.CLONE), QuickAction(AppIcons.REFRESH, "Refresh repository data", QuickActionType.REFRESH), + QuickAction(AppIcons.SIGN, "Signoff config", QuickActionType.SIGN_OFF), ) } @@ -126,5 +127,6 @@ data class QuickAction(val icon: String, val title: String, val type: QuickActio enum class QuickActionType { OPEN_DIR_IN_FILE_MANAGER, CLONE, - REFRESH; + REFRESH, + SIGN_OFF } \ No newline at end of file diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/dialogs/SignOffDialog.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/dialogs/SignOffDialog.kt new file mode 100644 index 0000000..066dfc2 --- /dev/null +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/dialogs/SignOffDialog.kt @@ -0,0 +1,174 @@ +package com.jetpackduba.gitnuro.ui.dialogs + +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.material.Checkbox +import androidx.compose.material.Icon +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.focus.FocusRequester +import androidx.compose.ui.focus.focusProperties +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.jetpackduba.gitnuro.AppIcons +import com.jetpackduba.gitnuro.extensions.handMouseClickable +import com.jetpackduba.gitnuro.ui.components.AdjustableOutlinedTextField +import com.jetpackduba.gitnuro.ui.components.PrimaryButton +import com.jetpackduba.gitnuro.viewmodels.SignOffDialogViewModel +import com.jetpackduba.gitnuro.viewmodels.SignOffState + +@Composable +fun SignOffDialog( + viewModel: SignOffDialogViewModel, + onClose: () -> Unit, +) { + val state = viewModel.state.collectAsState().value + + LaunchedEffect(viewModel) { + viewModel.loadSignOffFormat() + } + + + var signOffField by remember(viewModel, state) { + val signOff = if (state is SignOffState.Loaded) { + state.signOffConfig.format + } else { + "" + } + + mutableStateOf(signOff) + } + + var enabledSignOff by remember(viewModel, state) { + val signOff = if (state is SignOffState.Loaded) { + state.signOffConfig.isEnabled + } else { + true + } + + mutableStateOf(signOff) + } + + val signOffFieldFocusRequester = remember { FocusRequester() } + val buttonFieldFocusRequester = remember { FocusRequester() } + + MaterialDialog(onCloseRequested = onClose) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier.width(IntrinsicSize.Min), + ) { + Icon( + painterResource(AppIcons.SIGN), + contentDescription = null, + modifier = Modifier + .padding(bottom = 16.dp) + .size(64.dp), + tint = MaterialTheme.colors.onBackground, + ) + + Text( + text = "Edit sign off", + modifier = Modifier + .padding(bottom = 8.dp), + color = MaterialTheme.colors.onBackground, + style = MaterialTheme.typography.body1, + fontWeight = FontWeight.SemiBold, + ) + + Text( + text = "Enable or disable the signoff or adjust its format", + modifier = Modifier + .padding(bottom = 16.dp), + color = MaterialTheme.colors.onBackground, + style = MaterialTheme.typography.body2, + textAlign = TextAlign.Center, + ) + + AdjustableOutlinedTextField( + modifier = Modifier + .focusRequester(signOffFieldFocusRequester) + .focusProperties { + this.next = buttonFieldFocusRequester + } + .width(300.dp), + value = signOffField, + enabled = state is SignOffState.Loaded, + maxLines = 1, + onValueChange = { + signOffField = it + }, + ) + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.handMouseClickable( + interactionSource = remember { MutableInteractionSource() }, + indication = null, + ) { + if (state is SignOffState.Loaded) { + enabledSignOff = !enabledSignOff + } + } + .fillMaxWidth() + .padding(top = 8.dp) + ) { + Checkbox( + checked = enabledSignOff, + enabled = state is SignOffState.Loaded, + onCheckedChange = { + enabledSignOff = it + }, + modifier = Modifier + .padding(all = 8.dp) + .size(12.dp) + ) + + Text( + "Enable signoff for this repository", + style = MaterialTheme.typography.body2, + color = MaterialTheme.colors.onBackground, + ) + } + + Row( + modifier = Modifier + .padding(top = 16.dp) + .align(Alignment.End) + ) { + PrimaryButton( + text = "Cancel", + modifier = Modifier.padding(end = 8.dp), + onClick = onClose, + backgroundColor = Color.Transparent, + textColor = MaterialTheme.colors.onBackground, + ) + PrimaryButton( + modifier = Modifier + .focusRequester(buttonFieldFocusRequester) + .focusProperties { + this.previous = signOffFieldFocusRequester + this.next = signOffFieldFocusRequester + }, + enabled = signOffField.isNotBlank() && state is SignOffState.Loaded, + onClick = { + viewModel.saveSignOffFormat(enabledSignOff, signOffField) + onClose() + }, + text = "Save" + ) + } + } + } + + LaunchedEffect(state) { + signOffFieldFocusRequester.requestFocus() + } +} diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/SignOffDialogViewModel.kt b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/SignOffDialogViewModel.kt new file mode 100644 index 0000000..5d3e62f --- /dev/null +++ b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/SignOffDialogViewModel.kt @@ -0,0 +1,40 @@ +package com.jetpackduba.gitnuro.viewmodels + +import com.jetpackduba.gitnuro.git.RefreshType +import com.jetpackduba.gitnuro.git.TabState +import com.jetpackduba.gitnuro.git.config.LoadSignOffConfigUseCase +import com.jetpackduba.gitnuro.git.config.SaveLocalRepositoryConfigUseCase +import com.jetpackduba.gitnuro.models.SignOffConfig +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject + +class SignOffDialogViewModel @Inject constructor( + private val tabState: TabState, + private val loadSignOffConfigUseCase: LoadSignOffConfigUseCase, + private val saveLocalRepositoryConfigUseCase: SaveLocalRepositoryConfigUseCase, +) { + private val _state = MutableStateFlow(SignOffState.Loading) + val state = _state.asStateFlow() + + fun loadSignOffFormat() = tabState.runOperation( + showError = true, + refreshType = RefreshType.NONE, + ) { git -> + val signOffConfig = loadSignOffConfigUseCase(git.repository) + + _state.value = SignOffState.Loaded(signOffConfig) + } + + fun saveSignOffFormat(newIsEnabled: Boolean, newFormat: String) = tabState.runOperation( + showError = true, + refreshType = RefreshType.NONE, + ) { git -> + saveLocalRepositoryConfigUseCase(git.repository, SignOffConfig(newIsEnabled, newFormat)) + } +} + +sealed interface SignOffState { + object Loading : SignOffState + data class Loaded(val signOffConfig: SignOffConfig) : SignOffState +} \ No newline at end of file diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/TabViewModelsHolder.kt b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/TabViewModelsHolder.kt index 4e4c890..b313131 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/TabViewModelsHolder.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/TabViewModelsHolder.kt @@ -23,6 +23,7 @@ class TabViewModelsHolder @Inject constructor( private val authorViewModelProvider: Provider, private val changeDefaultUpstreamBranchViewModelProvider: Provider, private val submoduleDialogViewModelProvider: Provider, + private val signOffDialogViewModelProvider: Provider, ) { val viewModels = mapOf( logViewModel::class to logViewModel, @@ -43,6 +44,7 @@ class TabViewModelsHolder @Inject constructor( AuthorViewModel::class -> authorViewModelProvider.get() ChangeDefaultUpstreamBranchViewModel::class -> changeDefaultUpstreamBranchViewModelProvider.get() SubmoduleDialogViewModel::class -> submoduleDialogViewModelProvider.get() + SignOffDialogViewModel::class -> signOffDialogViewModelProvider.get() else -> throw NotImplementedError("View model provider not implemented") } } diff --git a/src/main/resources/sign.svg b/src/main/resources/sign.svg new file mode 100644 index 0000000..def954e --- /dev/null +++ b/src/main/resources/sign.svg @@ -0,0 +1 @@ + \ No newline at end of file