refactor(#78): extract navigation and drawer labels to string resources
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package com.m57.hermescontrol
|
package com.m57.hermescontrol
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -58,12 +59,14 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation3.runtime.NavKey
|
import androidx.navigation3.runtime.NavKey
|
||||||
import androidx.navigation3.runtime.entryProvider
|
import androidx.navigation3.runtime.entryProvider
|
||||||
import androidx.navigation3.runtime.rememberNavBackStack
|
import androidx.navigation3.runtime.rememberNavBackStack
|
||||||
import androidx.navigation3.ui.NavDisplay
|
import androidx.navigation3.ui.NavDisplay
|
||||||
|
import com.m57.hermescontrol.R
|
||||||
import com.m57.hermescontrol.data.local.AuthManager
|
import com.m57.hermescontrol.data.local.AuthManager
|
||||||
import com.m57.hermescontrol.data.ws.ConnectionStatus
|
import com.m57.hermescontrol.data.ws.ConnectionStatus
|
||||||
import com.m57.hermescontrol.data.ws.HermesWsClient
|
import com.m57.hermescontrol.data.ws.HermesWsClient
|
||||||
@@ -94,33 +97,33 @@ import com.m57.hermescontrol.ui.webhooks.WebhooksScreen as WebhooksScreenContent
|
|||||||
|
|
||||||
private data class BottomNavItem(
|
private data class BottomNavItem(
|
||||||
val key: NavKey,
|
val key: NavKey,
|
||||||
val label: String,
|
@param:StringRes val labelRes: Int,
|
||||||
val icon: ImageVector,
|
val icon: ImageVector,
|
||||||
)
|
)
|
||||||
|
|
||||||
/** Master list of ALL screens available for bottom-nav selection. */
|
/** Master list of ALL screens available for bottom-nav selection. */
|
||||||
private val ALL_NAV_ITEMS: List<BottomNavItem> =
|
private val ALL_NAV_ITEMS: List<BottomNavItem> =
|
||||||
listOf(
|
listOf(
|
||||||
BottomNavItem(ChatScreen, "Chat", Icons.AutoMirrored.Filled.Chat),
|
BottomNavItem(ChatScreen, R.string.screen_chat, Icons.AutoMirrored.Filled.Chat),
|
||||||
BottomNavItem(SkillsScreen, "Skills", Icons.Filled.Extension),
|
BottomNavItem(SkillsScreen, R.string.screen_skills, Icons.Filled.Extension),
|
||||||
BottomNavItem(CronJobsScreen, "Cron", Icons.Filled.Schedule),
|
BottomNavItem(CronJobsScreen, R.string.screen_cron, Icons.Filled.Schedule),
|
||||||
BottomNavItem(SystemScreen, "System", Icons.Filled.Info),
|
BottomNavItem(SystemScreen, R.string.screen_system, Icons.Filled.Info),
|
||||||
BottomNavItem(SettingsScreen, "Settings", Icons.Filled.Settings),
|
BottomNavItem(SettingsScreen, R.string.screen_settings, Icons.Filled.Settings),
|
||||||
BottomNavItem(ProfilesScreen, "Profiles", Icons.Filled.AccountCircle),
|
BottomNavItem(ProfilesScreen, R.string.screen_profiles, Icons.Filled.AccountCircle),
|
||||||
BottomNavItem(WebhooksScreen, "Webhooks", Icons.Filled.Webhook),
|
BottomNavItem(WebhooksScreen, R.string.screen_webhooks, Icons.Filled.Webhook),
|
||||||
BottomNavItem(GatewayScreen, "Gateway", Icons.Filled.Bolt),
|
BottomNavItem(GatewayScreen, R.string.screen_gateway, Icons.Filled.Bolt),
|
||||||
BottomNavItem(ToolsetsScreen, "Toolsets", Icons.Filled.Build),
|
BottomNavItem(ToolsetsScreen, R.string.screen_toolsets, Icons.Filled.Build),
|
||||||
BottomNavItem(PluginsScreen, "Plugins", Icons.Filled.Memory),
|
BottomNavItem(PluginsScreen, R.string.screen_plugins, Icons.Filled.Memory),
|
||||||
BottomNavItem(ConfigScreen, "Config", Icons.Filled.Code),
|
BottomNavItem(ConfigScreen, R.string.screen_config, Icons.Filled.Code),
|
||||||
BottomNavItem(McpServersScreen, "MCP Servers", Icons.Filled.Dashboard),
|
BottomNavItem(McpServersScreen, R.string.screen_mcp_servers, Icons.Filled.Dashboard),
|
||||||
BottomNavItem(ModelScreen, "Models", Icons.Filled.Settings),
|
BottomNavItem(ModelScreen, R.string.screen_models, Icons.Filled.Settings),
|
||||||
BottomNavItem(PairingScreen, "Pairing", Icons.Filled.Devices),
|
BottomNavItem(PairingScreen, R.string.screen_pairing, Icons.Filled.Devices),
|
||||||
BottomNavItem(KeysScreen, "Keys", Icons.Filled.Key),
|
BottomNavItem(KeysScreen, R.string.screen_keys, Icons.Filled.Key),
|
||||||
BottomNavItem(ChannelsScreen, "Channels", Icons.AutoMirrored.Filled.ListAlt),
|
BottomNavItem(ChannelsScreen, R.string.screen_channels, Icons.AutoMirrored.Filled.ListAlt),
|
||||||
BottomNavItem(LogsScreen, "Logs", Icons.Filled.HistoryEdu),
|
BottomNavItem(LogsScreen, R.string.screen_logs, Icons.Filled.HistoryEdu),
|
||||||
BottomNavItem(KanbanScreen, "Kanban", Icons.Filled.Dashboard),
|
BottomNavItem(KanbanScreen, R.string.screen_kanban, Icons.Filled.Dashboard),
|
||||||
BottomNavItem(AchievementsScreen, "Achievements", Icons.Filled.Info),
|
BottomNavItem(AchievementsScreen, R.string.screen_achievements, Icons.Filled.Info),
|
||||||
BottomNavItem(HistoryScreen, "History", Icons.Filled.History),
|
BottomNavItem(HistoryScreen, R.string.screen_history, Icons.Filled.History),
|
||||||
)
|
)
|
||||||
|
|
||||||
/** Lookup: data-object simple name → NavKey (used by bottom-nav config). */
|
/** Lookup: data-object simple name → NavKey (used by bottom-nav config). */
|
||||||
@@ -134,17 +137,17 @@ private fun resolveBottomNavItems(names: List<String>): List<BottomNavItem> =
|
|||||||
// ── Drawer sections ────────────────────────────────────────────────────
|
// ── Drawer sections ────────────────────────────────────────────────────
|
||||||
|
|
||||||
private enum class DrawerSection(
|
private enum class DrawerSection(
|
||||||
val title: String,
|
@param:StringRes val titleRes: Int,
|
||||||
) {
|
) {
|
||||||
CONVERSE("Converse"),
|
CONVERSE(R.string.nav_drawer_section_converse),
|
||||||
AUTOMATE("Automate"),
|
AUTOMATE(R.string.nav_drawer_section_automate),
|
||||||
CONFIGURE("Configure"),
|
CONFIGURE(R.string.nav_drawer_section_configure),
|
||||||
INSPECT("Inspect"),
|
INSPECT(R.string.nav_drawer_section_inspect),
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class DrawerEntry(
|
private data class DrawerEntry(
|
||||||
val key: NavKey,
|
val key: NavKey,
|
||||||
val label: String,
|
@param:StringRes val labelRes: Int,
|
||||||
val icon: ImageVector,
|
val icon: ImageVector,
|
||||||
val section: DrawerSection,
|
val section: DrawerSection,
|
||||||
)
|
)
|
||||||
@@ -152,29 +155,34 @@ private data class DrawerEntry(
|
|||||||
private val DRAWER_ENTRIES =
|
private val DRAWER_ENTRIES =
|
||||||
listOf(
|
listOf(
|
||||||
// Converse
|
// Converse
|
||||||
DrawerEntry(ChatScreen, "Chat", Icons.AutoMirrored.Filled.Chat, DrawerSection.CONVERSE),
|
DrawerEntry(ChatScreen, R.string.screen_chat, Icons.AutoMirrored.Filled.Chat, DrawerSection.CONVERSE),
|
||||||
DrawerEntry(HistoryScreen, "History", Icons.Filled.History, DrawerSection.CONVERSE),
|
DrawerEntry(HistoryScreen, R.string.screen_history, Icons.Filled.History, DrawerSection.CONVERSE),
|
||||||
DrawerEntry(ProfilesScreen, "Profiles", Icons.Filled.AccountCircle, DrawerSection.CONVERSE),
|
DrawerEntry(ProfilesScreen, R.string.screen_profiles, Icons.Filled.AccountCircle, DrawerSection.CONVERSE),
|
||||||
// Automate
|
// Automate
|
||||||
DrawerEntry(CronJobsScreen, "Cron", Icons.Filled.Schedule, DrawerSection.AUTOMATE),
|
DrawerEntry(CronJobsScreen, R.string.screen_cron, Icons.Filled.Schedule, DrawerSection.AUTOMATE),
|
||||||
DrawerEntry(WebhooksScreen, "Webhooks", Icons.Filled.Webhook, DrawerSection.AUTOMATE),
|
DrawerEntry(WebhooksScreen, R.string.screen_webhooks, Icons.Filled.Webhook, DrawerSection.AUTOMATE),
|
||||||
DrawerEntry(GatewayScreen, "Gateway", Icons.Filled.Bolt, DrawerSection.AUTOMATE),
|
DrawerEntry(GatewayScreen, R.string.screen_gateway, Icons.Filled.Bolt, DrawerSection.AUTOMATE),
|
||||||
// Configure
|
// Configure
|
||||||
DrawerEntry(SkillsScreen, "Skills", Icons.Filled.Extension, DrawerSection.CONFIGURE),
|
DrawerEntry(SkillsScreen, R.string.screen_skills, Icons.Filled.Extension, DrawerSection.CONFIGURE),
|
||||||
DrawerEntry(SettingsScreen, "Settings", Icons.Filled.Settings, DrawerSection.CONFIGURE),
|
DrawerEntry(SettingsScreen, R.string.screen_settings, Icons.Filled.Settings, DrawerSection.CONFIGURE),
|
||||||
DrawerEntry(ToolsetsScreen, "Toolsets", Icons.Filled.Build, DrawerSection.CONFIGURE),
|
DrawerEntry(ToolsetsScreen, R.string.screen_toolsets, Icons.Filled.Build, DrawerSection.CONFIGURE),
|
||||||
DrawerEntry(PluginsScreen, "Plugins", Icons.Filled.Memory, DrawerSection.CONFIGURE),
|
DrawerEntry(PluginsScreen, R.string.screen_plugins, Icons.Filled.Memory, DrawerSection.CONFIGURE),
|
||||||
DrawerEntry(ConfigScreen, "Config", Icons.Filled.Code, DrawerSection.CONFIGURE),
|
DrawerEntry(ConfigScreen, R.string.screen_config, Icons.Filled.Code, DrawerSection.CONFIGURE),
|
||||||
DrawerEntry(McpServersScreen, "MCP Servers", Icons.Filled.Dashboard, DrawerSection.CONFIGURE),
|
DrawerEntry(McpServersScreen, R.string.screen_mcp_servers, Icons.Filled.Dashboard, DrawerSection.CONFIGURE),
|
||||||
DrawerEntry(ModelScreen, "Models", Icons.Filled.Settings, DrawerSection.CONFIGURE),
|
DrawerEntry(ModelScreen, R.string.screen_models, Icons.Filled.Settings, DrawerSection.CONFIGURE),
|
||||||
DrawerEntry(PairingScreen, "Pairing", Icons.Filled.Devices, DrawerSection.CONFIGURE),
|
DrawerEntry(PairingScreen, R.string.screen_pairing, Icons.Filled.Devices, DrawerSection.CONFIGURE),
|
||||||
DrawerEntry(KeysScreen, "Keys", Icons.Filled.Key, DrawerSection.CONFIGURE),
|
DrawerEntry(KeysScreen, R.string.screen_keys, Icons.Filled.Key, DrawerSection.CONFIGURE),
|
||||||
DrawerEntry(ChannelsScreen, "Channels", Icons.AutoMirrored.Filled.ListAlt, DrawerSection.CONFIGURE),
|
DrawerEntry(
|
||||||
|
ChannelsScreen,
|
||||||
|
R.string.screen_channels,
|
||||||
|
Icons.AutoMirrored.Filled.ListAlt,
|
||||||
|
DrawerSection.CONFIGURE,
|
||||||
|
),
|
||||||
// Inspect
|
// Inspect
|
||||||
DrawerEntry(SystemScreen, "System", Icons.Filled.Info, DrawerSection.INSPECT),
|
DrawerEntry(SystemScreen, R.string.screen_system, Icons.Filled.Info, DrawerSection.INSPECT),
|
||||||
DrawerEntry(LogsScreen, "Logs", Icons.Filled.HistoryEdu, DrawerSection.INSPECT),
|
DrawerEntry(LogsScreen, R.string.screen_logs, Icons.Filled.HistoryEdu, DrawerSection.INSPECT),
|
||||||
DrawerEntry(KanbanScreen, "Kanban", Icons.Filled.Dashboard, DrawerSection.INSPECT),
|
DrawerEntry(KanbanScreen, R.string.screen_kanban, Icons.Filled.Dashboard, DrawerSection.INSPECT),
|
||||||
DrawerEntry(AchievementsScreen, "Achievements", Icons.Filled.Info, DrawerSection.INSPECT),
|
DrawerEntry(AchievementsScreen, R.string.screen_achievements, Icons.Filled.Info, DrawerSection.INSPECT),
|
||||||
)
|
)
|
||||||
|
|
||||||
private val DRAWER_GESTURE_SCREENS: Set<NavKey> = ALL_NAV_ITEMS.mapTo(mutableSetOf()) { it.key }
|
private val DRAWER_GESTURE_SCREENS: Set<NavKey> = ALL_NAV_ITEMS.mapTo(mutableSetOf()) { it.key }
|
||||||
@@ -370,7 +378,7 @@ fun MainNavigation(sessionId: String? = null) {
|
|||||||
modifier = Modifier.padding(start = 20.dp, top = 20.dp, end = 16.dp, bottom = 4.dp),
|
modifier = Modifier.padding(start = 20.dp, top = 20.dp, end = 16.dp, bottom = 4.dp),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "⚡ Hermes",
|
text = stringResource(R.string.nav_drawer_title),
|
||||||
style =
|
style =
|
||||||
MaterialTheme.typography.headlineSmall.copy(
|
MaterialTheme.typography.headlineSmall.copy(
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
@@ -386,7 +394,7 @@ fun MainNavigation(sessionId: String? = null) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
Text(
|
Text(
|
||||||
text = "AI Agent Control",
|
text = stringResource(R.string.nav_drawer_subtitle),
|
||||||
modifier = Modifier.padding(start = 20.dp, bottom = 12.dp, end = 16.dp),
|
modifier = Modifier.padding(start = 20.dp, bottom = 12.dp, end = 16.dp),
|
||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
@@ -395,7 +403,7 @@ fun MainNavigation(sessionId: String? = null) {
|
|||||||
|
|
||||||
for (section in DrawerSection.entries) {
|
for (section in DrawerSection.entries) {
|
||||||
Text(
|
Text(
|
||||||
text = section.title.uppercase(),
|
text = stringResource(section.titleRes).uppercase(),
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.padding(
|
Modifier.padding(
|
||||||
start = 20.dp,
|
start = 20.dp,
|
||||||
@@ -412,7 +420,7 @@ fun MainNavigation(sessionId: String? = null) {
|
|||||||
.forEach { entry ->
|
.forEach { entry ->
|
||||||
NavigationDrawerItem(
|
NavigationDrawerItem(
|
||||||
icon = { Icon(entry.icon, contentDescription = null) },
|
icon = { Icon(entry.icon, contentDescription = null) },
|
||||||
label = { Text(entry.label) },
|
label = { Text(stringResource(entry.labelRes)) },
|
||||||
selected = currentScreen == entry.key,
|
selected = currentScreen == entry.key,
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch { drawerState.close() }
|
scope.launch { drawerState.close() }
|
||||||
@@ -427,7 +435,11 @@ fun MainNavigation(sessionId: String? = null) {
|
|||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.padding(horizontal = 12.dp, vertical = 2.dp)
|
.padding(horizontal = 12.dp, vertical = 2.dp)
|
||||||
.testTag("drawer_${entry.label.lowercase().replace(' ', '_')}"),
|
.testTag(
|
||||||
|
"drawer_${entry.key::class.simpleName?.lowercase()?.removeSuffix(
|
||||||
|
"screen",
|
||||||
|
) ?: ""}",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -448,9 +460,12 @@ fun MainNavigation(sessionId: String? = null) {
|
|||||||
NavigationBarItem(
|
NavigationBarItem(
|
||||||
selected = currentScreen == item.key,
|
selected = currentScreen == item.key,
|
||||||
onClick = { NavigationController.navigateTo(item.key) },
|
onClick = { NavigationController.navigateTo(item.key) },
|
||||||
icon = { Icon(item.icon, contentDescription = item.label) },
|
icon = { Icon(item.icon, contentDescription = stringResource(item.labelRes)) },
|
||||||
label = { Text(item.label) },
|
label = { Text(stringResource(item.labelRes)) },
|
||||||
modifier = Modifier.testTag("nav_${item.label.lowercase().replace(' ', '_')}"),
|
modifier =
|
||||||
|
Modifier.testTag(
|
||||||
|
"nav_${item.key::class.simpleName?.lowercase()?.removeSuffix("screen") ?: ""}",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,4 +28,34 @@
|
|||||||
|
|
||||||
<!-- General Errors -->
|
<!-- General Errors -->
|
||||||
<string name="error_unknown">Unknown error</string>
|
<string name="error_unknown">Unknown error</string>
|
||||||
|
|
||||||
|
<!-- Navigation & Drawer -->
|
||||||
|
<string name="nav_drawer_title">⚡ Hermes</string>
|
||||||
|
<string name="nav_drawer_subtitle">AI Agent Control</string>
|
||||||
|
<string name="nav_drawer_section_converse">Converse</string>
|
||||||
|
<string name="nav_drawer_section_automate">Automate</string>
|
||||||
|
<string name="nav_drawer_section_configure">Configure</string>
|
||||||
|
<string name="nav_drawer_section_inspect">Inspect</string>
|
||||||
|
|
||||||
|
<!-- Screen Names -->
|
||||||
|
<string name="screen_chat">Chat</string>
|
||||||
|
<string name="screen_skills">Skills</string>
|
||||||
|
<string name="screen_cron">Cron</string>
|
||||||
|
<string name="screen_system">System</string>
|
||||||
|
<string name="screen_settings">Settings</string>
|
||||||
|
<string name="screen_profiles">Profiles</string>
|
||||||
|
<string name="screen_webhooks">Webhooks</string>
|
||||||
|
<string name="screen_gateway">Gateway</string>
|
||||||
|
<string name="screen_toolsets">Toolsets</string>
|
||||||
|
<string name="screen_plugins">Plugins</string>
|
||||||
|
<string name="screen_config">Config</string>
|
||||||
|
<string name="screen_mcp_servers">MCP Servers</string>
|
||||||
|
<string name="screen_models">Models</string>
|
||||||
|
<string name="screen_pairing">Pairing</string>
|
||||||
|
<string name="screen_keys">Keys</string>
|
||||||
|
<string name="screen_channels">Channels</string>
|
||||||
|
<string name="screen_logs">Logs</string>
|
||||||
|
<string name="screen_kanban">Kanban</string>
|
||||||
|
<string name="screen_achievements">Achievements</string>
|
||||||
|
<string name="screen_history">History</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user