diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/SharedRepositoryStateManager.kt b/src/main/kotlin/com/jetpackduba/gitnuro/SharedRepositoryStateManager.kt index dab2496..d56a01b 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/SharedRepositoryStateManager.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/SharedRepositoryStateManager.kt @@ -38,7 +38,12 @@ class SharedRepositoryStateManager @Inject constructor( } } launch { - tabState.refreshFlowFiltered(RefreshType.ALL_DATA, RefreshType.REPO_STATE) { + tabState.refreshFlowFiltered( + RefreshType.ALL_DATA, + RefreshType.REPO_STATE, + RefreshType.UNCOMMITTED_CHANGES, + RefreshType.UNCOMMITTED_CHANGES_AND_LOG + ) { updateRepositoryState(tabState.git) } } diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/credentials/CredentialsCacheRepository.kt b/src/main/kotlin/com/jetpackduba/gitnuro/credentials/CredentialsCacheRepository.kt index 3e6c6c8..c2a01ca 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/credentials/CredentialsCacheRepository.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/credentials/CredentialsCacheRepository.kt @@ -8,6 +8,7 @@ import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.SecretKeySpec import javax.inject.Inject import javax.inject.Singleton +import kotlin.random.Random private const val KEY_LENGTH = 16 @@ -60,9 +61,8 @@ class CredentialsCacheRepository @Inject constructor() { } private fun String.cipherEncrypt(): String { - val secretKeySpec = SecretKeySpec(encryptionKey.toByteArray(), "AES") - val iv = encryptionKey.toByteArray() - val ivParameterSpec = IvParameterSpec(iv) + val secretKeySpec = SecretKeySpec(encryptionKey, "AES") + val ivParameterSpec = IvParameterSpec(encryptionKey) val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec) @@ -72,9 +72,8 @@ class CredentialsCacheRepository @Inject constructor() { } private fun String.cipherDecrypt(): String { - val secretKeySpec = SecretKeySpec(encryptionKey.toByteArray(), "AES") - val iv = encryptionKey.toByteArray() - val ivParameterSpec = IvParameterSpec(iv) + val secretKeySpec = SecretKeySpec(encryptionKey, "AES") + val ivParameterSpec = IvParameterSpec(encryptionKey) val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec) @@ -84,11 +83,11 @@ class CredentialsCacheRepository @Inject constructor() { return String(decryptedValue) } - private fun getRandomKey(): String { - val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9') + "#!$%=?-_.,@ยต*:;+~".toList() - return (1..KEY_LENGTH) - .map { allowedChars.random() } - .joinToString("") + private fun getRandomKey(): ByteArray { + val byteArray = ByteArray(KEY_LENGTH) + Random.Default.nextBytes(byteArray) + + return byteArray } } diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/log/Log.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/log/Log.kt index 2a5a89a..c1a8556 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/log/Log.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/log/Log.kt @@ -67,6 +67,7 @@ import kotlinx.coroutines.launch import org.eclipse.jgit.lib.Ref import org.eclipse.jgit.lib.RepositoryState import org.eclipse.jgit.revwalk.RevCommit +import kotlin.math.log private val colors = listOf( Color(0xFF42a5f5), @@ -233,18 +234,27 @@ private fun LogLoaded( repositoryState = repositoryState, selectedItem = selectedItem, commitList = commitList, - logViewModel = logViewModel, graphWidth = graphWidth, commitsLimit = logStatus.commitsLimit, - onMerge = { ref -> - logViewModel.mergeBranch(ref) - }, - onRebase = { ref -> - logViewModel.rebaseBranch(ref) - }, - onShowLogDialog = { dialog -> - logViewModel.showDialog(dialog) - } + onMerge = { ref -> logViewModel.mergeBranch(ref) }, + onRebase = { ref -> logViewModel.rebaseBranch(ref) }, + onShowLogDialog = { dialog -> logViewModel.showDialog(dialog) }, + onCheckoutCommit = { logViewModel.checkoutCommit(it) }, + onRevertCommit = { logViewModel.revertCommit(it) }, + onCherryPickCommit = { logViewModel.cherryPickCommit(it) }, + onCheckoutRemoteBranch = { logViewModel.checkoutRemoteBranch(it) }, + onCheckoutRef = { logViewModel.checkoutRef(it) }, + onRebaseInteractive = { logViewModel.rebaseInteractive(it) }, + onCommitSelected = { logViewModel.selectCommit(it) }, + onUncommittedChangesSelected = { logViewModel.selectUncommittedChanges() }, + onDeleteStash = { logViewModel.deleteStash(it) }, + onApplyStash = { logViewModel.applyStash(it) }, + onPopStash = { logViewModel.popStash(it) }, + onDeleteBranch = { logViewModel.deleteBranch(it) }, + onDeleteRemoteBranch = { logViewModel.deleteRemoteBranch(it) }, + onDeleteTag = { logViewModel.deleteTag(it) }, + onPushToRemoteBranch = { logViewModel.pushToRemoteBranch(it) }, + onPullFromRemoteBranch = { logViewModel.pullFromRemoteBranch(it) }, ) val density = LocalDensity.current.density @@ -443,10 +453,25 @@ fun CommitsList( repositoryState: RepositoryState, selectedItem: SelectedItem, commitList: GraphCommitList, - logViewModel: LogViewModel, commitsLimit: Int, + onCheckoutCommit: (GraphNode) -> Unit, + onRevertCommit: (GraphNode) -> Unit, + onCherryPickCommit: (GraphNode) -> Unit, + onCheckoutRemoteBranch: (Ref) -> Unit, + onCheckoutRef: (Ref) -> Unit, onMerge: (Ref) -> Unit, onRebase: (Ref) -> Unit, + onRebaseInteractive: (GraphNode) -> Unit, + onCommitSelected: (GraphNode) -> Unit, + onUncommittedChangesSelected: () -> Unit, + onDeleteStash: (GraphNode) -> Unit, + onApplyStash: (GraphNode) -> Unit, + onPopStash: (GraphNode) -> Unit, + onDeleteBranch: (Ref) -> Unit, + onDeleteRemoteBranch: (Ref) -> Unit, + onDeleteTag: (Ref) -> Unit, + onPushToRemoteBranch: (Ref) -> Unit, + onPullFromRemoteBranch: (Ref) -> Unit, onShowLogDialog: (LogDialog) -> Unit, graphWidth: Dp, horizontalScrollState: ScrollState, @@ -478,7 +503,7 @@ fun CommitsList( modifier = Modifier.height(MaterialTheme.linesHeight.logCommitHeight) .clipToBounds() .fillMaxWidth() - .clickable { logViewModel.selectUncommittedChanges() } + .clickable { onUncommittedChangesSelected() } ) { UncommittedChangesGraphNode( hasPreviousCommits = commitList.isNotEmpty(), @@ -501,7 +526,6 @@ fun CommitsList( items(items = commitList) { graphNode -> CommitLine( graphWidth = graphWidth, - logViewModel = logViewModel, graphNode = graphNode, isSelected = selectedCommit?.name == graphNode.name, currentBranch = logStatus.currentBranch, @@ -511,13 +535,23 @@ fun CommitsList( showCreateNewTag = { onShowLogDialog(LogDialog.NewTag(graphNode)) }, resetBranch = { onShowLogDialog(LogDialog.ResetBranch(graphNode)) }, onMergeBranch = onMerge, + onDeleteBranch = onDeleteBranch, + onDeleteRemoteBranch = onDeleteRemoteBranch, + onDeleteTag = onDeleteTag, + onPushToRemoteBranch = onPushToRemoteBranch, + onPullFromRemoteBranch = onPullFromRemoteBranch, onRebaseBranch = onRebase, - onRebaseInteractive = { logViewModel.rebaseInteractive(graphNode) }, - onRevCommitSelected = { logViewModel.selectLogLine(graphNode) }, + onRebaseInteractive = { onRebaseInteractive(graphNode) }, + onRevCommitSelected = { onCommitSelected(graphNode) }, onChangeDefaultUpstreamBranch = { onShowLogDialog(LogDialog.ChangeDefaultBranch(it)) }, - onDeleteStash = { logViewModel.deleteStash(graphNode) }, - onApplyStash = { logViewModel.applyStash(graphNode) }, - onPopStash = { logViewModel.popStash(graphNode) }, + onDeleteStash = { onDeleteStash(graphNode) }, + onApplyStash = { onApplyStash(graphNode) }, + onPopStash = { onPopStash(graphNode) }, + onCheckoutCommit = { onCheckoutCommit(graphNode) }, + onRevertCommit = { onRevertCommit(graphNode) }, + onCherryPickCommit = { onCherryPickCommit(graphNode) }, + onCheckoutRemoteBranch = onCheckoutRemoteBranch, + onCheckoutRef = onCheckoutRef, ) } @@ -751,7 +785,6 @@ fun SummaryEntry( @Composable private fun CommitLine( graphWidth: Dp, - logViewModel: LogViewModel, graphNode: GraphNode, isSelected: Boolean, currentBranch: Ref?, @@ -763,9 +796,19 @@ private fun CommitLine( onPopStash: () -> Unit, onDeleteStash: () -> Unit, onMergeBranch: (Ref) -> Unit, + onDeleteBranch: (Ref) -> Unit, + onDeleteRemoteBranch: (Ref) -> Unit, + onDeleteTag: (Ref) -> Unit, + onPushToRemoteBranch: (Ref) -> Unit, + onPullFromRemoteBranch: (Ref) -> Unit, onRebaseBranch: (Ref) -> Unit, onRevCommitSelected: () -> Unit, onRebaseInteractive: () -> Unit, + onCheckoutCommit: () -> Unit, + onRevertCommit: () -> Unit, + onCherryPickCommit: () -> Unit, + onCheckoutRemoteBranch: (Ref) -> Unit, + onCheckoutRef: (Ref) -> Unit, onChangeDefaultUpstreamBranch: (Ref) -> Unit, horizontalScrollState: ScrollState, ) { @@ -781,11 +824,11 @@ private fun CommitLine( ) } else { logContextMenu( - onCheckoutCommit = { logViewModel.checkoutCommit(graphNode) }, + onCheckoutCommit = onCheckoutCommit,//{ logViewModel.checkoutCommit(graphNode) }, onCreateNewBranch = showCreateNewBranch, onCreateNewTag = showCreateNewTag, - onRevertCommit = { logViewModel.revertCommit(graphNode) }, - onCherryPickCommit = { logViewModel.cherrypickCommit(graphNode) }, + onRevertCommit = onRevertCommit,//{ logViewModel.revertCommit(graphNode) }, + onCherryPickCommit = onCherryPickCommit, //{ logViewModel.cherryPickCommit(graphNode) }, onRebaseInteractive = onRebaseInteractive, onResetBranch = { resetBranch() }, isLastCommit = isLastCommitOfCurrentBranch @@ -848,18 +891,18 @@ private fun CommitLine( currentBranch = currentBranch, onCheckoutRef = { ref -> if (ref.isRemote && ref.isBranch) { - logViewModel.checkoutRemoteBranch(ref) + onCheckoutRemoteBranch(ref) } else { - logViewModel.checkoutRef(ref) + onCheckoutRef(ref) } }, onMergeBranch = { ref -> onMergeBranch(ref) }, - onDeleteBranch = { ref -> logViewModel.deleteBranch(ref) }, - onDeleteRemoteBranch = { ref -> logViewModel.deleteRemoteBranch(ref) }, - onDeleteTag = { ref -> logViewModel.deleteTag(ref) }, + onDeleteBranch = { ref -> onDeleteBranch(ref) }, + onDeleteRemoteBranch = { ref -> onDeleteRemoteBranch(ref) }, + onDeleteTag = { ref -> onDeleteTag(ref) }, onRebaseBranch = { ref -> onRebaseBranch(ref) }, - onPushRemoteBranch = { ref -> logViewModel.pushToRemoteBranch(ref) }, - onPullRemoteBranch = { ref -> logViewModel.pullFromRemoteBranch(ref) }, + onPushRemoteBranch = { ref -> onPushToRemoteBranch(ref) }, + onPullRemoteBranch = { ref -> onPullFromRemoteBranch(ref) }, onChangeDefaultUpstreamBranch = { ref -> onChangeDefaultUpstreamBranch(ref) }, ) } diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/LogViewModel.kt b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/LogViewModel.kt index 866b4b1..26ec8a9 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/LogViewModel.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/LogViewModel.kt @@ -180,17 +180,18 @@ class LogViewModel @Inject constructor( fun resetToCommit(revCommit: RevCommit, resetType: ResetType) = tabState.safeProcessing( refreshType = RefreshType.ALL_DATA, title = "Branch reset", - subtitle = "Reseting branch to commit ${revCommit.shortName}", + subtitle = "Resetting branch to commit ${revCommit.shortName}", taskType = TaskType.RESET_TO_COMMIT, ) { git -> resetToCommitUseCase(git, revCommit, resetType = resetType) } - fun cherrypickCommit(revCommit: RevCommit) = tabState.safeProcessing( + fun cherryPickCommit(revCommit: RevCommit) = tabState.safeProcessing( refreshType = RefreshType.UNCOMMITTED_CHANGES_AND_LOG, title = "Cherry-pick", subtitle = "Cherry-picking commit ${revCommit.shortName}", taskType = TaskType.CHERRY_PICK_COMMIT, + refreshEvenIfCrashes = true, ) { git -> cherryPickCommitUseCase(git, revCommit) } @@ -266,7 +267,7 @@ class LogViewModel @Inject constructor( NONE_MATCHING_INDEX } - fun selectLogLine(commit: GraphNode) = tabState.runOperation( + fun selectCommit(commit: GraphNode) = tabState.runOperation( refreshType = RefreshType.NONE, ) { tabState.newSelectedCommit(commit) diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/StatusViewModel.kt b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/StatusViewModel.kt index 84cf354..be68806 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/StatusViewModel.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/StatusViewModel.kt @@ -4,10 +4,7 @@ import androidx.compose.foundation.lazy.LazyListState import androidx.compose.ui.text.input.TextFieldValue import com.jetpackduba.gitnuro.SharedRepositoryStateManager import com.jetpackduba.gitnuro.TaskType -import com.jetpackduba.gitnuro.extensions.delayedStateChange -import com.jetpackduba.gitnuro.extensions.isMerging -import com.jetpackduba.gitnuro.extensions.isReverting -import com.jetpackduba.gitnuro.extensions.lowercaseContains +import com.jetpackduba.gitnuro.extensions.* import com.jetpackduba.gitnuro.git.RefreshType import com.jetpackduba.gitnuro.git.TabState import com.jetpackduba.gitnuro.git.author.LoadAuthorUseCase @@ -125,7 +122,10 @@ class StatusViewModel @Inject constructor( if (showAsTree) { StageStateUi.TreeLoaded( staged = entriesToTreeEntry(stageStateFiltered.staged, contractedDirectories) { it.filePath }, - unstaged = entriesToTreeEntry(stageStateFiltered.unstaged, contractedDirectories) { it.filePath }, + unstaged = entriesToTreeEntry( + stageStateFiltered.unstaged, + contractedDirectories + ) { it.filePath }, isPartiallyReloading = stageStateFiltered.isPartiallyReloading, ) } else { @@ -241,19 +241,19 @@ class StatusViewModel @Inject constructor( private suspend fun loadStatus(git: Git) { val previousStatus = _stageState.value - - val requiredMessageType = if (git.repository.repositoryState == RepositoryState.MERGING) { + val type = if ( + git.repository.repositoryState.isRebasing || + git.repository.repositoryState.isMerging || + git.repository.repositoryState.isReverting || + git.repository.repositoryState.isCherryPicking + ) { MessageType.MERGE } else { MessageType.NORMAL } - if (requiredMessageType != savedCommitMessage.messageType) { - savedCommitMessage = CommitMessage(messageByRepoState(git), requiredMessageType) - _commitMessageChangesFlow.emit(savedCommitMessage.message) - - } else if (savedCommitMessage.message.isEmpty()) { - savedCommitMessage = savedCommitMessage.copy(message = messageByRepoState(git)) + if (type != savedCommitMessage.type) { + savedCommitMessage = CommitMessage(messageByRepoState(git), type) _commitMessageChangesFlow.emit(savedCommitMessage.message) } @@ -300,15 +300,17 @@ class StatusViewModel @Inject constructor( } private fun messageByRepoState(git: Git): String { - val message: String? = if ( - git.repository.repositoryState.isMerging || - git.repository.repositoryState.isRebasing || - git.repository.repositoryState.isReverting - ) { - git.repository.readMergeCommitMsg() - } else { - git.repository.readCommitEditMsg() - } + val message: String? = + if ( + git.repository.repositoryState.isMerging || + git.repository.repositoryState.isRebasing || + git.repository.repositoryState.isReverting || + git.repository.repositoryState.isCherryPicking + ) { + git.repository.readMergeCommitMsg() + } else { + git.repository.readCommitEditMsg() + } //TODO this replace is a workaround until this issue gets fixed https://github.com/JetBrains/compose-jb/issues/615 return message.orEmpty().replace("\t", " ") @@ -579,7 +581,7 @@ sealed interface StageStateUi { } } -data class CommitMessage(val message: String, val messageType: MessageType) +data class CommitMessage(val message: String, val type: MessageType) enum class MessageType { NORMAL,