diff --git a/Android/app/build.gradle.kts b/Android/app/build.gradle.kts index 777b96a9..2215d582 100644 --- a/Android/app/build.gradle.kts +++ b/Android/app/build.gradle.kts @@ -11,8 +11,8 @@ android { applicationId = "com.archipelago.app" minSdk = 26 targetSdk = 35 - versionCode = 15 - versionName = "0.4.11" + versionCode = 16 + versionName = "0.4.12" vectorDrawables { useSupportLibrary = true diff --git a/Android/app/src/main/java/com/archipelago/app/ui/components/NESMenu.kt b/Android/app/src/main/java/com/archipelago/app/ui/components/NESMenu.kt index ca827a5e..2eb41d43 100644 --- a/Android/app/src/main/java/com/archipelago/app/ui/components/NESMenu.kt +++ b/Android/app/src/main/java/com/archipelago/app/ui/components/NESMenu.kt @@ -75,6 +75,7 @@ fun NESMenu( onDismiss: () -> Unit, onSelectServer: (ServerEntry) -> Unit, onAddServer: (ServerEntry) -> Unit, + onEditServer: (ServerEntry, ServerEntry) -> Unit, onRemoveServer: (ServerEntry) -> Unit, onToggleMode: () -> Unit, onToggleStyle: () -> Unit, @@ -87,7 +88,7 @@ fun NESMenu( contentAlignment = Alignment.Center, ) { AnimatedVisibility(visible = visible, enter = fadeIn() + scaleIn(initialScale = 0.95f), exit = fadeOut() + scaleOut(targetScale = 0.95f)) { - MenuPanel(servers, activeServer, isGamepadMode, controllerStyle, onDismiss, onSelectServer, onAddServer, onRemoveServer, onToggleMode, onToggleStyle, onBackToWebView) + MenuPanel(servers, activeServer, isGamepadMode, controllerStyle, onDismiss, onSelectServer, onAddServer, onEditServer, onRemoveServer, onToggleMode, onToggleStyle, onBackToWebView) } } } @@ -102,21 +103,39 @@ private fun MenuPanel( onDismiss: () -> Unit, onSelectServer: (ServerEntry) -> Unit, onAddServer: (ServerEntry) -> Unit, + onEditServer: (ServerEntry, ServerEntry) -> Unit, onRemoveServer: (ServerEntry) -> Unit, onToggleMode: () -> Unit, onToggleStyle: () -> Unit, onBackToWebView: (() -> Unit)?, ) { var showAdd by remember { mutableStateOf(false) } + // The saved server being edited, or null when adding a new one. + var editing by remember { mutableStateOf(null) } var nm by remember { mutableStateOf("") } var addr by remember { mutableStateOf("") } var pwd by remember { mutableStateOf("") } + fun resetForm() { + nm = ""; addr = ""; pwd = ""; showAdd = false; editing = null + } + + fun startEdit(server: ServerEntry) { + editing = server + nm = server.name; addr = server.address; pwd = server.password + showAdd = false + } + fun submit() { - if (addr.isNotBlank()) { + if (addr.isBlank()) return + val orig = editing + if (orig != null) { + // Preserve fields the compact form doesn't expose (scheme, port). + onEditServer(orig, orig.copy(address = addr, password = pwd, name = nm)) + } else { onAddServer(ServerEntry(addr, false, password = pwd, name = nm)) - nm = ""; addr = ""; pwd = ""; showAdd = false } + resetForm() } Column( @@ -149,6 +168,7 @@ private fun MenuPanel( label = server.displayName(), selected = active, onClick = { onSelectServer(server) }, + onEdit = { startEdit(server) }, onRemove = { onRemoveServer(server) }, ) } @@ -157,8 +177,8 @@ private fun MenuPanel( Text("No servers", color = TextMuted, fontSize = 14.sp, modifier = Modifier.padding(vertical = 4.dp)) } - // Add server - if (showAdd) { + // Add / edit server + if (showAdd || editing != null) { Column( Modifier .fillMaxWidth() @@ -168,6 +188,25 @@ private fun MenuPanel( .padding(12.dp), verticalArrangement = Arrangement.spacedBy(8.dp), ) { + Row( + Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text( + if (editing != null) "Edit Server" else "Add Server", + color = TextMuted, + fontSize = 13.sp, + letterSpacing = 1.sp, + fontWeight = FontWeight.Medium, + ) + Text( + "Cancel", + color = TextMuted, + fontSize = 13.sp, + modifier = Modifier.clickable { resetForm() }.padding(start = 8.dp), + ) + } GlassField( value = nm, onValueChange = { nm = it }, placeholder = "Name (optional)", @@ -228,6 +267,7 @@ private fun MenuItem( selected: Boolean = false, labelColor: Color = TextPrimary, onClick: () -> Unit, + onEdit: (() -> Unit)? = null, onRemove: (() -> Unit)? = null, ) { Row( @@ -247,7 +287,16 @@ private fun MenuItem( color = if (selected) BitcoinOrange else labelColor, fontSize = 16.sp, fontWeight = FontWeight.Medium, + modifier = Modifier.weight(1f), ) + if (onEdit != null) { + Text( + "✎", + color = TextMuted, + fontSize = 16.sp, + modifier = Modifier.clickable { onEdit() }.padding(horizontal = 8.dp), + ) + } if (onRemove != null) { Text( "✕", diff --git a/Android/app/src/main/java/com/archipelago/app/ui/screens/RemoteInputScreen.kt b/Android/app/src/main/java/com/archipelago/app/ui/screens/RemoteInputScreen.kt index 94322deb..ad35d89e 100644 --- a/Android/app/src/main/java/com/archipelago/app/ui/screens/RemoteInputScreen.kt +++ b/Android/app/src/main/java/com/archipelago/app/ui/screens/RemoteInputScreen.kt @@ -216,6 +216,17 @@ fun RemoteInputScreen(onBack: () -> Unit) { onAddServer = { server -> scope.launch { prefs.addSavedServer(server); if (activeServer == null) prefs.setActiveServer(server) } }, + onEditServer = { original, updated -> + scope.launch { + prefs.updateSavedServer(original, updated) + // If the edited server is the live one, reconnect with the new + // address/credentials so the change takes effect immediately. + if (original.serialize() == activeServer?.serialize()) { + ws.disconnect() + prefs.setActiveServer(updated) + } + } + }, onRemoveServer = { server -> scope.launch { prefs.removeSavedServer(server) diff --git a/neode-ui/public/packages/archipelago-companion.apk b/neode-ui/public/packages/archipelago-companion.apk index f6338941..6a177479 100644 Binary files a/neode-ui/public/packages/archipelago-companion.apk and b/neode-ui/public/packages/archipelago-companion.apk differ