Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/main/kotlin/com/jetpackduba/gitnuro/models/CustomAction.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.jetpackduba.gitnuro.models

import kotlinx.serialization.Serializable

@Serializable
data class CustomAction(
val id: String,
val name: String,
val command: String,
val icon: String = "bolt" // default icon name
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ import com.jetpackduba.gitnuro.ui.dialogs.settings.ProxyType
import com.jetpackduba.gitnuro.viewmodels.TextDiffType
import com.jetpackduba.gitnuro.viewmodels.textDiffTypeFromValue
import kotlinx.coroutines.flow.MutableStateFlow
import com.jetpackduba.gitnuro.models.CustomAction
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.serialization.json.Json
import kotlinx.serialization.encodeToString
import java.io.File
import java.util.prefs.Preferences
import javax.inject.Inject
Expand Down Expand Up @@ -59,6 +61,7 @@ private const val PREF_GIT_PULL_REBASE = "gitPullRebase"
private const val PREF_GIT_PUSH_WITH_LEASE = "gitPushWithLease"

private const val PREF_VERIFY_SSL = "verifySsl"
private const val PREF_CUSTOM_ACTIONS = "customQuickActions"

private const val DEFAULT_SWAP_UNCOMMITTED_CHANGES = false
private const val DEFAULT_SHOW_CHANGES_AS_TREE = false
Expand Down Expand Up @@ -419,6 +422,24 @@ class AppSettingsRepository @Inject constructor() {
_proxyFlow.value = _proxyFlow.value.copy(hostPassword = value)
}

var customActions: List<CustomAction>
get() {
val json = preferences.get(PREF_CUSTOM_ACTIONS, null)
return if (json != null) {
try {
Json.decodeFromString<List<CustomAction>>(json)
} catch (e: Exception) {
emptyList()
}
} else {
emptyList()
}
}
set(value) {
val json = Json.encodeToString(value)
preferences.put(PREF_CUSTOM_ACTIONS, json)
}

fun saveCustomTheme(filePath: String) {
val file = File(filePath)
val content = file.readText()
Expand Down
6 changes: 5 additions & 1 deletion src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,17 @@ fun RepositoryOpenPage(
onClose = { showQuickActionsDialog = false },
onAction = {
showQuickActionsDialog = false
when (it) {
when (it.type) {
QuickActionType.OPEN_DIR_IN_FILE_MANAGER -> repositoryOpenViewModel.openFolderInFileExplorer()
QuickActionType.CLONE -> onShowCloneDialog()
QuickActionType.REFRESH -> repositoryOpenViewModel.refreshAll()
QuickActionType.SIGN_OFF -> showSignOffDialog = true
QuickActionType.CUSTOM_ACTION -> {
it.command?.let { command -> repositoryOpenViewModel.executeCustomAction(command) }
}
}
},
customActions = repositoryOpenViewModel.customActions
)
} else if (showSignOffDialog) {
SignOffDialog(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,22 @@ import org.jetbrains.compose.resources.painterResource
@Composable
fun QuickActionsDialog(
onClose: () -> Unit,
onAction: (QuickActionType) -> Unit,
onAction: (QuickAction) -> Unit,
customActions: List<com.jetpackduba.gitnuro.models.CustomAction>
) {

val textFieldFocusRequester = remember { FocusRequester() }
val items = remember {
listOf(
QuickAction(Res.drawable.code, "Open repository in file manager", QuickActionType.OPEN_DIR_IN_FILE_MANAGER),
QuickAction(Res.drawable.download, "Clone new repository", QuickActionType.CLONE),
QuickAction(Res.drawable.refresh, "Refresh repository data", QuickActionType.REFRESH),
QuickAction(Res.drawable.sign, "Signoff config", QuickActionType.SIGN_OFF),
)
val items = remember(customActions) {
(
listOf(
QuickAction(Res.drawable.code, "Open repository in file manager", QuickActionType.OPEN_DIR_IN_FILE_MANAGER),
QuickAction(Res.drawable.download, "Clone new repository", QuickActionType.CLONE),
QuickAction(Res.drawable.refresh, "Refresh repository data", QuickActionType.REFRESH),
QuickAction(Res.drawable.sign, "Signoff config", QuickActionType.SIGN_OFF),
) + customActions.map {
QuickAction(Res.drawable.bolt, it.name, QuickActionType.CUSTOM_ACTION, it.command)
}
).sortedBy { it.title }
}

var searchFilter by remember { mutableStateOf("") }
Expand Down Expand Up @@ -70,7 +75,7 @@ fun QuickActionsDialog(
} else if (keyEvent.matchesBinding(KeybindingOption.SIMPLE_ACCEPT)) {
val item = filteredItems.getOrNull(selectedIndex)
if (item != null)
onAction(item.type)
onAction(item)
true
} else
false
Expand Down Expand Up @@ -98,7 +103,7 @@ fun QuickActionsDialog(
.fillMaxWidth()
.clip(RoundedCornerShape(4.dp))
.backgroundIf(selectedIndex == index, MaterialTheme.colors.backgroundSelected)
.handMouseClickable { onAction(item.type) }
.handMouseClickable { onAction(item) }
) {
Icon(
painterResource(item.icon),
Expand All @@ -123,11 +128,12 @@ fun QuickActionsDialog(
}
}

data class QuickAction(val icon: DrawableResource, val title: String, val type: QuickActionType)
data class QuickAction(val icon: DrawableResource, val title: String, val type: QuickActionType, val command: String = "")

enum class QuickActionType {
OPEN_DIR_IN_FILE_MANAGER,
CLONE,
REFRESH,
SIGN_OFF
SIGN_OFF,
CUSTOM_ACTION
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Icon
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
Expand All @@ -25,6 +27,7 @@ import com.jetpackduba.gitnuro.extensions.handOnHover
import com.jetpackduba.gitnuro.extensions.toSmartSystemString
import com.jetpackduba.gitnuro.generated.resources.*
import com.jetpackduba.gitnuro.managers.Error
import com.jetpackduba.gitnuro.models.CustomAction
import com.jetpackduba.gitnuro.preferences.AvatarProviderType
import com.jetpackduba.gitnuro.repositories.DEFAULT_UI_SCALE
import com.jetpackduba.gitnuro.theme.*
Expand Down Expand Up @@ -72,6 +75,8 @@ val settings = listOf(
SettingsEntry.Section("Tools"),
SettingsEntry.Entry(Res.drawable.terminal, "Terminal") { Terminal(it) },
SettingsEntry.Entry(Res.drawable.info, "Logs") { Logs(it) },
SettingsEntry.Entry(Res.drawable.bolt, "Custom Actions") { CustomActions(it) },

)

val linesHeightTypesList = listOf(
Expand Down Expand Up @@ -370,6 +375,96 @@ fun Terminal(settingsViewModel: SettingsViewModel) {
)
}

@Composable
fun CustomActions(settingsViewModel: SettingsViewModel) {

var actions by remember { mutableStateOf(settingsViewModel.customActions.toMutableList()) }

Text(
text = "Custom Actions",
style = MaterialTheme.typography.body2,
color = MaterialTheme.colors.onBackground,
modifier = Modifier.fillMaxWidth(),
maxLines = 1
)

// Header row
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp)
) {
Text(
text = "Name",
style = MaterialTheme.typography.body2,
color = MaterialTheme.colors.onBackgroundSecondary,
modifier = Modifier.width(120.dp)
)
Spacer(Modifier.width(8.dp))
Text(
text = "Command",
style = MaterialTheme.typography.body2,
color = MaterialTheme.colors.onBackgroundSecondary,
modifier = Modifier.weight(1f)
)
Spacer(Modifier.width(8.dp))
// Empty space for Remove button
Spacer(Modifier.width(80.dp))
}

LazyColumn {
items(actions.size) { index ->
val command = actions[index]
Row(verticalAlignment = Alignment.CenterVertically) {
AdjustableOutlinedTextField(
value = command.name,
modifier = Modifier.width(120.dp),
onValueChange = { newName: String ->
actions = actions.toMutableList().also { it[index] = command.copy(name = newName) }
settingsViewModel.customActions = actions.toList()
},
singleLine = true
)
Spacer(Modifier.width(8.dp))
AdjustableOutlinedTextField(
value = command.command,
modifier = Modifier.weight(1f),
onValueChange = { newCmd: String ->
actions = actions.toMutableList().also { it[index] = command.copy(command = newCmd) }
settingsViewModel.customActions = actions.toList()
},
singleLine = true,
)
Spacer(Modifier.width(8.dp))
PrimaryButton(
text = "Remove",
onClick = {
actions = actions.toMutableList().also { it.removeAt(index) }
settingsViewModel.customActions = actions.toList()
}
)
}
Spacer(Modifier.height(8.dp))
}
}

Spacer(Modifier.height(8.dp))
PrimaryButton(
text = "Add Action",
onClick = {
actions = actions.toMutableList().apply {
add(CustomAction(
id = java.util.UUID.randomUUID().toString(),
name = "New Action",
command = "",
))
}
settingsViewModel.customActions = actions.toList()
}
)
}

@Composable
fun Logs(settingsViewModel: SettingsViewModel) {
SettingButton(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.jetpackduba.gitnuro.logging.printLog
import com.jetpackduba.gitnuro.managers.AppStateManager
import com.jetpackduba.gitnuro.managers.ErrorsManager
import com.jetpackduba.gitnuro.managers.newErrorNow
import com.jetpackduba.gitnuro.managers.IShellManager
import com.jetpackduba.gitnuro.models.AuthorInfoSimple
import com.jetpackduba.gitnuro.models.errorNotification
import com.jetpackduba.gitnuro.models.positiveNotification
Expand All @@ -25,6 +26,8 @@ import com.jetpackduba.gitnuro.ui.TabsManager
import com.jetpackduba.gitnuro.ui.VerticalSplitPaneConfig
import com.jetpackduba.gitnuro.updates.Update
import com.jetpackduba.gitnuro.updates.UpdatesRepository
import com.jetpackduba.gitnuro.repositories.AppSettingsRepository

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
Expand Down Expand Up @@ -72,6 +75,9 @@ class RepositoryOpenViewModel @Inject constructor(
private val globalMenuActionsViewModel: GlobalMenuActionsViewModel,
sharedRepositoryStateManager: SharedRepositoryStateManager,
updatesRepository: UpdatesRepository,
private val appSettingsRepository: AppSettingsRepository,
private val shellManager: IShellManager,

) : IVerticalSplitPaneConfig by verticalSplitPaneConfig,
IGlobalMenuActionsViewModel by globalMenuActionsViewModel {
private val errorsManager: ErrorsManager = tabState.errorsManager
Expand Down Expand Up @@ -110,6 +116,12 @@ class RepositoryOpenViewModel @Inject constructor(
var authorViewModel: AuthorViewModel? = null
private set

var customActions: List<com.jetpackduba.gitnuro.models.CustomAction>
get() = appSettingsRepository.customActions
set(value) {
appSettingsRepository.customActions = value
}

private var hasGitDirChanged = false

init {
Expand Down Expand Up @@ -361,8 +373,14 @@ class RepositoryOpenViewModel @Inject constructor(
fun closeLastView() = tabScope.launch {
tabState.closeLastView()
}
}

fun executeCustomAction(command: String) = tabState.runOperation(
showError = true,
refreshType = RefreshType.REPO_STATE,
) { git ->
shellManager.runCommandInPath(listOf(command), git.repository.workTree.absolutePath)
}
}

sealed interface BlameState {
data class Loading(val filePath: String) : BlameState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,12 @@ class SettingsViewModel @Inject constructor(
appSettingsRepository.proxyHostPassword = value
}

var customActions: List<com.jetpackduba.gitnuro.models.CustomAction>
get() = appSettingsRepository.customActions
set(value) {
appSettingsRepository.customActions = value
}

fun saveCustomTheme(filePath: String): Error? {
return try {
appSettingsRepository.saveCustomTheme(filePath)
Expand Down