From d901cba8288986674db0acaf0474b4a1ae0403ce Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Wed, 5 Jun 2024 01:22:05 +0200 Subject: [PATCH] feat(better_location): edit coordinates Signed-off-by: rhunk <101876869+rhunk@users.noreply.github.com> --- .../me/rhunk/snapenhance/ui/manager/Routes.kt | 2 +- .../pages/location/AddCoordinatesDialog.kt | 91 +++++++ .../{ => location}/BetterLocationRoot.kt | 225 +++++++++++------- common/src/main/assets/lang/en_US.json | 2 + 4 files changed, 235 insertions(+), 85 deletions(-) create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/location/AddCoordinatesDialog.kt rename app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/{ => location}/BetterLocationRoot.kt (65%) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Routes.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Routes.kt index d4eafbda..79ae5557 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Routes.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Routes.kt @@ -11,7 +11,7 @@ import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavGraphBuilder import me.rhunk.snapenhance.RemoteSideContext -import me.rhunk.snapenhance.ui.manager.pages.BetterLocationRoot +import me.rhunk.snapenhance.ui.manager.pages.location.BetterLocationRoot import me.rhunk.snapenhance.ui.manager.pages.FileImportsRoot import me.rhunk.snapenhance.ui.manager.pages.LoggerHistoryRoot import me.rhunk.snapenhance.ui.manager.pages.TasksRootSection diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/location/AddCoordinatesDialog.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/location/AddCoordinatesDialog.kt new file mode 100644 index 00000000..ce0b636a --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/location/AddCoordinatesDialog.kt @@ -0,0 +1,91 @@ +package me.rhunk.snapenhance.ui.manager.pages.location + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Button +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.text.TextRange +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import kotlinx.coroutines.delay +import me.rhunk.snapenhance.bridge.location.LocationCoordinates +import me.rhunk.snapenhance.common.bridge.wrapper.LocaleWrapper +import me.rhunk.snapenhance.ui.util.AlertDialogs + + +@Composable +fun AddCoordinatesDialog( + alertDialogs: AlertDialogs, + translation: LocaleWrapper, + locationCoordinates: LocationCoordinates, + confirm: (locationCoordinates: LocationCoordinates) -> Unit +) { + var savedName by remember { + mutableStateOf( + (locationCoordinates.name ?: "").let { + TextFieldValue(it, selection = TextRange(it.length)) + } + ) + } + var savedLatitude by remember { mutableStateOf(locationCoordinates.latitude.toFloat().toString()) } + var savedLongitude by remember { mutableStateOf(locationCoordinates.longitude.toFloat().toString()) } + + alertDialogs.DefaultDialogCard { + val focusRequester = remember { FocusRequester() } + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(2.dp) + ) { + Text(translation["save_coordinates_dialog_title"], fontSize = 20.sp, fontWeight = FontWeight.Bold) + OutlinedTextField( + modifier = Modifier + .focusRequester(focusRequester), + value = savedName, + onValueChange = { savedName = it }, + label = { Text(translation["saved_name_dialog_hint"]) } + ) + + LaunchedEffect(Unit) { + delay(200) + focusRequester.requestFocus() + } + + OutlinedTextField( + value = savedLatitude, + onValueChange = { savedLatitude = it }, + label = { Text(translation["latitude_dialog_hint"]) } + ) + OutlinedTextField( + value = savedLongitude, + onValueChange = { savedLongitude = it }, + label = { Text(translation["longitude_dialog_hint"]) } + ) + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp), + horizontalArrangement = Arrangement.End + ) { + Button( + onClick = { + confirm(LocationCoordinates().apply { + this.name = savedName.text + this.latitude = savedLatitude.toDoubleOrNull() ?: 0.0 + this.longitude = savedLongitude.toDoubleOrNull() ?: 0.0 + }) + }, + enabled = savedName.text.isNotBlank() && savedLatitude.isNotBlank() && savedLongitude.isNotBlank() + ) { + Text(translation["save_dialog_button"]) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/BetterLocationRoot.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/location/BetterLocationRoot.kt similarity index 65% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/BetterLocationRoot.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/location/BetterLocationRoot.kt index 78017fb5..212c6114 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/BetterLocationRoot.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/pages/location/BetterLocationRoot.kt @@ -1,25 +1,27 @@ -package me.rhunk.snapenhance.ui.manager.pages +package me.rhunk.snapenhance.ui.manager.pages.location -import androidx.compose.foundation.clickable +import android.os.Parcel import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.DeleteOutline +import androidx.compose.material.icons.filled.Edit import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clipToBounds -import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavBackStackEntry +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import me.rhunk.snapenhance.bridge.location.FriendLocation import me.rhunk.snapenhance.bridge.location.LocationCoordinates import me.rhunk.snapenhance.common.ui.rememberAsyncMutableStateList @@ -167,10 +169,9 @@ class BetterLocationRoot : Routes.Route() { val mapView = remember { mutableStateOf(null) } var spoofedCoordinates by remember(showTeleportDialog, showMap) { mutableStateOf(coordinatesProperty.value.get() as? Pair<*, *>) } - fun addSavedCoordinate(id: Int?, locationCoordinates: LocationCoordinates) { + fun addSavedCoordinate(id: Int?, locationCoordinates: LocationCoordinates, onSuccess: suspend (id: Int) -> Unit = {}) { context.coroutineScope.launch { - context.database.addOrUpdateLocationCoordinate(id, locationCoordinates) - updateDispatcher.dispatch() + onSuccess(context.database.addOrUpdateLocationCoordinate(id, locationCoordinates)) } } @@ -199,53 +200,30 @@ class BetterLocationRoot : Routes.Route() { "latitude" to ((spoofedCoordinates?.first as? Double)?.toFloat() ?: "0.0").toString(), "longitude" to ((spoofedCoordinates?.second as? Double)?.toFloat() ?: "0.0").toString() ), - fontSize = 20.sp, + fontSize = 18.sp, fontWeight = FontWeight.Bold, textAlign = TextAlign.Center, modifier = Modifier .fillMaxWidth() - .padding(12.dp) + .padding(8.dp) ) if (addSavedCoordinateDialog) { - var savedName by remember { mutableStateOf("") } me.rhunk.snapenhance.ui.util.Dialog( onDismissRequest = { addSavedCoordinateDialog = false }, content = { - alertDialogs.DefaultDialogCard { - val focusRequester = remember { FocusRequester() } - Column( - modifier = Modifier.padding(8.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - Text(translation["save_coordinates_dialog_title"], fontSize = 20.sp, fontWeight = FontWeight.Bold) - OutlinedTextField( - modifier = Modifier - .focusRequester(focusRequester) - .onGloballyPositioned { - focusRequester.requestFocus() - }, - value = savedName, - onValueChange = { savedName = it }, - label = { Text(translation["saved_name_dialog_hint"]) } - ) - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.End - ) { - Button( - onClick = { - addSavedCoordinateDialog = false - addSavedCoordinate(null, LocationCoordinates().apply { - this.name = savedName - this.latitude = marker.value?.position?.latitude as Double - this.longitude = marker.value?.position?.longitude as Double - }) - }, - enabled = savedName.isNotBlank() - ) { - Text(translation["save_dialog_button"]) - } + AddCoordinatesDialog( + alertDialogs, + translation, + LocationCoordinates().apply { + this.latitude = marker.value?.position?.latitude ?: 0.0 + this.longitude = marker.value?.position?.longitude ?: 0.0 + }, + ) { coordinates -> + addSavedCoordinateDialog = false + addSavedCoordinate(null, coordinates) { + withContext(Dispatchers.Main) { + savedCoordinates.add(0, coordinates.apply { id = it }) } } } @@ -263,6 +241,11 @@ class BetterLocationRoot : Routes.Route() { showMap = false context.config.writeConfig() } + DisposableEffect(Unit) { + onDispose { + marker.value = null + } + } } ) } @@ -276,7 +259,7 @@ class BetterLocationRoot : Routes.Route() { Row( modifier = Modifier .fillMaxWidth() - .padding(16.dp), + .padding(8.dp), horizontalArrangement = Arrangement.SpaceEvenly, verticalAlignment = Alignment.CenterVertically ) { @@ -290,7 +273,7 @@ class BetterLocationRoot : Routes.Route() { } item { Row( - modifier = Modifier.padding(16.dp), + modifier = Modifier.padding(start = 16.dp, end = 16.dp), verticalAlignment = Alignment.CenterVertically ) { Text(text = translation["spoof_location_toggle"]) @@ -306,12 +289,27 @@ class BetterLocationRoot : Routes.Route() { } } item { - Text( - translation["saved_coordinates_title"], - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - modifier = Modifier.padding(start = 16.dp) - ) + Row( + modifier = Modifier + .fillMaxWidth() + .padding(start = 12.dp, end = 12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + translation["saved_coordinates_title"], + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier.weight(1f), + lineHeight = 20.sp + ) + IconButton( + onClick = { + addSavedCoordinateDialog = true + } + ) { + Icon(Icons.Default.Add, contentDescription = "Add") + } + } } item { if (savedCoordinates.isEmpty()) { @@ -323,8 +321,19 @@ class BetterLocationRoot : Routes.Route() { ) } } - items(savedCoordinates) { coordinates -> + items(savedCoordinates, key = { it.id }) { coordinates -> + var mutableCoordinates by remember { mutableStateOf(coordinates) } + val isSelected = spoofedCoordinates == mutableCoordinates.latitude to mutableCoordinates.longitude var showDeleteDialog by remember { mutableStateOf(false) } + var showEditDialog by remember { mutableStateOf(false) } + + fun setSpoofedCoordinates() { + spoofedCoordinates = mutableCoordinates.latitude to mutableCoordinates.longitude + coordinatesProperty.value.setAny(spoofedCoordinates) + context.coroutineScope.launch { + context.config.writeConfig() + } + } if (showDeleteDialog) { me.rhunk.snapenhance.ui.util.Dialog( @@ -337,7 +346,7 @@ class BetterLocationRoot : Routes.Route() { showDeleteDialog = false context.coroutineScope.launch { context.database.removeLocationCoordinate(coordinates.id) - updateDispatcher.dispatch() + savedCoordinates.remove(coordinates) } }, onDismiss = { showDeleteDialog = false } @@ -346,39 +355,87 @@ class BetterLocationRoot : Routes.Route() { ) } - Row( + if (showEditDialog) { + me.rhunk.snapenhance.ui.util.Dialog( + onDismissRequest = { showEditDialog = false }, + content = { + AddCoordinatesDialog( + alertDialogs, + translation, + mutableCoordinates + ) { + val itemId = coordinates.id + context.coroutineScope.launch { + addSavedCoordinate(itemId, it) + } + Parcel.obtain().apply { + it.writeToParcel(this, 0) + setDataPosition(0) + coordinates.readFromParcel(this) + coordinates.id = itemId + recycle() + } + mutableCoordinates = it + if (isSelected) setSpoofedCoordinates() + showEditDialog = false + } + } + ) + } + + ElevatedCard( + onClick = { + mutableCoordinates = coordinates + setSpoofedCoordinates() + GeoPoint(coordinates.latitude, coordinates.longitude).also { + marker.value?.position = it + mapView.value?.controller?.apply { + animateTo(it) + setZoom(16.0) + } + } + }, modifier = Modifier .fillMaxWidth() - .padding(start = 16.dp, end = 16.dp), - verticalAlignment = Alignment.CenterVertically + .padding(5.dp), ) { - Text( - text = "${coordinates.name} (${coordinates.latitude.toFloat()}, ${coordinates.longitude.toFloat()})", - fontWeight = if (spoofedCoordinates == coordinates.latitude to coordinates.longitude) FontWeight.Bold else FontWeight.Light, + Row( modifier = Modifier - .padding(8.dp) - .weight(1f) - .clickable { - spoofedCoordinates = - coordinates.latitude to coordinates.longitude - coordinatesProperty.value.setAny(spoofedCoordinates) - context.coroutineScope.launch { - context.config.writeConfig() - } - GeoPoint(coordinates.latitude, coordinates.longitude).also { - marker.value?.position = it - mapView.value?.controller?.apply { - animateTo(it) - setZoom(16.0) - } - } - }, - fontSize = 16.sp - ) - FilledIconButton(onClick = { - showDeleteDialog = true - }) { - Icon(Icons.Default.DeleteOutline, contentDescription = "Delete") + .fillMaxWidth() + .padding(4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Column( + modifier = Modifier + .padding(2.dp) + .weight(1f) + ) { + Text( + text = remember(mutableCoordinates) { mutableCoordinates.name }, + fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Light, + fontSize = 16.sp, + lineHeight = 20.sp, + overflow = TextOverflow.Ellipsis + ) + Text( + text = remember(mutableCoordinates) { "(${mutableCoordinates.latitude.toFloat()}, ${mutableCoordinates.longitude.toFloat()})" }, + fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Light, + fontSize = 12.sp, + lineHeight = 15.sp, + overflow = TextOverflow.Ellipsis + ) + } + FilledIconButton(onClick = { + showEditDialog = true + }) { + Icon(Icons.Default.Edit, contentDescription = "Delete") + } + Spacer(modifier = Modifier.width(4.dp)) + FilledIconButton(onClick = { + showDeleteDialog = true + }) { + Icon(Icons.Default.DeleteOutline, contentDescription = "Delete") + } } } } diff --git a/common/src/main/assets/lang/en_US.json b/common/src/main/assets/lang/en_US.json index 3d068e30..44654a09 100644 --- a/common/src/main/assets/lang/en_US.json +++ b/common/src/main/assets/lang/en_US.json @@ -152,6 +152,8 @@ "spoofed_coordinates_title": "Spoofed Coordinates\nLat {latitude}, Lng {longitude}", "save_coordinates_dialog_title": "Save Coordinates", "saved_name_dialog_hint": "Saved Name", + "latitude_dialog_hint": "Latitude", + "longitude_dialog_hint": "Longitude", "save_dialog_button": "Save", "choose_location_button": "Choose Location", "teleport_to_friend_button": "Teleport to Friend",