Add map/list toggle on the start screen; fix text contrast over map

The "found N places" overlay text (and the checking/no-places states)
used the theme's default light text color, which washed out against
the now-real, mostly light-colored OSM map tiles. Fixed with a fixed
dark color (Mocha.Crust) for text overlaid on the map.

Added a toggle button (top bar, visible once places are found) that
switches the start screen between the map and a plain list preview of
the found places (reusing PlaceCard in its neutral/UPCOMING state) —
lets the user see what's "ready to read" before committing to Start.

Verified: testDebugUnitTest passes, assembleDebug produces a working
APK, sent to Telegram.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
vrubelroman 2026-07-09 22:12:35 +00:00
parent 37edc96d50
commit 28f310b56f
3 changed files with 46 additions and 2 deletions

View file

@ -3,12 +3,14 @@ package com.guidecity.app.ui.map
import android.Manifest import android.Manifest
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -16,12 +18,15 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.List
import androidx.compose.material.icons.filled.Map
import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -40,6 +45,12 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import com.guidecity.app.R import com.guidecity.app.R
import com.guidecity.app.map.CityMapView import com.guidecity.app.map.CityMapView
import com.guidecity.app.theme.Mocha
import com.guidecity.app.ui.guide.CardState
import com.guidecity.app.ui.guide.PlaceCard
/** Overlay text sits on top of the (now real, often light-colored) map tiles, so it needs a fixed dark color rather than the theme's default light-on-dark text. */
private val MapOverlayTextColor = Mocha.Crust
@Composable @Composable
fun MapScreen( fun MapScreen(
@ -53,6 +64,7 @@ fun MapScreen(
val resolvedLocation by viewModel.resolvedLocation.collectAsState() val resolvedLocation by viewModel.resolvedLocation.collectAsState()
val nearbyPlaces by viewModel.nearbyPlaces.collectAsState() val nearbyPlaces by viewModel.nearbyPlaces.collectAsState()
var query by remember { mutableStateOf("") } var query by remember { mutableStateOf("") }
var showList by remember { mutableStateOf(false) }
val permissionLauncher = rememberLauncherForActivityResult( val permissionLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestMultiplePermissions(), contract = ActivityResultContracts.RequestMultiplePermissions(),
@ -69,6 +81,16 @@ fun MapScreen(
TopAppBar( TopAppBar(
title = { Text(stringResource(R.string.app_name)) }, title = { Text(stringResource(R.string.app_name)) },
actions = { actions = {
if (uiState is MapUiState.ReadyToStart) {
IconButton(onClick = { showList = !showList }) {
Icon(
imageVector = if (showList) Icons.Filled.Map else Icons.Filled.List,
contentDescription = stringResource(
if (showList) R.string.map_show_map else R.string.map_show_list,
),
)
}
}
IconButton(onClick = onOpenFavorites) { IconButton(onClick = onOpenFavorites) {
Icon(Icons.Filled.Favorite, contentDescription = stringResource(R.string.favorites_title)) Icon(Icons.Filled.Favorite, contentDescription = stringResource(R.string.favorites_title))
} }
@ -151,7 +173,7 @@ fun MapScreen(
CircularProgressIndicator() CircularProgressIndicator()
if (state == MapUiState.CheckingNearby) { if (state == MapUiState.CheckingNearby) {
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
Text(stringResource(R.string.map_checking_nearby)) Text(stringResource(R.string.map_checking_nearby), color = MapOverlayTextColor)
} }
} }
} }
@ -164,11 +186,28 @@ fun MapScreen(
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Text(stringResource(R.string.map_no_places_nearby)) Text(stringResource(R.string.map_no_places_nearby), color = MapOverlayTextColor)
} }
} }
is MapUiState.ReadyToStart -> { is MapUiState.ReadyToStart -> {
if (showList) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background),
contentPadding = PaddingValues(bottom = 140.dp),
) {
items(nearbyPlaces, key = { it.id }) { place ->
PlaceCard(
place = place,
state = CardState.UPCOMING,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
)
}
}
}
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -178,6 +217,7 @@ fun MapScreen(
) { ) {
Text( Text(
text = stringResource(R.string.map_places_found, state.placesFound, state.radiusM), text = stringResource(R.string.map_places_found, state.placesFound, state.radiusM),
color = MapOverlayTextColor,
) )
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Button(onClick = onLocationResolved) { Button(onClick = onLocationResolved) {

View file

@ -19,6 +19,8 @@
<string name="map_places_found">Найдено мест поблизости: %1$d (в радиусе %2$d м)</string> <string name="map_places_found">Найдено мест поблизости: %1$d (в радиусе %2$d м)</string>
<string name="map_start">Начать</string> <string name="map_start">Начать</string>
<string name="map_no_places_nearby">В радиусе 10 км ничего не нашлось.</string> <string name="map_no_places_nearby">В радиусе 10 км ничего не нашлось.</string>
<string name="map_show_list">Показать список</string>
<string name="map_show_map">Показать карту</string>
<string name="guide_no_places_nearby">Поблизости пока не найдено интересных мест.</string> <string name="guide_no_places_nearby">Поблизости пока не найдено интересных мест.</string>
<string name="guide_places_count">Мест поблизости: %1$d</string> <string name="guide_places_count">Мест поблизости: %1$d</string>

View file

@ -19,6 +19,8 @@
<string name="map_places_found">Found %1$d place(s) nearby (within %2$d m)</string> <string name="map_places_found">Found %1$d place(s) nearby (within %2$d m)</string>
<string name="map_start">Start</string> <string name="map_start">Start</string>
<string name="map_no_places_nearby">Nothing found within 10 km of here.</string> <string name="map_no_places_nearby">Nothing found within 10 km of here.</string>
<string name="map_show_list">Show list</string>
<string name="map_show_map">Show map</string>
<string name="guide_no_places_nearby">No places found nearby yet.</string> <string name="guide_no_places_nearby">No places found nearby yet.</string>
<string name="guide_places_count">%1$d place(s) nearby</string> <string name="guide_places_count">%1$d place(s) nearby</string>