Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Filter pins on map to match filters/search for storied sites #4459

3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased
## Added
- Filter pins on map to match filters/search for storied sites [#4458](https://github.com/rokwire/illinois-app/issues/4458)
- Add search to Bottomsheet [#4456](https://github.com/rokwire/illinois-app/issues/4456)

## [6.0.56] - 2024-11-04
### Fixed
- Bug fixes on the stories sites bottomsheet [#4454](https://github.com/rokwire/illinois-app/issues/4454)
Expand All @@ -31,7 +33,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added user_auth_type standard analytics property [#4419](https://github.com/rokwire/illinois-app/issues/4419).


## [6.0.51] - 2024-10-15
### Fixed
- Fixed Poll UI issues [#4414](https://github.com/rokwire/illinois-app/issues/4414).
Expand Down
2 changes: 1 addition & 1 deletion assets/strings.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1153,7 +1153,7 @@

"panel.explore.storied_sites.title": "Storied Sites",
"panel.explore.storied_sites.load.error": "Failed to load stored site details",
"panel.explore.storied_sites.check_in.try_again": "Check-in failed. Please try again.",
"panel.explore.storied_sites.check_in.try_again": "Check-in failed. Please sign in and try again.",
"panel.explore.storied_sites.check_in.failed": "Check-in failed due to an error.",
"panel.explore.storied_sites.one.day": "You can only check in once per day.",
"panel.explore.storied_sites.failed.clear": "Failed to clear check-in date. Please try again.",
Expand Down
2 changes: 1 addition & 1 deletion assets/strings.es.json
Original file line number Diff line number Diff line change
Expand Up @@ -1152,7 +1152,7 @@

"panel.explore.storied_sites.title": "Storied Sites",
"panel.explore.storied_sites.load.error": "Failed to load stored site details",
"panel.explore.storied_sites.check_in.try_again": "Check-in failed. Please try again.",
"panel.explore.storied_sites.check_in.try_again": "Check-in failed. Please sign in and try again.",
"panel.explore.storied_sites.check_in.failed": "Check-in failed due to an error.",
"panel.explore.storied_sites.one.day": "You can only check in once per day.",
"panel.explore.storied_sites.failed.clear": "Failed to clear check-in date. Please try again.",
Expand Down
2 changes: 1 addition & 1 deletion assets/strings.zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -1152,7 +1152,7 @@

"panel.explore.storied_sites.title": "Storied Sites",
"panel.explore.storied_sites.load.error": "Failed to load stored site details",
"panel.explore.storied_sites.check_in.try_again": "Check-in failed. Please try again.",
"panel.explore.storied_sites.check_in.try_again": "Check-in failed. Please sign in and try again.",
"panel.explore.storied_sites.check_in.failed": "Check-in failed due to an error.",
"panel.explore.storied_sites.one.day": "You can only check in once per day.",
"panel.explore.storied_sites.failed.clear": "Failed to clear check-in date. Please try again.",
Expand Down
2 changes: 2 additions & 0 deletions lib/model/Analytics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class AnalyticsFeature {
static const AnalyticsFeature MapMyLocations = AnalyticsFeature("Map: My Locations", priority: -1);
static const AnalyticsFeature MapMentalHealth = AnalyticsFeature("Map: Therapist", priority: -1);
static const AnalyticsFeature MapStateFarm = AnalyticsFeature("Map: State Farm", priority: -1);
static const AnalyticsFeature StoriedSites = AnalyticsFeature("Map: Storied Sites", priority: -1);

static const AnalyticsFeature Events = AnalyticsFeature("Events", key: "Event");
static const AnalyticsFeature EventsAll = AnalyticsFeature("Events: All", priority: -1);
Expand Down Expand Up @@ -139,6 +140,7 @@ class AnalyticsFeature {
MapMyLocations,
MapMentalHealth,
MapStateFarm,
StoriedSites,

Events,
EventsAll,
Expand Down
126 changes: 103 additions & 23 deletions lib/ui/explore/ExploreMapPanel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class ExploreMapPanel extends StatefulWidget with AnalyticsInfo {
ExploreMapType.MyLocations: AnalyticsFeature.MapMyLocations,
ExploreMapType.MentalHealth: AnalyticsFeature.MapMentalHealth,
ExploreMapType.StateFarmWayfinding: AnalyticsFeature.MapStateFarm,
ExploreMapType.StoriedSites: AnalyticsFeature.StoriedSites,
};

final Map<String, dynamic> params = <String, dynamic>{};
Expand Down Expand Up @@ -195,6 +196,7 @@ class _ExploreMapPanelState extends State<ExploreMapPanel>
bool _filtersDropdownVisible = false;

List<Explore>? _explores;
List<Explore>? _filteredExplores;
bool _exploreProgress = false;
Future<List<Explore>?>? _exploreTask;

Expand Down Expand Up @@ -228,6 +230,7 @@ class _ExploreMapPanelState extends State<ExploreMapPanel>
Future<Set<Marker>?>? _buildMarkersTask;
Explore? _pinnedMapExplore;
dynamic _selectedMapExplore;
Explore? _selectedStoriedSiteExplore;
AnimationController? _mapExploreBarAnimationController;

String? _loadingMapStopIdRoutes;
Expand Down Expand Up @@ -375,7 +378,7 @@ class _ExploreMapPanelState extends State<ExploreMapPanel>
}
else if (name == Storage.notifySettingChanged) {
if (param == Storage.debugMapThresholdDistanceKey) {
_buildMapContentData(_explores, pinnedExplore: _pinnedMapExplore, updateCamera: false, zoom: _lastMarkersUpdateZoom, showProgress: true);
_buildMapContentData(_filteredExplores ?? _explores, pinnedExplore: _pinnedMapExplore, updateCamera: false, zoom: _lastMarkersUpdateZoom, showProgress: true);
}
else if (param == Storage.debugMapShowLevelsKey) {
setStateIfMounted(() { });
Expand Down Expand Up @@ -467,10 +470,9 @@ class _ExploreMapPanelState extends State<ExploreMapPanel>
ExploreStoriedSightsBottomSheet(
key: _storiedSightsKey,
places: _explores?.whereType<Place>().toList() ?? [],
onPlaceSelected: (places_model.Place place) {
_centerMapOnExplore(place, zoom: false);
_selectMapExplore(place);
},
onPlaceSelected: _onStoriedSitesPlaceSelected,
onFilteredPlacesChanged: _onStoriedSitesFilteredPlacesChanged,
onBackPressed: _onStoriedSitesBackPressed,
),
_buildExploreTypesDropDownContainer(),
]),
Expand Down Expand Up @@ -501,7 +503,8 @@ class _ExploreMapPanelState extends State<ExploreMapPanel>
Widget _buildMapContent() {
return Stack(children: [
_buildMapView(),
_buildMapExploreBar(),
if (_selectedMapType != ExploreMapType.StoriedSites)
_buildMapExploreBar(),
Visibility(visible: _markersProgress, child:
Positioned.fill(child:
Center(child:
Expand Down Expand Up @@ -579,7 +582,7 @@ class _ExploreMapPanelState extends State<ExploreMapPanel>
_lastMarkersUpdateZoom = value;
}
else if ((_lastMarkersUpdateZoom! - value).abs() > _groupMarkersUpdateThresoldDelta) {
_buildMapContentData(_explores, pinnedExplore: _pinnedMapExplore, updateCamera: false, zoom: value, showProgress: true);
_buildMapContentData(_filteredExplores ?? _explores, pinnedExplore: _pinnedMapExplore, updateCamera: false, zoom: value, showProgress: true);
}
});
}
Expand Down Expand Up @@ -614,19 +617,78 @@ class _ExploreMapPanelState extends State<ExploreMapPanel>

void _onTapMarker(dynamic origin) {
if (_selectedMapType == ExploreMapType.StoriedSites) {
if (origin is Place) {
_storiedSightsKey.currentState?.selectPlace(origin);
// _centerMapOnExplore(origin);
} else if (origin is List<Explore>) {
List<places_model.Place> places = origin.cast<places_model.Place>();
_storiedSightsKey.currentState?.selectPlaces(places);
_centerMapOnExplore(places);
}
} else {
_selectStoriedSiteExplore(origin);
}
else {
_selectMapExplore(origin);
}
}

void _onStoriedSitesPlaceSelected(places_model.Place place) {
_centerMapOnExplore(place, zoom: false);
_selectMapExplore(place);
}

void _onStoriedSitesFilteredPlacesChanged(List<places_model.Place>? filteredExplores) {
if (!DeepCollectionEquality().equals(_filteredExplores, filteredExplores)) {
_mapController?.getZoomLevel().then((double value) {
_filteredExplores = (filteredExplores != null) ? List.from(filteredExplores) : null;
_buildMapContentData(_filteredExplores ?? _explores, pinnedExplore: _pinnedMapExplore, updateCamera: false, showProgress: true, zoom: value);
});
}
}

void _onStoriedSitesBackPressed() {
_selectStoriedSiteExplore(null);
}

void _selectStoriedSiteExplore(dynamic explore) {
if (explore is Place) {
_storiedSightsKey.currentState?.selectPlace(explore);
}
else if (explore is List<Explore>) {
List<places_model.Place> places = explore.cast<places_model.Place>();
_storiedSightsKey.currentState?.selectPlaces(places);
_centerMapOnExplore(places);
}

_updateSelectedStoriedSiteMarker(explore is Explore ? explore : null);

_logAnalyticsSelect(explore);
}

Future<void> _updateSelectedStoriedSiteMarker(Explore? selectedStoriedSiteExplore) async {
Set<Marker>? targetMarkers = (_targetMarkers != null) ? Set<Marker>.from(_targetMarkers!) : null;
if ((targetMarkers != null) && (_selectedStoriedSiteExplore != selectedStoriedSiteExplore)) {
ImageConfiguration imageConfiguration = createLocalImageConfiguration(context);

if (_selectedStoriedSiteExplore != null) {
Marker? selectedStoriedSiteMarker = targetMarkers.exploreMarker(_selectedStoriedSiteExplore);
Marker? selectedStoriedSiteMarkerUpd = await _createExploreMarker(_selectedStoriedSiteExplore, imageConfiguration: imageConfiguration);
if ((selectedStoriedSiteMarker != null) && (selectedStoriedSiteMarkerUpd != null)) {
targetMarkers.remove(selectedStoriedSiteMarker);
targetMarkers.add(selectedStoriedSiteMarkerUpd);
}
}

_selectedStoriedSiteExplore = selectedStoriedSiteExplore;

if (_selectedStoriedSiteExplore != null) {
Marker? selectedStoriedSiteMarker = targetMarkers.exploreMarker(_selectedStoriedSiteExplore);
Marker? selectedStoriedSiteMarkerUpd = await _createExploreMarker(_selectedStoriedSiteExplore, imageConfiguration: imageConfiguration, markerColor: Styles().colors.fillColorSecondary);
if ((selectedStoriedSiteMarker != null) && (selectedStoriedSiteMarkerUpd != null)) {
targetMarkers.remove(selectedStoriedSiteMarker);
targetMarkers.add(selectedStoriedSiteMarkerUpd);
}
}
}

if (!DeepCollectionEquality().equals(_targetMarkers, targetMarkers)) {
setStateIfMounted((){
_targetMarkers = targetMarkers;
});
}
}

void _centerMapOnExplore(dynamic explore, {bool zoom = true}) async {
LatLng? targetPosition;
Expand Down Expand Up @@ -831,6 +893,7 @@ class _ExploreMapPanelState extends State<ExploreMapPanel>
else {
_pinMapExplore(null);
}

_logAnalyticsSelect(explore);
}

Expand Down Expand Up @@ -1880,16 +1943,19 @@ class _ExploreMapPanelState extends State<ExploreMapPanel>
if (mounted && (exploreTask == _exploreTask)) {
setState(() {
_explores = explores;
_filteredExplores = null;
_exploreTask = null;
_exploreProgress = false;
_mapKey = UniqueKey(); // force map rebuild
});
_selectMapExplore(null);
_selectStoriedSiteExplore(null);
_displayContentPopups();
}
}
}
}


Future<void> _refreshExplores() async {
Future<List<Explore>?> exploreTask = _loadExplores();
List<Explore>? explores = await (_exploreTask = exploreTask);
Expand All @@ -1899,6 +1965,7 @@ class _ExploreMapPanelState extends State<ExploreMapPanel>
if (mounted && (exploreTask == _exploreTask)) {
setState(() {
_explores = explores;
_filteredExplores = null;
_exploreProgress = false;
_exploreTask = null;
});
Expand Down Expand Up @@ -2083,6 +2150,7 @@ class _ExploreMapPanelState extends State<ExploreMapPanel>
if (mounted) {
setState(() {
_explores = explores;
_filteredExplores = null;
});
}
});
Expand Down Expand Up @@ -2149,7 +2217,7 @@ class _ExploreMapPanelState extends State<ExploreMapPanel>
thresoldDistance = 0;
exploreMarkerGroups = (explores != null) ? <dynamic>{ ExploreMap.validFromList(explores) } : null;
}

if (!DeepCollectionEquality().equals(_exploreMarkerGroups, exploreMarkerGroups)) {
Future<Set<Marker>> buildMarkersTask = _buildMarkers(context, exploreGroups: exploreMarkerGroups, pinnedExplore: pinnedExplore);
_buildMarkersTask = buildMarkersTask;
Expand Down Expand Up @@ -2327,7 +2395,7 @@ class _ExploreMapPanelState extends State<ExploreMapPanel>
}


Future<Marker?> _createExploreMarker(Explore? explore, {required ImageConfiguration imageConfiguration}) async {
Future<Marker?> _createExploreMarker(Explore? explore, {required ImageConfiguration imageConfiguration, Color? markerColor }) async {
LatLng? markerPosition = explore?.exploreLocation?.exploreLocationMapCoordinate;
if (markerPosition != null) {
BitmapDescriptor? markerIcon;
Expand All @@ -2339,19 +2407,19 @@ class _ExploreMapPanelState extends State<ExploreMapPanel>
markerAnchor = Offset(0.5, 0.5);
}
else {
Color? exploreColor = explore?.mapMarkerColor;
Color? exploreColor = markerColor ?? explore?.mapMarkerColor;
markerIcon = (exploreColor != null) ? BitmapDescriptor.defaultMarkerWithHue(ColorUtils.hueFromColor(exploreColor).toDouble()) : BitmapDescriptor.defaultMarker;
markerAnchor = Offset(0.5, 1);
}
return Marker(
markerId: MarkerId("${markerPosition.latitude.toStringAsFixed(6)}:${markerPosition.latitude.toStringAsFixed(6)}"),
markerId: MarkerId("${markerPosition.latitude.toStringAsFixed(6)}:${markerPosition.longitude.toStringAsFixed(6)}"),
position: markerPosition,
icon: markerIcon,
anchor: markerAnchor,
consumeTapEvents: true,
onTap: () => _onTapMarker(explore),
infoWindow: InfoWindow(
title: explore?.mapMarkerTitle,
title: explore?.mapMarkerTitle,
snippet: explore?.mapMarkerSnippet,
anchor: markerAnchor)
);
Expand Down Expand Up @@ -2515,4 +2583,16 @@ String? _exploreMapTypeToString(ExploreMapType? value) {
class ExploreMapSearchEventsParam {
final String searchText;
ExploreMapSearchEventsParam(this.searchText);
}
}

extension _MapMarkersSet on Set<Marker> {

Marker? exploreMarker(Explore? explore) {
LatLng? markerPosition = explore?.exploreLocation?.exploreLocationMapCoordinate;
String? markerId = (markerPosition != null) ? "${markerPosition.latitude.toStringAsFixed(6)}:${markerPosition.longitude.toStringAsFixed(6)}" : null;
return (markerId != null) ? markerById(MarkerId(markerId)) : null;
}

Marker? markerById(MarkerId markerId) =>
firstWhereOrNull((marker) => marker.markerId == markerId);
}
Loading