refactor(#78): extract UI strings for remaining batch 2.3 screens

This commit is contained in:
Hy4ri committed 2026-06-21 11:55:18 +03:00
1 parent b1950ece34
commit 44e2006434
9 files changed
+207 -65

No files matched your search

@@ -23,10 +23,12 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.m57.hermescontrol.R
import com.m57.hermescontrol.ui.common.EmptyState
import com.m57.hermescontrol.ui.common.ErrorState
import com.m57.hermescontrol.ui.common.HermesScaffold
@@ -45,7 +47,7 @@ fun AchievementsScreen(
}
HermesScaffold(
title = { Text("Agent Achievements") },
title = { Text(stringResource(R.string.screen_achievements)) },
onOpenDrawer = onOpenDrawer,
isRefreshing = state.isLoading,
onRefresh = { viewModel.loadAchievements() },
@@ -65,8 +67,8 @@ fun AchievementsScreen(
state.achievements.isEmpty() -> {
EmptyState(
title = "No achievements yet",
subtitle = "Complete tasks to unlock achievements.",
title = stringResource(R.string.achievements_empty_title),
subtitle = stringResource(R.string.achievements_empty_desc),
icon = Icons.Filled.Refresh,
modifier = Modifier.padding(paddingValues),
)
@@ -156,8 +158,11 @@ fun AchievementsScreen(
) {
Text(
text =
"Progress: ${achievement.progress?.toInt() ?: 0} / " +
"${achievement.next_threshold?.toInt() ?: 0}",
stringResource(
R.string.achievements_label_progress,
achievement.progress?.toInt() ?: 0,
achievement.next_threshold?.toInt() ?: 0,
),
style = MaterialTheme.typography.bodySmall,
)
Text(
@@ -29,9 +29,11 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.m57.hermescontrol.R
import com.m57.hermescontrol.data.model.MessagingPlatformUpdate
import com.m57.hermescontrol.ui.common.EmptyState
import com.m57.hermescontrol.ui.common.ErrorState
@@ -59,7 +61,7 @@ fun ChannelsScreen(
}
HermesScaffold(
title = { Text("Messaging Channels") },
title = { Text(stringResource(R.string.screen_channels)) },
onOpenDrawer = onOpenDrawer,
isRefreshing = state.isLoading,
onRefresh = { viewModel.loadPlatforms() },
@@ -79,8 +81,8 @@ fun ChannelsScreen(
state.platforms.isEmpty() -> {
EmptyState(
title = "No channels configured",
subtitle = "Add messaging platforms in Hermes to see them here.",
title = stringResource(R.string.channels_empty_title),
subtitle = stringResource(R.string.channels_empty_desc),
modifier = Modifier.padding(paddingValues),
)
}
@@ -104,7 +106,14 @@ fun ChannelsScreen(
Column {
Text(text = platform.name, style = MaterialTheme.typography.titleLarge)
Text(
text = if (platform.enabled) "Active" else "Disabled",
text =
if (platform.enabled) {
stringResource(
R.string.channels_status_active,
)
} else {
stringResource(R.string.channels_status_disabled)
},
color =
if (platform.enabled) {
MaterialTheme.colorScheme.primary
@@ -126,7 +135,15 @@ fun ChannelsScreen(
)
Spacer(modifier = Modifier.width(8.dp))
Button(onClick = { showConfigureForm = !showConfigureForm }) {
Text(if (showConfigureForm) "Cancel" else "Configure")
Text(
if (showConfigureForm) {
stringResource(
R.string.action_cancel,
)
} else {
stringResource(R.string.channels_action_configure)
},
)
}
}
}
@@ -134,7 +151,7 @@ fun ChannelsScreen(
if (showConfigureForm) {
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Configuration Settings",
text = stringResource(R.string.channels_sec_settings),
style = MaterialTheme.typography.titleMedium,
)
@@ -148,7 +165,7 @@ fun ChannelsScreen(
label = { Text(field.prompt ?: field.key) },
placeholder = {
if (field.isSet) {
Text("******** (Already Configured)")
Text(stringResource(R.string.channels_placeholder_configured))
}
},
modifier = Modifier.fillMaxWidth(),
@@ -169,13 +186,16 @@ fun ChannelsScreen(
},
modifier = Modifier.align(Alignment.End),
) {
Text("Save Settings")
Text(stringResource(R.string.channels_action_save_settings))
}
} else {
val envFields = platform.envVars.orEmpty()
if (envFields.isNotEmpty()) {
Spacer(modifier = Modifier.height(8.dp))
Text(text = "Configured Keys:", style = MaterialTheme.typography.titleSmall)
Text(
text = stringResource(R.string.channels_sec_keys),
style = MaterialTheme.typography.titleSmall,
)
envFields.forEach { field ->
Row(
modifier = Modifier.fillMaxWidth(),
@@ -187,7 +207,14 @@ fun ChannelsScreen(
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Text(
text = if (field.isSet) "Configured" else "Not set",
text =
if (field.isSet) {
stringResource(
R.string.channels_status_configured,
)
} else {
stringResource(R.string.channels_status_not_set)
},
style = MaterialTheme.typography.bodyMedium,
color =
if (field.isSet) {
@@ -24,9 +24,11 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.m57.hermescontrol.R
import com.m57.hermescontrol.theme.LocalSpacing
import com.m57.hermescontrol.ui.common.EmptyState
import com.m57.hermescontrol.ui.common.ErrorState
@@ -60,7 +62,7 @@ fun CronJobsScreen(
}
HermesScaffold(
title = { Text("Cron Jobs") },
title = { Text(stringResource(R.string.screen_cron)) },
onOpenDrawer = onOpenDrawer,
isRefreshing = state.isLoading,
onRefresh = { viewModel.loadCronJobs() },
@@ -79,8 +81,8 @@ fun CronJobsScreen(
state.jobs.isEmpty() -> {
EmptyState(
title = "No cron jobs",
subtitle = "Scheduled tasks from Hermes will appear here.",
title = stringResource(R.string.cron_empty_title),
subtitle = stringResource(R.string.cron_empty_desc),
icon = Icons.Filled.Schedule,
)
}
@@ -112,7 +114,14 @@ fun CronJobsScreen(
modifier = Modifier.weight(1f),
)
StatusBadge(
text = if (job.state == "active") "Active" else "Paused",
text =
if (job.state == "active") {
stringResource(
R.string.cron_status_active,
)
} else {
stringResource(R.string.cron_status_paused)
},
status =
if (job.state == "active") {
StatusBadgeType.SUCCESS
@@ -143,21 +152,21 @@ fun CronJobsScreen(
onClick = { viewModel.pauseCronJob(job.id) },
modifier = Modifier.weight(1f),
) {
Text("Pause")
Text(stringResource(R.string.cron_action_pause))
}
} else {
OutlinedButton(
onClick = { viewModel.resumeCronJob(job.id) },
modifier = Modifier.weight(1f),
) {
Text("Resume")
Text(stringResource(R.string.cron_action_resume))
}
}
OutlinedButton(
onClick = { viewModel.triggerCronJob(job.id) },
modifier = Modifier.weight(1f),
) {
Text("Run")
Text(stringResource(R.string.cron_action_run))
}
Button(
onClick = { viewModel.deleteCronJob(job.id) },
@@ -167,7 +176,7 @@ fun CronJobsScreen(
),
modifier = Modifier.weight(1f),
) {
Text("Delete")
Text(stringResource(R.string.action_delete))
}
}
}
@@ -32,10 +32,12 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.m57.hermescontrol.R
import com.m57.hermescontrol.ui.common.ErrorState
import com.m57.hermescontrol.ui.common.HermesScaffold
import com.m57.hermescontrol.ui.common.LoadingState
@@ -62,7 +64,7 @@ fun GatewayScreen(
}
HermesScaffold(
title = { Text("Gateway Control") },
title = { Text(stringResource(R.string.screen_gateway)) },
onOpenDrawer = onOpenDrawer,
isRefreshing = state.isLoading,
onRefresh = { viewModel.loadStatus() },
@@ -96,7 +98,7 @@ fun GatewayScreen(
)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { viewModel.loadStatus() }) {
Text("Retry")
Text(stringResource(R.string.action_retry))
}
}
} else {
@@ -132,7 +134,14 @@ fun GatewayScreen(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = if (isRunning) "GATEWAY RUNNING" else "GATEWAY STOPPED",
text =
if (isRunning) {
stringResource(
R.string.gateway_status_running,
)
} else {
stringResource(R.string.gateway_status_stopped)
},
style = MaterialTheme.typography.headlineSmall,
fontWeight = FontWeight.Bold,
color =
@@ -145,7 +154,7 @@ fun GatewayScreen(
status?.version?.let {
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "Version: $it",
text = stringResource(R.string.gateway_label_version, it),
style = MaterialTheme.typography.bodySmall,
color =
if (isRunning) {
@@ -170,7 +179,7 @@ fun GatewayScreen(
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Text(
text = "Controls",
text = stringResource(R.string.gateway_sec_controls),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
)
@@ -191,7 +200,7 @@ fun GatewayScreen(
) {
Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null)
Spacer(modifier = Modifier.width(4.dp))
Text("Start")
Text(stringResource(R.string.gateway_action_start))
}
Button(
@@ -204,7 +213,7 @@ fun GatewayScreen(
contentColor = Color.White,
),
) {
Text("Stop")
Text(stringResource(R.string.gateway_action_stop))
}
}
@@ -219,7 +228,7 @@ fun GatewayScreen(
) {
Icon(imageVector = Icons.Filled.Refresh, contentDescription = null)
Spacer(modifier = Modifier.width(4.dp))
Text("Restart")
Text(stringResource(R.string.gateway_action_restart))
}
if (state.isActionRunning) {
@@ -245,7 +254,7 @@ fun GatewayScreen(
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
Text(
text = "Active Platforms",
text = stringResource(R.string.gateway_sec_active_platforms),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
)
@@ -36,10 +36,12 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.m57.hermescontrol.R
import com.m57.hermescontrol.ui.common.EmptyState
import com.m57.hermescontrol.ui.common.ErrorState
import com.m57.hermescontrol.ui.common.HermesScaffold
@@ -67,7 +69,7 @@ fun KeysScreen(
}
HermesScaffold(
title = { Text("Keys & Credentials") },
title = { Text(stringResource(R.string.screen_keys)) },
onOpenDrawer = onOpenDrawer,
isRefreshing = state.isLoading,
onRefresh = { viewModel.loadKeys() },
@@ -87,10 +89,10 @@ fun KeysScreen(
state.envVars.isEmpty() -> {
EmptyState(
title = "No environment keys reported",
subtitle = "Verify configuration or retry",
title = stringResource(R.string.keys_empty_title),
subtitle = stringResource(R.string.toolsets_empty_desc),
onAction = { viewModel.loadKeys() },
actionLabel = "Refresh",
actionLabel = stringResource(R.string.content_desc_refresh),
modifier = Modifier.padding(paddingValues),
)
}
@@ -123,7 +125,7 @@ fun KeysScreen(
} else if (config.isSet) {
config.redactedValue ?: "********"
} else {
"Not configured"
stringResource(R.string.keys_label_not_configured)
}
Card(modifier = Modifier.fillMaxWidth()) {
@@ -158,13 +160,13 @@ fun KeysScreen(
viewModel.updateKey(key, editedValue)
isEditing = false
}) {
Text("Save")
Text(stringResource(R.string.action_save))
}
Spacer(modifier = Modifier.width(8.dp))
Button(onClick = {
isEditing = false
}) {
Text("Cancel")
Text(stringResource(R.string.action_cancel))
}
}
} else {
@@ -196,7 +198,10 @@ fun KeysScreen(
} else {
Icons.Filled.Visibility
},
contentDescription = "Toggle Visibility",
contentDescription =
stringResource(
R.string.keys_action_toggle_visibility,
),
)
}
}
@@ -206,7 +211,10 @@ fun KeysScreen(
}) {
Icon(
imageVector = Icons.Filled.Edit,
contentDescription = "Edit",
contentDescription =
stringResource(
R.string.nav_drawer_section_converse,
),
)
}
}
@@ -28,11 +28,13 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.m57.hermescontrol.R
import com.m57.hermescontrol.ui.common.ErrorState
import com.m57.hermescontrol.ui.common.HermesScaffold
import com.m57.hermescontrol.ui.common.LoadingState
@@ -59,7 +61,7 @@ fun McpServersScreen(
}
HermesScaffold(
title = { Text("MCP Servers") },
title = { Text(stringResource(R.string.screen_mcp_servers)) },
onOpenDrawer = onOpenDrawer,
isRefreshing = state.isLoading,
onRefresh = { viewModel.loadServers() },
@@ -89,7 +91,7 @@ fun McpServersScreen(
Text(text = state.errorMessage ?: "", color = MaterialTheme.colorScheme.error)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { viewModel.loadServers() }) {
Text("Retry")
Text(stringResource(R.string.action_retry))
}
}
} else {
@@ -129,7 +131,11 @@ fun McpServersScreen(
fontWeight = FontWeight.Bold,
)
Text(
text = "Transport: ${server.transport ?: "stdio"}",
text =
stringResource(
R.string.mcp_servers_label_transport,
server.transport ?: "stdio",
),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
@@ -145,7 +151,7 @@ fun McpServersScreen(
Spacer(modifier = Modifier.height(8.dp))
val fullCmd = "${server.command} ${server.args.orEmpty().joinToString(" ")}"
Text(
text = "Command:",
text = stringResource(R.string.mcp_servers_label_command),
style = MaterialTheme.typography.bodySmall,
fontWeight = FontWeight.Bold,
)
@@ -168,13 +174,13 @@ fun McpServersScreen(
onClick = { viewModel.testServer(server.name) },
modifier = Modifier.padding(end = 8.dp),
) {
Text("Test")
Text(stringResource(R.string.mcp_servers_action_test))
}
OutlinedButton(
onClick = { viewModel.deleteServer(server.name) },
) {
Text("Delete")
Text(stringResource(R.string.action_delete))
}
}
}
@@ -28,9 +28,11 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.m57.hermescontrol.R
import com.m57.hermescontrol.ui.common.EmptyState
import com.m57.hermescontrol.ui.common.ErrorState
import com.m57.hermescontrol.ui.common.HermesScaffold
@@ -58,7 +60,7 @@ fun PluginsScreen(
}
HermesScaffold(
title = { Text("Plugins") },
title = { Text(stringResource(R.string.screen_plugins)) },
onOpenDrawer = onOpenDrawer,
isRefreshing = state.isLoading,
onRefresh = { viewModel.loadPlugins() },
@@ -78,10 +80,10 @@ fun PluginsScreen(
state.plugins.isEmpty() -> {
EmptyState(
title = "No plugins found",
subtitle = "Verify configuration or retry",
title = stringResource(R.string.plugins_empty_title),
subtitle = stringResource(R.string.plugins_empty_desc),
onAction = { viewModel.loadPlugins() },
actionLabel = "Refresh",
actionLabel = stringResource(R.string.content_desc_refresh),
modifier = Modifier.padding(paddingValues),
)
}
@@ -129,7 +131,7 @@ fun PluginsScreen(
}
Spacer(modifier = Modifier.height(4.dp))
Text(
text = plugin.description ?: "No description provided.",
text = plugin.description ?: stringResource(R.string.plugins_no_desc),
style = MaterialTheme.typography.bodyMedium,
)
Spacer(modifier = Modifier.height(8.dp))
@@ -139,7 +141,7 @@ fun PluginsScreen(
) {
if (plugin.installed) {
OutlinedButton(onClick = { viewModel.updatePlugin(plugin.name) }) {
Text("Update")
Text(stringResource(R.string.plugins_action_update))
}
Spacer(modifier = Modifier.width(8.dp))
OutlinedButton(
@@ -149,11 +151,11 @@ fun PluginsScreen(
contentColor = MaterialTheme.colorScheme.error,
),
) {
Text("Uninstall")
Text(stringResource(R.string.plugins_action_uninstall))
}
} else {
Button(onClick = { viewModel.installPlugin(plugin.name) }) {
Text("Install")
Text(stringResource(R.string.plugins_action_install))
}
}
}
@@ -35,10 +35,12 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.m57.hermescontrol.R
import com.m57.hermescontrol.ui.common.EmptyState
import com.m57.hermescontrol.ui.common.ErrorState
import com.m57.hermescontrol.ui.common.HermesScaffold
@@ -71,7 +73,7 @@ fun ProfilesScreen(
}
HermesScaffold(
title = { Text("Profiles") },
title = { Text(stringResource(R.string.screen_profiles)) },
onOpenDrawer = onOpenDrawer,
isRefreshing = state.isLoading,
onRefresh = { viewModel.loadProfiles() },
@@ -91,10 +93,10 @@ fun ProfilesScreen(
state.profiles.isEmpty() -> {
EmptyState(
title = "No profiles available",
subtitle = "Verify configuration or retry",
title = stringResource(R.string.profiles_empty_title),
subtitle = stringResource(R.string.toolsets_empty_desc),
onAction = { viewModel.loadProfiles() },
actionLabel = "Refresh",
actionLabel = stringResource(R.string.content_desc_refresh),
modifier = Modifier.padding(paddingValues),
)
}
@@ -111,7 +113,7 @@ fun ProfilesScreen(
Text(text = state.errorMessage ?: "", color = MaterialTheme.colorScheme.error)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { viewModel.loadProfiles() }) {
Text("Retry")
Text(stringResource(R.string.action_retry))
}
}
} else {
@@ -170,7 +172,10 @@ fun ProfilesScreen(
if (isActive) {
Icon(
imageVector = Icons.Filled.Check,
contentDescription = "Active Profile",
contentDescription =
stringResource(
R.string.profiles_content_desc_active,
),
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(start = 8.dp),
)
@@ -186,11 +191,19 @@ fun ProfilesScreen(
) {
Column {
Text(
text = "Model: ${profile.model ?: "None"}",
text =
stringResource(
R.string.profiles_label_model,
profile.model ?: "None",
),
style = MaterialTheme.typography.bodyMedium,
)
Text(
text = "Provider: ${profile.provider ?: "None"}",
text =
stringResource(
R.string.profiles_label_provider,
profile.provider ?: "None",
),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
@@ -216,7 +229,7 @@ fun ProfilesScreen(
modifier = Modifier.width(16.dp),
)
Spacer(modifier = Modifier.width(4.dp))
Text("Edit Soul")
Text(stringResource(R.string.profiles_action_edit_soul))
}
Button(
@@ -226,7 +239,7 @@ fun ProfilesScreen(
tempModelName = profile.model ?: ""
},
) {
Text("Set Model")
Text(stringResource(R.string.profiles_action_set_model))
}
}
}