diff --git a/designsystem/detekt-baseline.xml b/designsystem/detekt-baseline.xml
index 7b3fe37e..9d56895a 100644
--- a/designsystem/detekt-baseline.xml
+++ b/designsystem/detekt-baseline.xml
@@ -11,7 +11,6 @@
CyclomaticComplexMethod:DiffLine.kt$@Composable fun AslDiffLine( kind: AslDiffKind, text: String, modifier: Modifier = Modifier, oldNo: Int? = null, newNo: Int? = null, noWrap: Boolean = false, )
CyclomaticComplexMethod:FileIcons.kt$AslFileIcons$fun iconFor(fileName: String): String
CyclomaticComplexMethod:FileIcons.kt$AslFileIcons$fun tintFor(fileName: String, colors: AslColorScheme): Color
- CyclomaticComplexMethod:FileTree.kt$@Composable private fun AslFileTreeRow( node: AslFileTreeNode, depth: Int, expanded: Boolean, selected: Boolean, rowWidth: Dp, actionsEnabled: Boolean, canPaste: Boolean, selectDirectories: Boolean, onToggle: (String) -> Unit, onSelect: (AslFileTreeNode) -> Unit, onFocus: (AslFileTreeNode) -> Unit, onAction: (AslFileTreeNode, AslFileTreeAction) -> Unit, )
CyclomaticComplexMethod:ListItem.kt$@Composable fun AslListItem( title: String, modifier: Modifier = Modifier, subtitle: String? = null, icon: String? = null, iconColor: Color? = null, leading: (@Composable () -> Unit)? = null, trailing: (@Composable () -> Unit)? = null, divider: Boolean = true, selected: Boolean = false, disabled: Boolean = false, onClick: (() -> Unit)? = null, )
CyclomaticComplexMethod:TextField.kt$@OptIn(ExperimentalFoundationApi::class) @Composable fun AslTextField( value: String, onValueChange: (String) -> Unit, modifier: Modifier = Modifier, label: String? = null, placeholder: String? = null, helper: String? = null, error: String? = null, disabled: Boolean = false, type: AslTextFieldType = AslTextFieldType.Text, leadingIcon: String? = null, trailingIcon: String? = null, onTrailingClick: (() -> Unit)? = null, onFocusChanged: ((Boolean) -> Unit)? = null, )
CyclomaticComplexMethod:ToolCallCard.kt$private fun computeLineDiff(old: String?, new: String): List<DiffLine>
@@ -82,7 +81,6 @@
LongParameterList:Button.kt$( label: String, onClick: () -> Unit, modifier: Modifier = Modifier, variant: AslButtonVariant = AslButtonVariant.Primary, size: AslButtonSize = AslButtonSize.Md, icon: String? = null, trailingIcon: String? = null, disabled: Boolean = false, loading: Boolean = false, fullWidth: Boolean = false, )
LongParameterList:Chip.kt$( label: String, modifier: Modifier = Modifier, kind: AslChipKind = AslChipKind.Filter, selected: Boolean = false, disabled: Boolean = false, icon: String? = null, status: AslChipStatus = AslChipStatus.Neutral, onRemove: (() -> Unit)? = null, onClick: (() -> Unit)? = null, )
LongParameterList:Dialog.kt$( title: String, onDismiss: () -> Unit, modifier: Modifier = Modifier, variant: AslDialogVariant = AslDialogVariant.Alert, body: String? = null, confirmLabel: String = stringResource(R.string.asl_action_ok), cancelLabel: String? = null, destructive: Boolean = false, onConfirm: () -> Unit = {}, inputContent: (@Composable () -> Unit)? = null, )
- LongParameterList:EditorToolbar.kt$( modifier: Modifier = Modifier, projectName: String = "MyApplication", running: Boolean = false, onRun: () -> Unit = {}, onMenu: () -> Unit = {}, actions: @Composable (RowScope.() -> Unit)? = null, overflowItems: List<AslOverflowMenuEntry> = emptyList(), onOverflowSelect: (AslOverflowMenuEntry.Item, Int) -> Unit = { _, _ -> }, )
LongParameterList:EmptyState.kt$( title: String, modifier: Modifier = Modifier, icon: String = "folder-open", subtitle: String? = null, actionLabel: String? = null, onAction: () -> Unit = {}, secondaryLabel: String? = null, onSecondary: () -> Unit = {}, )
LongParameterList:ErrorState.kt$( title: String, modifier: Modifier = Modifier, icon: String = "triangle-alert", explanation: String? = null, detail: String? = null, actionLabel: String = stringResource(R.string.asl_action_retry), onAction: () -> Unit = {}, secondaryLabel: String? = null, onSecondary: () -> Unit = {}, )
LongParameterList:FileTree.kt$( items: List<AslFileTreeNode>, expandedIds: Set<String>, modifier: Modifier = Modifier, selectedId: String? = null, actionsEnabled: Boolean = false, canPaste: Boolean = false, selectDirectories: Boolean = false, onToggle: (String) -> Unit = {}, onSelect: (AslFileTreeNode) -> Unit = {}, onFocus: (AslFileTreeNode) -> Unit = {}, onAction: (AslFileTreeNode, AslFileTreeAction) -> Unit = { _, _ -> }, )
diff --git a/designsystem/src/main/java/com/ahmadkharfan/androidstudiolite/designsystem/component/content/FileTree.kt b/designsystem/src/main/java/com/ahmadkharfan/androidstudiolite/designsystem/component/content/FileTree.kt
index 2687ce93..832afaf9 100644
--- a/designsystem/src/main/java/com/ahmadkharfan/androidstudiolite/designsystem/component/content/FileTree.kt
+++ b/designsystem/src/main/java/com/ahmadkharfan/androidstudiolite/designsystem/component/content/FileTree.kt
@@ -5,9 +5,11 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@@ -191,111 +193,176 @@ private fun AslFileTreeRow(
},
),
) {
- Row(
+ FileTreeRowContent(
+ node = node,
+ expanded = expanded,
+ selected = selected,
+ selectDirectories = selectDirectories,
+ callbacks = FileTreeRowCallbacks(onToggle, onSelect, onFocus),
modifier = Modifier
.width(rowWidth)
.height(AslMetrics.treeRow)
.padding(start = (8 + depth * 16).dp, end = 8.dp),
- verticalAlignment = Alignment.CenterVertically,
- ) {
- if (isDir) {
- val rotation by animateFloatAsState(
- targetValue = if (expanded) 90f else 0f,
- animationSpec = AslMotion.standardSpec(),
- label = "chevronRotation",
- )
- Box(
- modifier = Modifier
- .size(20.dp)
- .then(
- if (selectDirectories) {
- Modifier.pointerInput(node.id) {
- detectTapGestures(onTap = { onToggle(node.id) })
- }
- } else {
- Modifier
- },
- ),
- contentAlignment = Alignment.Center,
- ) {
- AslIcon(
- name = "chevron-right",
- size = 14.dp,
- tint = colors.textTertiary,
- modifier = Modifier.rotate(rotation),
- )
- }
- } else {
- Box(modifier = Modifier.width(14.dp))
- }
- Spacer(Modifier.width(6.dp))
- Row(
- modifier = Modifier
- .weight(1f)
- .fillMaxWidth()
- .then(
- if (selectDirectories) {
- Modifier.pointerInput(node.id) {
- detectTapGestures(onTap = {
- onFocus(node)
- onSelect(node)
- })
- }
- } else {
- Modifier
- },
- ),
- verticalAlignment = Alignment.CenterVertically,
- ) {
- AslIcon(
- name = node.icon ?: if (isDir) (if (expanded) "folder-open" else "folder") else "file",
- size = 16.dp,
- tint = when {
- isDir -> colors.textSecondary
- else -> AslFileIcons.tintFor(node.name, colors)
- },
- )
- Spacer(Modifier.width(6.dp))
- Text(
- text = node.name,
- style = MaterialTheme.typography.bodySmall,
- fontWeight = if (selected) FontWeight.Medium else FontWeight.Normal,
- color = node.git?.let { gitTint(it, colors) } ?: colors.textPrimary,
- maxLines = 1,
- softWrap = false,
- )
- if (node.git != null) {
- Spacer(Modifier.width(8.dp))
- Text(
- text = node.git.letter,
- style = AslCode.codeTiny,
- color = gitTint(node.git, colors),
- )
- }
+ )
+ if (actionsEnabled && menuOpen) {
+ FileTreeRowMenu(menuAnchor, isDir, canPaste, openMenuUpward, onDismiss = { menuOpen = false }) { action ->
+ menuOpen = false
+ onAction(node, action)
}
}
- if (actionsEnabled && menuOpen) {
- Box(
- modifier = Modifier
- .align(Alignment.TopStart)
- .offset(x = menuAnchor.x, y = menuAnchor.y)
- .size(1.dp),
- ) {
- AslFileTreeActionMenu(
- isDirectory = isDir,
- canPaste = canPaste,
- openUpward = openMenuUpward,
- onDismiss = { menuOpen = false },
- onSelect = { action ->
- menuOpen = false
- onAction(node, action)
+ }
+}
+
+@Immutable
+private data class FileTreeRowCallbacks(
+ val onToggle: (String) -> Unit,
+ val onSelect: (AslFileTreeNode) -> Unit,
+ val onFocus: (AslFileTreeNode) -> Unit,
+)
+
+@Composable
+private fun FileTreeRowContent(
+ node: AslFileTreeNode,
+ expanded: Boolean,
+ selected: Boolean,
+ selectDirectories: Boolean,
+ callbacks: FileTreeRowCallbacks,
+ modifier: Modifier = Modifier,
+) {
+ val isDirectory = node.children != null
+ Row(
+ modifier = modifier,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ FileTreeRowChevron(node, isDirectory, expanded, selectDirectories, callbacks.onToggle)
+ Spacer(Modifier.width(6.dp))
+ FileTreeRowLabel(node, isDirectory, expanded, selected, selectDirectories, callbacks.onSelect, callbacks.onFocus)
+ }
+}
+
+@Composable
+private fun FileTreeRowChevron(
+ node: AslFileTreeNode,
+ isDirectory: Boolean,
+ expanded: Boolean,
+ selectDirectories: Boolean,
+ onToggle: (String) -> Unit,
+) {
+ if (isDirectory) {
+ val colors = AslTheme.colors
+ val rotation by animateFloatAsState(
+ targetValue = if (expanded) 90f else 0f,
+ animationSpec = AslMotion.standardSpec(),
+ label = "chevronRotation",
+ )
+ Box(
+ modifier = Modifier
+ .size(20.dp)
+ .then(
+ if (selectDirectories) {
+ Modifier.pointerInput(node.id) {
+ detectTapGestures(onTap = { onToggle(node.id) })
+ }
+ } else {
+ Modifier
},
- )
- }
+ ),
+ contentAlignment = Alignment.Center,
+ ) {
+ AslIcon(
+ name = "chevron-right",
+ size = 14.dp,
+ tint = colors.textTertiary,
+ modifier = Modifier.rotate(rotation),
+ )
+ }
+ } else {
+ Box(modifier = Modifier.width(14.dp))
+ }
+}
+
+@Composable
+private fun RowScope.FileTreeRowLabel(
+ node: AslFileTreeNode,
+ isDirectory: Boolean,
+ expanded: Boolean,
+ selected: Boolean,
+ selectDirectories: Boolean,
+ onSelect: (AslFileTreeNode) -> Unit,
+ onFocus: (AslFileTreeNode) -> Unit,
+) {
+ val colors = AslTheme.colors
+ Row(
+ modifier = Modifier
+ .weight(1f)
+ .fillMaxWidth()
+ .then(
+ if (selectDirectories) {
+ Modifier.pointerInput(node.id) {
+ detectTapGestures(onTap = {
+ onFocus(node)
+ onSelect(node)
+ })
+ }
+ } else {
+ Modifier
+ },
+ ),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ AslIcon(
+ name = node.icon ?: if (isDirectory) (if (expanded) "folder-open" else "folder") else "file",
+ size = 16.dp,
+ tint = when {
+ isDirectory -> colors.textSecondary
+ else -> AslFileIcons.tintFor(node.name, colors)
+ },
+ )
+ Spacer(Modifier.width(6.dp))
+ Text(
+ text = node.name,
+ style = MaterialTheme.typography.bodySmall,
+ fontWeight = if (selected) FontWeight.Medium else FontWeight.Normal,
+ color = node.git?.let { gitTint(it, colors) } ?: colors.textPrimary,
+ maxLines = 1,
+ softWrap = false,
+ )
+ if (node.git != null) {
+ Spacer(Modifier.width(8.dp))
+ Text(
+ text = node.git.letter,
+ style = AslCode.codeTiny,
+ color = gitTint(node.git, colors),
+ )
}
}
}
+@Composable
+private fun BoxScope.FileTreeRowMenu(
+ menuAnchor: DpOffset,
+ isDirectory: Boolean,
+ canPaste: Boolean,
+ openUpward: Boolean,
+ onDismiss: () -> Unit,
+ onSelect: (AslFileTreeAction) -> Unit,
+) {
+ Box(
+ modifier = Modifier
+ .align(Alignment.TopStart)
+ .offset(x = menuAnchor.x, y = menuAnchor.y)
+ .size(1.dp),
+ ) {
+ AslFileTreeActionMenu(
+ isDirectory = isDirectory,
+ canPaste = canPaste,
+ openUpward = openUpward,
+ onDismiss = onDismiss,
+ onSelect = onSelect,
+ )
+ }
+}
+
@Composable
private fun AslFileTreeActionMenu(
isDirectory: Boolean,
diff --git a/feature/git/detekt-baseline.xml b/feature/git/detekt-baseline.xml
index 3043930a..69c0e8e1 100644
--- a/feature/git/detekt-baseline.xml
+++ b/feature/git/detekt-baseline.xml
@@ -6,7 +6,6 @@
CyclomaticComplexMethod:GitAuthPrompt.kt$@Composable fun GitHubAuthDialog(state: GitAuthPromptState, actions: GitAuthPromptActions)
CyclomaticComplexMethod:GitErrorMessageMapper.kt$fun gitErrorMessage(error: Throwable): String
CyclomaticComplexMethod:GitHistoryScreen.kt$@Composable private fun GitHistoryScreen( uiState: GitHistoryUiState, interactionListener: GitHistoryInteractionListener, onBack: () -> Unit, onOpenDiff: (String, String) -> Unit, )
- CyclomaticComplexMethod:GitPanelScreen.kt$@Composable private fun GitChangesHeader( uiState: GitPanelUiState, interactionListener: GitPanelInteractionListener, )
CyclomaticComplexMethod:GitPanelScreen.kt$@Composable private fun GitPanelScreen( uiState: GitPanelUiState, interactionListener: GitPanelInteractionListener, onClose: () -> Unit, onOpenDiff: (String, GitDiffTarget) -> Unit, onOpenHistory: () -> Unit, onOpenBranches: () -> Unit, onOpenTags: () -> Unit, onOpenStashes: () -> Unit, onOpenConflicts: () -> Unit, )
ForbiddenImport:GitDiffScreen.kt$import androidx.compose.material3.MaterialTheme
ForbiddenImport:GitHistoryScreen.kt$import androidx.compose.material3.MaterialTheme
@@ -14,9 +13,7 @@
LongMethod:GitAuthPrompt.kt$@Composable fun GitHubAuthDialog(state: GitAuthPromptState, actions: GitAuthPromptActions)
LongMethod:GitDiffScreen.kt$@Composable private fun GitDiffScreen( uiState: GitDiffUiState, interactionListener: GitDiffInteractionListener, onBack: () -> Unit, )
LongMethod:GitHistoryScreen.kt$@Composable private fun GitHistoryScreen( uiState: GitHistoryUiState, interactionListener: GitHistoryInteractionListener, onBack: () -> Unit, onOpenDiff: (String, String) -> Unit, )
- LongMethod:GitPanelScreen.kt$@Composable private fun GitChangesHeader( uiState: GitPanelUiState, interactionListener: GitPanelInteractionListener, )
LongMethod:GitPanelScreen.kt$@Composable private fun GitPanelScreen( uiState: GitPanelUiState, interactionListener: GitPanelInteractionListener, onClose: () -> Unit, onOpenDiff: (String, GitDiffTarget) -> Unit, onOpenHistory: () -> Unit, onOpenBranches: () -> Unit, onOpenTags: () -> Unit, onOpenStashes: () -> Unit, onOpenConflicts: () -> Unit, )
- LongMethod:GitPanelScreen.kt$@Composable private fun RemotesView(uiState: GitPanelUiState, interactionListener: GitPanelInteractionListener)
LongMethod:GitRefsScreen.kt$@Composable private fun BranchList( state: GitRefsUiState, interactionListener: GitRefsInteractionListener, onRename: (GitBranch) -> Unit, onDelete: (GitBranch) -> Unit, onMerge: (GitBranch) -> Unit, )
LongMethod:GitRefsScreen.kt$@Composable private fun GitRefsScreen( uiState: GitRefsUiState, interactionListener: GitRefsInteractionListener, onBack: () -> Unit, )
LongParameterList:GitPanelApi.kt$GitPanelApi$( projectId: String, onClose: () -> Unit, onOpenDiff: (String, GitDiffTarget) -> Unit = { _, _ -> }, onOpenHistory: () -> Unit = {}, onOpenBranches: () -> Unit = {}, onOpenTags: () -> Unit = {}, onOpenStashes: () -> Unit = {}, onOpenConflicts: () -> Unit = {}, )
@@ -24,7 +21,6 @@
LongParameterList:GitPanelScreen.kt$( uiState: GitPanelUiState, interactionListener: GitPanelInteractionListener, onClose: () -> Unit, onOpenDiff: (String, GitDiffTarget) -> Unit, onOpenHistory: () -> Unit, onOpenBranches: () -> Unit, onOpenTags: () -> Unit, onOpenStashes: () -> Unit, onOpenConflicts: () -> Unit, )
ModifierMissing:GitAuthPrompt.kt$GitHubAuthDialog
ModifierMissing:GitBlameScreen.kt$GitBlameRoute
- MultipleEmitters:GitPanelScreen.kt$GitChangesHeader
TooGenericExceptionCaught:GitPanelControllerContext.kt$GitPanelControllerContext$error: Throwable
TooGenericExceptionCaught:GitPanelViewModel.kt$GitPanelViewModel$pushError: Throwable
diff --git a/feature/git/src/main/java/com/ahmadkharfan/androidstudiolite/feature/editor/git/GitPanelScreen.kt b/feature/git/src/main/java/com/ahmadkharfan/androidstudiolite/feature/editor/git/GitPanelScreen.kt
index 42c7a526..0955f125 100644
--- a/feature/git/src/main/java/com/ahmadkharfan/androidstudiolite/feature/editor/git/GitPanelScreen.kt
+++ b/feature/git/src/main/java/com/ahmadkharfan/androidstudiolite/feature/editor/git/GitPanelScreen.kt
@@ -22,6 +22,7 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
@@ -288,113 +289,130 @@ private fun GitChangesHeader(
}
val statusText = uiState.operationLabel ?: uiState.statusMessage
if (statusText != null || uiState.isBusy) {
- Column(modifier = Modifier.fillMaxWidth().padding(start = 12.dp, end = 8.dp, top = 6.dp)) {
- if (statusText != null) {
- Row(verticalAlignment = Alignment.CenterVertically) {
- Text(
- text = statusText,
- style = AslTypography.labelSmall,
- color = colors.textSecondary,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- modifier = Modifier.weight(1f),
+ GitChangesStatus(
+ uiState = uiState,
+ statusText = statusText,
+ interactionListener = interactionListener,
+ textColor = colors.textSecondary,
+ )
+ }
+ if (uiState.hasChipRow) {
+ GitChangesChipRow(uiState, interactionListener)
+ }
+ HorizontalDivider(color = colors.borderSubtle, thickness = 1.dp)
+}
+
+@Composable
+private fun GitChangesStatus(
+ uiState: GitPanelUiState,
+ statusText: String?,
+ interactionListener: GitPanelInteractionListener,
+ textColor: Color,
+) {
+ Column(modifier = Modifier.fillMaxWidth().padding(start = 12.dp, end = 8.dp, top = 6.dp)) {
+ if (statusText != null) {
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Text(
+ text = statusText,
+ style = AslTypography.labelSmall,
+ color = textColor,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ modifier = Modifier.weight(1f),
+ )
+ if (uiState.operationCancellable) {
+ AslIconButton(
+ icon = "x",
+ contentDescription = stringResource(R.string.git_panel_cancel_operation),
+ onClick = interactionListener::onCancelOperation,
+ size = 32.dp,
+ iconSize = 16.dp,
)
- if (uiState.operationCancellable) {
- AslIconButton(
- icon = "x",
- contentDescription = stringResource(R.string.git_panel_cancel_operation),
- onClick = interactionListener::onCancelOperation,
- size = 32.dp,
- iconSize = 16.dp,
- )
- }
}
}
- if (uiState.isBusy) {
- AslLinearProgress(value = uiState.operationProgress, modifier = Modifier.padding(top = 4.dp, end = 4.dp))
- }
+ }
+ if (uiState.isBusy) {
+ AslLinearProgress(value = uiState.operationProgress, modifier = Modifier.padding(top = 4.dp, end = 4.dp))
}
}
- if (uiState.hasChipRow) {
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .horizontalScroll(rememberScrollState())
- .padding(horizontal = 12.dp, vertical = 8.dp),
- horizontalArrangement = Arrangement.spacedBy(6.dp),
- verticalAlignment = Alignment.CenterVertically,
- ) {
- if (uiState.hasSelection) {
+}
+
+@Composable
+private fun GitChangesChipRow(
+ uiState: GitPanelUiState,
+ interactionListener: GitPanelInteractionListener,
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .horizontalScroll(rememberScrollState())
+ .padding(horizontal = 12.dp, vertical = 8.dp),
+ horizontalArrangement = Arrangement.spacedBy(6.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ if (uiState.hasSelection) {
+ AslChip(label = pluralStringResource(R.plurals.git_panel_selected, uiState.selectionCount, uiState.selectionCount), kind = AslChipKind.Filter, selected = true)
+ if (uiState.canStageSelection) {
+ GitSelectionActionChip(stringResource(R.string.git_panel_stage), "plus", uiState.isBusy, interactionListener::onStageSelected)
+ }
+ if (uiState.canUnstageSelection) {
+ GitSelectionActionChip(stringResource(R.string.git_panel_unstage), "minus", uiState.isBusy, interactionListener::onUnstageSelected)
+ }
+ if (uiState.canRevertSelection) {
+ GitSelectionActionChip(stringResource(R.string.git_panel_revert), "rotate-ccw", uiState.isBusy, interactionListener::onRevertSelected)
+ }
+ AslChip(
+ label = stringResource(R.string.git_panel_clear),
+ icon = "x",
+ kind = AslChipKind.Assist,
+ disabled = uiState.isBusy,
+ onClick = interactionListener::onClearSelection,
+ )
+ } else {
+ if (uiState.changeCount > 0) {
AslChip(
- label = pluralStringResource(R.plurals.git_panel_selected, uiState.selectionCount, uiState.selectionCount),
- kind = AslChipKind.Filter,
- selected = true,
+ label = pluralStringResource(
+ R.plurals.git_panel_changes,
+ uiState.changeCount,
+ uiState.changeCount,
+ ),
+ kind = AslChipKind.Status,
+ status = AslChipStatus.Neutral,
)
- if (uiState.canStageSelection) {
- AslChip(
- label = stringResource(R.string.git_panel_stage),
- icon = "plus",
- kind = AslChipKind.Filter,
- disabled = uiState.isBusy,
- onClick = interactionListener::onStageSelected,
- )
- }
- if (uiState.canUnstageSelection) {
- AslChip(
- label = stringResource(R.string.git_panel_unstage),
- icon = "minus",
- kind = AslChipKind.Filter,
- disabled = uiState.isBusy,
- onClick = interactionListener::onUnstageSelected,
- )
- }
- if (uiState.canRevertSelection) {
- AslChip(
- label = stringResource(R.string.git_panel_revert),
- icon = "rotate-ccw",
- kind = AslChipKind.Filter,
- disabled = uiState.isBusy,
- onClick = interactionListener::onRevertSelected,
- )
- }
+ }
+ uiState.behind?.takeIf { it > 0 }?.let {
+ AslChip(label = behindLabel(it), kind = AslChipKind.Status, status = AslChipStatus.Info)
+ }
+ uiState.ahead?.takeIf { it > 0 }?.let {
+ AslChip(label = aheadLabel(it), kind = AslChipKind.Status, status = AslChipStatus.Success)
+ }
+ if (uiState.changeCount > 0) {
AslChip(
- label = stringResource(R.string.git_panel_clear),
- icon = "x",
- kind = AslChipKind.Assist,
+ label = stringResource(R.string.git_panel_select),
+ icon = "circle-check",
+ kind = AslChipKind.Filter,
disabled = uiState.isBusy,
- onClick = interactionListener::onClearSelection,
+ onClick = interactionListener::onSelectAllChanges,
)
- } else {
- if (uiState.changeCount > 0) {
- AslChip(
- label = pluralStringResource(
- R.plurals.git_panel_changes,
- uiState.changeCount,
- uiState.changeCount,
- ),
- kind = AslChipKind.Status,
- status = AslChipStatus.Neutral,
- )
- }
- uiState.behind?.takeIf { it > 0 }?.let {
- AslChip(label = behindLabel(it), kind = AslChipKind.Status, status = AslChipStatus.Info)
- }
- uiState.ahead?.takeIf { it > 0 }?.let {
- AslChip(label = aheadLabel(it), kind = AslChipKind.Status, status = AslChipStatus.Success)
- }
- if (uiState.changeCount > 0) {
- AslChip(
- label = stringResource(R.string.git_panel_select),
- icon = "circle-check",
- kind = AslChipKind.Filter,
- disabled = uiState.isBusy,
- onClick = interactionListener::onSelectAllChanges,
- )
- }
}
}
}
- HorizontalDivider(color = colors.borderSubtle, thickness = 1.dp)
+}
+
+@Composable
+private fun GitSelectionActionChip(
+ label: String,
+ icon: String,
+ disabled: Boolean,
+ onClick: () -> Unit,
+) {
+ AslChip(
+ label = label,
+ icon = icon,
+ kind = AslChipKind.Filter,
+ disabled = disabled,
+ onClick = onClick,
+ )
}
@Composable
@@ -846,45 +864,50 @@ private fun RemotesView(uiState: GitPanelUiState, interactionListener: GitPanelI
)
}
HorizontalDivider(color = AslTheme.colors.borderSubtle)
- when {
- uiState.remotesLoading -> AslLinearProgress(
- label = stringResource(R.string.git_remotes_loading),
- modifier = Modifier.padding(16.dp),
- )
- uiState.remotes.isEmpty() -> AslEmptyState(
- icon = "globe",
- title = stringResource(R.string.git_remotes_none),
- subtitle = stringResource(R.string.git_remotes_none_hint),
- modifier = Modifier.fillMaxSize(),
- )
- else -> Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
- uiState.remotes.forEach { remote ->
- AslListItem(
- title = remote.name,
- subtitle = remote.url.middleEllipsis(),
- icon = "globe",
- trailing = {
- Row {
- AslIconButton(
- icon = "edit-2",
- contentDescription = stringResource(R.string.git_remotes_edit_description, remote.name),
- onClick = { interactionListener.onEditRemote(remote.name) },
- size = 28.dp,
- iconSize = 14.dp,
- disabled = uiState.isBusy,
- )
- AslIconButton(
- icon = "trash-2",
- contentDescription = stringResource(R.string.git_remotes_remove_description, remote.name),
- onClick = { interactionListener.onRequestRemoveRemote(remote.name) },
- size = 28.dp,
- iconSize = 14.dp,
- disabled = uiState.isBusy,
- )
- }
- },
- )
- }
+ RemotesContent(uiState, interactionListener)
+ }
+}
+
+@Composable
+private fun RemotesContent(uiState: GitPanelUiState, interactionListener: GitPanelInteractionListener) {
+ when {
+ uiState.remotesLoading -> AslLinearProgress(
+ label = stringResource(R.string.git_remotes_loading),
+ modifier = Modifier.padding(16.dp),
+ )
+ uiState.remotes.isEmpty() -> AslEmptyState(
+ icon = "globe",
+ title = stringResource(R.string.git_remotes_none),
+ subtitle = stringResource(R.string.git_remotes_none_hint),
+ modifier = Modifier.fillMaxSize(),
+ )
+ else -> Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
+ uiState.remotes.forEach { remote ->
+ AslListItem(
+ title = remote.name,
+ subtitle = remote.url.middleEllipsis(),
+ icon = "globe",
+ trailing = {
+ Row {
+ AslIconButton(
+ icon = "edit-2",
+ contentDescription = stringResource(R.string.git_remotes_edit_description, remote.name),
+ onClick = { interactionListener.onEditRemote(remote.name) },
+ size = 28.dp,
+ iconSize = 14.dp,
+ disabled = uiState.isBusy,
+ )
+ AslIconButton(
+ icon = "trash-2",
+ contentDescription = stringResource(R.string.git_remotes_remove_description, remote.name),
+ onClick = { interactionListener.onRequestRemoveRemote(remote.name) },
+ size = 28.dp,
+ iconSize = 14.dp,
+ disabled = uiState.isBusy,
+ )
+ }
+ },
+ )
}
}
}