diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt index 342e495a1d..9e4ebe9d63 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt @@ -37,6 +37,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd import androidx.compose.foundation.verticalScroll @@ -106,6 +107,8 @@ import com.vitorpamplona.amethyst.ui.note.creators.expiration.ExpirationDatePick import com.vitorpamplona.amethyst.ui.note.creators.invoice.AddLnInvoiceButton import com.vitorpamplona.amethyst.ui.note.creators.invoice.InvoiceRequest import com.vitorpamplona.amethyst.ui.note.creators.location.AddGeoHashButton +import com.vitorpamplona.amethyst.ui.note.creators.location.DisplayLocationInTitle +import com.vitorpamplona.amethyst.ui.note.creators.location.GeohashLocationPickerDialog import com.vitorpamplona.amethyst.ui.note.creators.location.LocationAsHash import com.vitorpamplona.amethyst.ui.note.creators.messagefield.MessageField import com.vitorpamplona.amethyst.ui.note.creators.notify.Notifying @@ -527,19 +530,7 @@ private fun NewPostScreenBody( } if (postViewModel.wantsToAddGeoHash) { - Row( - verticalAlignment = CenterVertically, - modifier = Modifier.padding(vertical = Size10dp, horizontal = Size10dp), - ) { - LocationAsHash(postViewModel) { - SettingsRow( - R.string.geohash_exclusive, - R.string.geohash_exclusive_explainer, - ) { - Switch(postViewModel.wantsExclusiveGeoPost, onCheckedChange = { postViewModel.wantsExclusiveGeoPost = it }) - } - } - } + GeoHashPostSection(postViewModel) } if (postViewModel.wantsForwardZapTo) { @@ -881,6 +872,85 @@ private fun BottomRowActions( } } +/** + * The composer's location section. Defaults to the device GPS flow ([LocationAsHash]), + * but a "pick on map" action opens the shared [GeohashLocationPickerDialog] and stores + * the chosen geohash in [ShortNotePostViewModel.pickedGeoHash], which then overrides GPS + * at build time. Picking a place also skips the GPS permission prompt. + */ +@Composable +private fun GeoHashPostSection(postViewModel: ShortNotePostViewModel) { + var showPicker by remember { mutableStateOf(false) } + val picked = postViewModel.pickedGeoHash + + Column( + modifier = Modifier.fillMaxWidth().padding(vertical = Size10dp, horizontal = Size10dp), + ) { + if (picked != null) { + // A map-picked place: show it, and let the user clear back to GPS. + Row(verticalAlignment = CenterVertically, modifier = Modifier.fillMaxWidth()) { + Icon( + symbol = MaterialSymbols.LocationOn, + contentDescription = null, + modifier = Modifier.size(20.dp), + tint = MaterialTheme.colorScheme.primary, + ) + Text( + text = stringRes(R.string.geohash_title), + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.W500, + modifier = Modifier.padding(start = 10.dp), + ) + DisplayLocationInTitle(geohash = picked) + Spacer(modifier = Modifier.weight(1f)) + IconButton(onClick = { postViewModel.pickedGeoHash = null }) { + Icon( + symbol = MaterialSymbols.Close, + contentDescription = stringRes(R.string.remove_location), + modifier = Modifier.size(20.dp), + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + HorizontalDivider() + SettingsRow(R.string.geohash_exclusive, R.string.geohash_exclusive_explainer) { + Switch(postViewModel.wantsExclusiveGeoPost, onCheckedChange = { postViewModel.wantsExclusiveGeoPost = it }) + } + } else { + // GPS mode (unchanged): current device location + the exclusive-post switch. + LocationAsHash(postViewModel) { + SettingsRow(R.string.geohash_exclusive, R.string.geohash_exclusive_explainer) { + Switch(postViewModel.wantsExclusiveGeoPost, onCheckedChange = { postViewModel.wantsExclusiveGeoPost = it }) + } + } + } + + TextButton(onClick = { showPicker = true }, modifier = Modifier.padding(top = 4.dp)) { + Icon( + symbol = MaterialSymbols.LocationOn, + contentDescription = null, + modifier = Modifier.size(18.dp), + tint = MaterialTheme.colorScheme.primary, + ) + Text( + text = stringRes(if (picked != null) R.string.location_change_place else R.string.location_pick_on_map), + modifier = Modifier.padding(start = 6.dp), + ) + } + } + + if (showPicker) { + GeohashLocationPickerDialog( + initialGeohash = picked, + onDismiss = { showPicker = false }, + onConfirm = { cell -> + postViewModel.pickedGeoHash = cell + showPicker = false + }, + ) + } +} + @SuppressLint("ViewModelConstructorInComposable") @Preview @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt index ea22e857a2..43e64bbae2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostViewModel.kt @@ -329,6 +329,12 @@ open class ShortNotePostViewModel : // GeoHash var wantsToAddGeoHash by mutableStateOf(false) var location: StateFlow? = null + + /** + * A geohash the user picked on the map, which overrides the live GPS location at + * build time. Null means "use my current GPS location" (the default behavior). + */ + var pickedGeoHash by mutableStateOf(null) var wantsExclusiveGeoPost by mutableStateOf(false) // ZapRaiser @@ -753,6 +759,7 @@ open class ShortNotePostViewModel : val geohash = draftEvent.getGeoHash() wantsToAddGeoHash = geohash != null + pickedGeoHash = geohash if (geohash != null) { wantsExclusiveGeoPost = draftEvent.kind == CommentEvent.KIND } @@ -858,6 +865,7 @@ open class ShortNotePostViewModel : val geohash = draftEvent.getGeoHash() wantsToAddGeoHash = geohash != null + pickedGeoHash = geohash if (geohash != null) { wantsExclusiveGeoPost = draftEvent.kind == CommentEvent.KIND } @@ -931,6 +939,7 @@ open class ShortNotePostViewModel : val geohash = draftEvent.getGeoHash() wantsToAddGeoHash = geohash != null + pickedGeoHash = geohash if (geohash != null) { wantsExclusiveGeoPost = draftEvent.kind == CommentEvent.KIND } @@ -1236,7 +1245,13 @@ open class ShortNotePostViewModel : val zapReceiver = if (wantsForwardZapTo) forwardZapTo.value.toZapSplitSetup() else null - val geoHash = if (wantsToAddGeoHash) (location?.value as? LocationState.LocationResult.Success)?.geoHash?.toString() else null + val geoHash = + if (wantsToAddGeoHash) { + // A map-picked geohash wins over the live GPS fix. + pickedGeoHash ?: (location?.value as? LocationState.LocationResult.Success)?.geoHash?.toString() + } else { + null + } val localZapRaiserAmount = if (wantsZapRaiser) zapRaiserAmount.value else null val emojis = account.emoji.findEmojiTags(tagger.message) @@ -1563,6 +1578,7 @@ open class ShortNotePostViewModel : wantsToMarkAsSensitive = false contentWarningDescription = "" wantsToAddGeoHash = false + pickedGeoHash = null wantsExclusiveGeoPost = false wantsSecretEmoji = false wantsAnonymousPost = false diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index b488c55f4d..d6a11e6af2 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -2149,6 +2149,8 @@ Adds a Geohash of your location to the post. The public will know you are within 5km (3mi) of the current location Teleport ✈ Teleport here + Pick a place on the map + Change place on the map Location-exclusive Post Only followers of the location will see it. Your general followers won\'t see it.