From 7718e3ab19c2e405703eacef5ee9967f6f723d78 Mon Sep 17 00:00:00 2001 From: davotoula Date: Tue, 28 Jul 2026 20:23:12 +0200 Subject: [PATCH] fix: extract duplicated string literals --- .../mediaServers/BlossomBlobManagerScreen.kt | 13 ++++++++----- .../amethyst/cli/commands/BuzzAgentCommands.kt | 11 +++++++---- .../amethyst/cli/commands/BuzzCommands.kt | 9 ++++++--- .../amethyst/cli/commands/BuzzWorkflowCommands.kt | 10 +++++++--- .../amethyst/cli/commands/GitBrowseCommands.kt | 9 ++++++--- 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/BlossomBlobManagerScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/BlossomBlobManagerScreen.kt index d209e5888b..799345786b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/BlossomBlobManagerScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/BlossomBlobManagerScreen.kt @@ -115,6 +115,9 @@ import kotlinx.coroutines.launch import net.engawapg.lib.zoomable.rememberZoomState import net.engawapg.lib.zoomable.zoomable +private const val MIME_IMAGE_PREFIX = "image/" +private const val MIME_VIDEO_PREFIX = "video/" + @Composable fun BlossomBlobManagerScreen( accountViewModel: AccountViewModel, @@ -291,7 +294,7 @@ private fun OverflowMenuIcon(symbol: MaterialSymbol) { /** Whether a blob is an image or a video, i.e. it can be previewed and shown full-screen. */ private val BlobRow.isViewable: Boolean - get() = type?.let { it.startsWith("image/") || it.startsWith("video/") } == true + get() = type?.let { it.startsWith(MIME_IMAGE_PREFIX) || it.startsWith(MIME_VIDEO_PREFIX) } == true @Composable private fun CenteredState(content: @Composable () -> Unit) { @@ -381,7 +384,7 @@ private fun BlobPreview( glyphSize: Dp = 34.dp, playIconSize: Dp = 40.dp, ) { - val isVideo = row.type?.startsWith("video/") == true + val isVideo = row.type?.startsWith(MIME_VIDEO_PREFIX) == true Box(modifier = modifier, contentAlignment = Alignment.Center) { if (row.url != null && row.isViewable) { SubcomposeAsyncImage( @@ -483,7 +486,7 @@ private fun BlossomBlobViewer( ) { val context = LocalContext.current var drawerOpen by remember { mutableStateOf(false) } - val isVideo = row.type?.startsWith("video/") == true + val isVideo = row.type?.startsWith(MIME_VIDEO_PREFIX) == true Dialog( onDismissRequest = onDismiss, @@ -739,8 +742,8 @@ private fun DetailAction( private fun glyphFor(type: String?): MaterialSymbol = when { - type?.startsWith("image/") == true -> MaterialSymbols.Image - type?.startsWith("video/") == true -> MaterialSymbols.PlayCircle + type?.startsWith(MIME_IMAGE_PREFIX) == true -> MaterialSymbols.Image + type?.startsWith(MIME_VIDEO_PREFIX) == true -> MaterialSymbols.PlayCircle else -> MaterialSymbols.Storage } diff --git a/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/BuzzAgentCommands.kt b/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/BuzzAgentCommands.kt index 590a5ee385..e3f97c2c67 100644 --- a/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/BuzzAgentCommands.kt +++ b/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/BuzzAgentCommands.kt @@ -146,12 +146,12 @@ object BuzzAgentCommands { val repo = args.flag("repo") ?: return Output.error("bad_args", "pass --repo DIR (your git checkout — the agent works and opens PRs here)") if (!File(repo).resolve(".git").exists()) return Output.error("bad_args", "--repo is not a git repository: $repo") val approver = args.flag("approver") ?: return Output.error("bad_args", "pass --approver NPUB (the human who signs off each run)") - val baseRef = args.flag("base-ref") + val baseRef = args.flag(FLAG_BASE_REF) val poll = args.flag("poll") val timeout = args.flag("timeout") val once = args.bool("once") val explicitChannel = args.flag("channel") - args.rejectUnknown("repo", "approver", "channel", "base-ref", "poll", "timeout", "once") + args.rejectUnknown("repo", "approver", "channel", FLAG_BASE_REF, "poll", "timeout", "once") val channel = explicitChannel @@ -320,7 +320,7 @@ object BuzzAgentCommands { val timeoutSecs = args.flag("timeout")?.toLongOrNull() ?: 8 val execTimeoutSecs = args.flag("exec-timeout")?.toLongOrNull() ?: 1800 val worktreeBase = args.flag("worktree") - val baseRef = args.flag("base-ref") ?: "HEAD" + val baseRef = args.flag(FLAG_BASE_REF) ?: "HEAD" val branchPrefix = args.flag("branch-prefix") ?: "claude/job-" val fromChannel = args.bool("accept-from-channel") val acceptFrom = @@ -340,7 +340,7 @@ object BuzzAgentCommands { "claim-untargeted", "parallel", "worktree", - "base-ref", + FLAG_BASE_REF, "branch-prefix", "poll", "exec-timeout", @@ -714,4 +714,7 @@ object BuzzAgentCommands { } private const val MAX_BODY = 60_000 + + /** Flag name for the git ref each job's worktree branches off. */ + private const val FLAG_BASE_REF = "base-ref" } diff --git a/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/BuzzCommands.kt b/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/BuzzCommands.kt index 8d1f37a498..ab66424c51 100644 --- a/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/BuzzCommands.kt +++ b/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/BuzzCommands.kt @@ -147,7 +147,7 @@ object BuzzCommands { ctx.prepare() val me = ctx.identity.pubKeyHex val relays = relaysFor(ctx, relaysFlag) - if (relays.isEmpty()) return Output.error("no_relays", "no relays: pass --relays ws://…") + if (relays.isEmpty()) return Output.error("no_relays", NO_RELAYS_MSG) // The deployed Buzz relay does NOT emit kind-41001; instead it (a) confirms a DM's // channel id synchronously in the open OK, and (b) addresses each member a kind-44100 @@ -518,7 +518,7 @@ object BuzzCommands { ctx.prepare() val me = ctx.identity.pubKeyHex val relays = relaysFor(ctx, relaysFlag) - if (relays.isEmpty()) return Output.error("no_relays", "no relays: pass --relays ws://…") + if (relays.isEmpty()) return Output.error("no_relays", NO_RELAYS_MSG) val filter = Filter(kinds = listOf(AgentTurnMetricEvent.KIND), tags = mapOf("p" to listOf(me))) val decrypted = @@ -572,7 +572,7 @@ object BuzzCommands { ctx.prepare() val me = ctx.identity.pubKeyHex val relays = relaysFor(ctx, relaysFlag) - if (relays.isEmpty()) return Output.error("no_relays", "no relays: pass --relays ws://…") + if (relays.isEmpty()) return Output.error("no_relays", NO_RELAYS_MSG) val filter = Filter(kinds = listOf(PersonaEvent.KIND), authors = listOf(me)) val personas = @@ -600,6 +600,9 @@ object BuzzCommands { } } + /** Message for the `no_relays` error: neither `--relays` nor the account's outbox had one. */ + private const val NO_RELAYS_MSG = "no relays: pass --relays ws://…" + /** The `--relays` set if given, else the account's outbox relays. */ private suspend fun relaysFor( ctx: Context, diff --git a/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/BuzzWorkflowCommands.kt b/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/BuzzWorkflowCommands.kt index 5196584d1b..2b82f48ed1 100644 --- a/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/BuzzWorkflowCommands.kt +++ b/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/BuzzWorkflowCommands.kt @@ -125,7 +125,7 @@ object BuzzWorkflowCommands { val wfId = args.positionalOrNull(1) ?: return Output.error("bad_args", usage) val relay = normalizeGroupRelay(relayUrl) ?: return Output.error("bad_args", "invalid relay url: $relayUrl") val task = args.flag("task")?.takeIf { it.isNotBlank() } ?: return Output.error("bad_args", "pass --task TEXT") - val channel = args.flag("channel") ?: return Output.error("bad_args", "pass --channel GID") + val channel = args.flag("channel") ?: return Output.error("bad_args", MISSING_CHANNEL_MSG) args.rejectUnknown("task", "channel") Context.open(dataDir).use { ctx -> @@ -181,7 +181,7 @@ object BuzzWorkflowCommands { val usage = "buzz workflow list RELAY --channel GID [--timeout SECS]" val relayUrl = args.positionalOrNull(0) ?: return Output.error("bad_args", usage) val relay = normalizeGroupRelay(relayUrl) ?: return Output.error("bad_args", "invalid relay url: $relayUrl") - val channel = args.flag("channel") ?: return Output.error("bad_args", "pass --channel GID") + val channel = args.flag("channel") ?: return Output.error("bad_args", MISSING_CHANNEL_MSG) val timeoutSecs = args.flag("timeout")?.toLongOrNull() ?: 8 args.rejectUnknown("channel", "timeout") @@ -301,7 +301,7 @@ object BuzzWorkflowCommands { val relayUrl = args.positionalOrNull(0) ?: return Output.error("bad_args", USAGE) val relay = normalizeGroupRelay(relayUrl) ?: return Output.error("bad_args", "invalid relay url: $relayUrl") val exec = args.flag("exec") ?: return Output.error("bad_args", "pass --exec CMD (the agent's work step)") - val channel = args.flag("channel") ?: return Output.error("bad_args", "pass --channel GID") + val channel = args.flag("channel") ?: return Output.error("bad_args", MISSING_CHANNEL_MSG) val approverInput = args.flag("approver") ?: return Output.error("bad_args", "pass --approver NPUB (who signs off the gate)") val approver = decodePublicKeyAsHexOrNull(approverInput.trim())?.takeIf { it.isValid() } @@ -597,6 +597,10 @@ object BuzzWorkflowCommands { .orEmpty() private const val MAX = 60_000 + + /** Message for the `bad_args` error raised when `--channel` is missing. */ + private const val MISSING_CHANNEL_MSG = "pass --channel GID" + private val LIFECYCLE_KINDS = listOf( WorkflowTriggeredEvent.KIND, diff --git a/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/GitBrowseCommands.kt b/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/GitBrowseCommands.kt index ef2a62f4d9..e9778ed30b 100644 --- a/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/GitBrowseCommands.kt +++ b/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/GitBrowseCommands.kt @@ -38,13 +38,16 @@ import java.io.File * pushing objects back is still out of scope (see cli/ROADMAP.md). */ object GitBrowseCommands { + /** Name of the first positional argument: the repo's naddr or `kind:pubkey:d` coordinates. */ + private const val ARG_REPO_COORD = "repo-naddr-or-coordinates" + /** `git browse REPO [PATH]` — list the tree entries at PATH (default: repo root). */ suspend fun browse( dataDir: DataDir, rest: Array, ): Int { val args = Args(rest) - val coord = args.positional(0, "repo-naddr-or-coordinates") + val coord = args.positional(0, ARG_REPO_COORD) val path = args.positionalOrNull(1).orEmpty() val ref = args.flag("ref") val cloneOverride = args.flag("clone") @@ -79,7 +82,7 @@ object GitBrowseCommands { rest: Array, ): Int { val args = Args(rest) - val coord = args.positional(0, "repo-naddr-or-coordinates") + val coord = args.positional(0, ARG_REPO_COORD) val path = args.positional(1, "file-path") val ref = args.flag("ref") val cloneOverride = args.flag("clone") @@ -118,7 +121,7 @@ object GitBrowseCommands { rest: Array, ): Int { val args = Args(rest) - val coord = args.positional(0, "repo-naddr-or-coordinates") + val coord = args.positional(0, ARG_REPO_COORD) val ref = args.flag("ref") val cloneOverride = args.flag("clone") val depth = args.intFlag("depth", 50)