Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/src/commonMain/composeResources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,8 @@
<string name="quest_drinking_water_type_spring">Spring</string>
<string name="quest_drinking_water_type_disused">Disused drinking water object</string>

<string name="quest_doctor_type_title">What doctors are present here?</string>

<string name="quest_existence_title2">Is this still here?</string>

<string name="quest_ferry_pedestrian_title">Does this ferry route transport pedestrians?</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import de.westnordost.streetcomplete.quests.camping.AddCampPower
import de.westnordost.streetcomplete.quests.camping.AddCampShower
import de.westnordost.streetcomplete.quests.camping.AddCaravans
import de.westnordost.streetcomplete.quests.camping.AddTents
import de.westnordost.streetcomplete.quests.doctor_type.AddDoctorType
import de.westnordost.streetcomplete.quests.car_wash_type.AddCarWashType
import de.westnordost.streetcomplete.quests.charge.AddParkingCharge
import de.westnordost.streetcomplete.quests.charging_station_bicycles.AddChargingStationBicycles
Expand Down Expand Up @@ -365,6 +366,8 @@ fun questTypeRegistry(

60 to AddGeneralFee(),

200 to AddDoctorType(getCountryInfoByLocation),

61 to AddDrinkingWater(), // used by AnyFinder
62 to AddDrinkingWaterType(),

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package de.westnordost.streetcomplete.quests.doctor_type

import androidx.compose.runtime.Composable
import de.westnordost.osmfeatures.Feature
import de.westnordost.streetcomplete.data.meta.CountryInfo
import de.westnordost.streetcomplete.data.osm.geometry.ElementGeometry
import de.westnordost.streetcomplete.data.osm.mapdata.Element
import de.westnordost.streetcomplete.data.osm.mapdata.LatLon
import de.westnordost.streetcomplete.data.osm.osmquests.OsmFilterQuestType
import de.westnordost.streetcomplete.data.osm.osmquests.QuestAction
import de.westnordost.streetcomplete.data.user.achievements.EditTypeAchievement.LIFESAVER
import de.westnordost.streetcomplete.data.user.achievements.EditTypeAchievement.CITIZEN
import de.westnordost.streetcomplete.osm.Tags
import de.westnordost.streetcomplete.resources.*

class AddDoctorType(getCountryInfoByLocation: (LatLon) -> CountryInfo) : OsmFilterQuestType<List<Feature>>() {

override val elementFilter = """
nodes, ways with
(
amenity=doctors
and !healthcare:speciality
)
"""
override val changesetComment = "Survey what type of healthcare a doctor provides"
override val wikiLink = "Key:healthcare:speciality"
override val icon = Res.drawable.quest_restaurant_vegan
override val title = Res.string.quest_doctor_type_title
override val achievements = listOf(CITIZEN, LIFESAVER)

@Composable
override fun Form(on: (QuestAction<List<Feature>>) -> Unit, element: Element, geometry: ElementGeometry, countryInfo: CountryInfo) {
AddDoctorTypeForm(on, element)
}

override fun applyAnswerTo(answer: List<Feature>, tags: Tags, geometry: ElementGeometry, timestampEdited: Long) {
tags["healthcare:speciality"] = answer.joinToString(";") { it.tags["healthcare:speciality"] ?: "" }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package de.westnordost.streetcomplete.quests.doctor_type

import androidx.compose.runtime.Composable
import de.westnordost.osmfeatures.Feature
import de.westnordost.osmfeatures.FeatureDictionary
import de.westnordost.streetcomplete.data.osm.mapdata.Element
import de.westnordost.streetcomplete.data.osm.osmquests.QuestAction
import de.westnordost.streetcomplete.ui.common.quest.FeatureSelectionForm
import de.westnordost.streetcomplete.util.ktx.geometryType
import org.koin.compose.koinInject

val POPULAR_DOCTORS_FEATURE_IDS = listOf(
// ordered roughly by usage number according to taginfo
"amenity/doctors/ophthalmology",
"amenity/doctors/paediatrics",
"amenity/doctors/gynaecology",
"amenity/doctors/psychiatry",
"amenity/doctors/orthodontics",
"amenity/doctors/dermatology",
)

@Composable
fun AddDoctorTypeForm(
on: (QuestAction<List<Feature>>) -> Unit,
element: Element,
featureDictionary: FeatureDictionary = koinInject()
) {
FeatureSelectionForm(
on = on,
featureDictionary = featureDictionary,
geometryType = element.geometryType,
filterFn = { (it.tags["amenity"] == "doctors" && it.tags["healthcare:speciality"] != null) },
codesOfDefaultFeatures = POPULAR_DOCTORS_FEATURE_IDS
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ fun FeatureSearch(
filterFn: (Feature) -> Boolean = { true },
codesOfDefaultFeatures: List<String> = emptyList(),
) {
val state = rememberLazyListState()

val focusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) {
Expand All @@ -63,7 +62,7 @@ fun FeatureSearch(
languages = languages,
country = countryCode
)
}
}.filter(filterFn)
}
val features = remember(search, featureDictionary, languages, countryCode, geometryType, filterFn, defaultFeatures) {
if (search.isNotEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package de.westnordost.streetcomplete.ui.common.quest

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import de.westnordost.osmfeatures.Feature
import de.westnordost.osmfeatures.FeatureDictionary
import de.westnordost.osmfeatures.GeometryType
import de.westnordost.streetcomplete.data.osm.osmquests.Answer
import de.westnordost.streetcomplete.data.osm.osmquests.QuestAction
import de.westnordost.streetcomplete.resources.*
import de.westnordost.streetcomplete.ui.common.Button2
import de.westnordost.streetcomplete.ui.common.feature.FeatureItem
import de.westnordost.streetcomplete.ui.common.feature.FeatureSearchDialog
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource

/**
* A quest form that allows selecting of features via search or from a list of presets.
*/
@Composable
fun FeatureSelectionForm(
on: (QuestAction<List<Feature>>) -> Unit,
featureDictionary: FeatureDictionary,
modifier: Modifier = Modifier,
initialSelectedFeatures: List<Feature> = emptyList(),
geometryType: GeometryType? = null,
countryCode: String? = null,
filterFn: (Feature) -> Boolean = { true },
codesOfDefaultFeatures: List<String> = emptyList(),
) {
var selectedFeatures by remember { mutableStateOf(initialSelectedFeatures) }
var showSearch by remember { mutableStateOf(false) }

QuestForm(
on = on,
isComplete = selectedFeatures.isNotEmpty(),
onClickOk = { on(Answer(selectedFeatures)) },
modifier = modifier,
) {
Column {
selectedFeatures.forEach { feature ->
FeatureDisplay(
feature = feature,
featureDictionary = featureDictionary,
countryCode = countryCode,
onRemove = { selectedFeatures = selectedFeatures - feature },
modifier = Modifier.padding(vertical = 4.dp)
)
}

Button2(
onClick = { showSearch = true }
) {
Icon(painterResource(Res.drawable.ic_add_24), contentDescription = null)
Spacer(Modifier.size(8.dp))
Text(stringResource(Res.string.quest_presets_preset_add))
}

if (showSearch) {
FeatureSearchDialog(
onDismissRequest = { showSearch = false },
onSelectedFeature = {
selectedFeatures = selectedFeatures + it
showSearch = false
},
featureDictionary = featureDictionary,
geometryType = geometryType,
countryCode = countryCode,
// Ensure a preset cannot be selected twice
filterFn = { feature -> feature !in selectedFeatures && filterFn(feature) },
codesOfDefaultFeatures = codesOfDefaultFeatures
)
}
}
}
}

@Composable
private fun FeatureDisplay(
feature: Feature,
featureDictionary: FeatureDictionary,
countryCode: String?,
onRemove: () -> Unit,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
FeatureItem(
feature = feature,
featureDictionary = featureDictionary,
countryCode = countryCode,
modifier = Modifier.weight(1f)
)
IconButton(onClick = onRemove) {
Icon(painterResource(Res.drawable.ic_delete_24), contentDescription = null)
}
}
}