LWC SuperBadge Challenge 7 boatsNearMe Error - salesforce

My component works fine and I can see it is working properly with the maps being formed in UI and markers coming up properly, however I am still get this error for the challenge :
Challenge Not yet complete... here's what's wrong:
We can't find the correct settings for the createMapMarkers() function in the component boatsNearMe JavaScript file. Make sure the component was created according to the requirements, including the right mapMarkers, title, Latitude (Geolocation__Latitude__s), Longitude (Geolocation__Longitude__s), the correct constants, stopping the loading spinner, and using the proper case-sensitivity and consistent quotation.
This is the Challenge 7 Statement as per the trailhead:
"Build the component boatsNearMe and display boats near you
Create the component boatsNearMe and show boats that are near the user, using the browser location and boat type. Display them on a map, always with the consent of the user (and only while the page is open)."
https://trailhead.salesforce.com/en/content/learn/superbadges/superbadge_lwc_specialist
Here is my boatsNearMe LWC code
import { LightningElement, wire, api } from 'lwc';
import getBoatsByLocation from '#salesforce/apex/BoatDataService.getBoatsByLocation';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
const LABEL_YOU_ARE_HERE = 'You are here!';
const ICON_STANDARD_USER = 'standard:user';
const ERROR_TITLE = 'Error loading Boats Near Me';
const ERROR_VARIANT = 'error';
export default class BoatsNearMe extends LightningElement {
#api boatTypeId;
mapMarkers = [];
isLoading = true;
isRendered = false;
latitude;
longitude;
#wire(getBoatsByLocation, { latitude: '$latitude', longitude: '$longitude', boatTypeId: '$boatTypeId' })
wiredBoatsJSON({ error, data }) {
if (data) {
this.isLoading = true;
this.createMapMarkers(JSON.parse(data));
} else if (error) {
this.dispatchEvent(
new ShowToastEvent({
title: ERROR_TITLE,
message: error.body.message,
variant: ERROR_VARIANT
})
);
// this.isLoading = false;
}
}
getLocationFromBrowser() {
navigator.geolocation.getCurrentPosition(
position => {
this.latitude = position.coords.latitude;
this.longitude = position.coords.longitude;
},
(error) => {}
);
}
createMapMarkers(boatData) {
const newMarkers = boatData.map((boat) => {
return {
location: {
Latitude: boat.Geolocation__Latitude__s,
Longitude: boat.Geolocation__Latitude__s
},
title: boat.Name,
};
});
newMarkers.unshift({
location: {
Latitude: this.latitude,
Longitude: this.longitude
},
title: LABEL_YOU_ARE_HERE,
icon: ICON_STANDARD_USER
});
this.mapMarkers = newMarkers;
this.isLoading = false;
}
renderedCallback() {
if (this.isRendered == false) {
this.getLocationFromBrowser();
}
this.isRendered = true;
}
}

I think you have Geolocation__Latitude__s twice in this part:
createMapMarkers(boatData) {
const newMarkers = boatData.map((boat) => {
return {
location: {
Latitude: boat.Geolocation__Latitude__s,
Longitude: boat.Geolocation__Latitude__s // <<-- should be Longitude
},
title: boat.Name,
};
});

Related

How can i get the name of images and use the images

In product page, I want to get all images path that are in a specific folder and send those to client side, so I can use them in client side by passing the paths to Image component of next js. I tried this when I was developing my app via running npm run dev and it was successful. Then I pushed the changes to my GitHub repository and vercel built my app again. Now, when I go to the product page, I get an error from the server. I tried some ways to fix this problem, but I couldn't fix that. For example, I tried changing my entered path in readdir, but the problem didn't fix. Here are my codes:
const getPagePhotosAndReview = async (productName) => {
const root = process.cwd();
let notFound = false;
const allDatas = await fs
.readdir(root + `/public/about-${productName}`, { encoding: "utf8" })
.then((files) => {
const allDatas = { pageImages: [], review: null };
files.forEach((value) => {
const image = value.split(".")[0];
const imageInfos = {
src: `/about-${productName}/${value}`,
alt: productName,
};
if (Number(image)) {
allDatas.pageImages.push(imageInfos);
}
});
return allDatas;
})
.catch((reason) => (notFound = true));
if (notFound) return 404;
await fs
.readFile(root + `/public/about-${productName}/review.txt`, {
encoding: "utf-8",
})
.then((value) => {
allDatas.review = value;
})
.catch((reason) => {
allDatas.review = null;
});
return allDatas;
};
export async function getServerSideProps(context) {
if (context.params.product.length > 3) {
return { notFound: true };
}
if (context.params.product.length < 3) {
const filters = {
kinds: originKinds[context.params.product[0]] || " ",
};
if (context.params.product[1]) filters.brands = context.params.product[1];
const products = getFilteredProducts(filters, true);
if (products.datas.length === 0) {
return {
notFound: true,
};
}
return {
props: {
products: { ...products },
},
};
}
if (context.params.product.length === 3) {
const filters = {
path: context.resolvedUrl,
};
const product = getFilteredProducts(filters, false);
if (product.length === 0) {
return {
notFound: true,
};
}
const splitedPath = product[0].path.split("/");
const pagePhotosAndReview = await getPagePhotosAndReview(
splitedPath[splitedPath.length - 1]
);
if (pagePhotosAndReview === 404) return { notFound: true };
product[0] = {
...product[0],
...pagePhotosAndReview,
};
product[0].addressArray = [
textOfPaths[context.params.product[0]],
textOfPaths[context.params.product[1]],
];
return {
props: {
product: product[0],
},
};
}
}
This is the base code and I tried some ways but couldn't fix the problem. So to fix this problem, I want to ask: how can I get the name of all images in a specific directory and then use those images in client side? And errors that I get: if I go to a page directly and without going to the home of the website, I get internal server error with code of 500 and when I go to a page of my website, and then I go to my product page, I get
Application error: a client-side exception has occurred (see the browser console for more information).
And I should say that I know I should remove public from paths when I want to load an image from public folder. I did it but I still get error.

I listen the data from socket.When I receive the data, I update the array like this .but state is not updated.Please help me and thanks in advance

socket?.on('addLocation', data => {
console.log('addLocation', data);
let isFound = false;
let newMarkers = markers.map(marker => {
if (marker.fleet_id == data.fleetId) {
isFound = false;
return {
...marker,
latitude: data.lat,
longitude: data.long,
};
} else {
return marker;
}
});
if (!isFound) {
newMarkers = [
...newMarkers,
{
fleet_id: data.fleetId,
latitude: data.lat,
longitude: data.long,
},
];
}
setMarker(newMarkers);
})
Assuming markers the state that setMarker is updating then this appears to be an issue of a stale enclosure over the markers state. The callback is updating the closed over state and not the current markers state value.
Use a functional state update to correctly access the previous state instead of the stale value. Array.prototype.map is meant to map one set of values to another, and shouldn't have side-effects like setting an isFound flag. Search the data first to decide if the state needs to be updated or if a new element needs to be appended.
Example:
socket?.on('addLocation', data => {
console.log('addLocation', data);
setMarker(markers => {
const isFound = markers.some(marker => marker.fleet_id == data.fleetId);
if (isFound) {
return markers.map(marker => marker.fleet_id == data.fleetId
? {
...marker,
latitude: data.lat,
longitude: data.long,
}
: marker
);
}
return [
...markers,
{
fleet_id: data.fleetId,
latitude: data.lat,
longitude: data.long,
},
];
});
});
socket?.on('addLocation', ({ fleetId, lat, long }) => {
setMarkers(existing => {
const latLng = { latitude: lat, longitude: long };
const index = existing.findIndex(m => m.fleet_id === fleetId);
if (index === -1) {
return [...existing, {
fleet_id: fleetId,
...latLng
}]
} else {
const clone = [...existing];
clone[index] = {
...clone[index],
...latLng
}
return clone;
}
});
});
There are a few things here:
You should use the callback flavour of setMarkers to ensure you have the "latest" copy when you're modifying it.
The code as written is pretty convoluted, you can simplify it a lot. isFound will always be false is also a logic issue.

React Native Maps

I'm busy developing a location-based app and making use of expo, is there anyone who knows how to track user location on the map and tell if they reached a specific destination on expo project similar to GPS.
<MapViewDirections
origin={userContext.userLocation}
destination={userContext.userLocation}
apikey={googleMapApi}
mode='DRIVING'
language='en's
strokeWidth={8}
strokeColor="#2A9D8F"
optimizeWaypoints={true}
resetOnChange={false}
precision={"low"}
/>
If you are using expo you should use watchPositionAsync from expo-location api
https://docs.expo.io/versions/latest/sdk/location/#locationwatchpositionasyncoptions-callback
You should track here the changes and then put the coordinates in the map.
Usage
async componentWillMount() {
const { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status === 'granted') {
this._getLocationAsync();
} else {
this.setState({ error: 'Locations services needed' });
}
}
componentWillUnmount() {
this.location.remove(callback...);
}
_getLocationAsync = async () => {
this.location = await Location.watchPositionAsync(
{
enableHighAccuracy: true,
distanceInterval: 1,
timeInterval: 10000
},
newLocation => {
let coords = newLocation.coords;
// this.props.getMyLocation sets my reducer state my_location
this.props.getMyLocation({
latitude: parseFloat(coords.latitude),
longitude: parseFloat(coords.longitude)
});
},
error => console.log(error)
);
return location;
};

How would I use Promises to fix this issue of synchronization in React?

I am working on a group project where we have to make a weather app. I am doing the details section, which displays values such as temperature, humidity, chance of rain, etc. from an API. I am using two different APIs in this project as OpenWeatherMap does not have some of the data I need. A different section of our app is getting the location and it is passed down to my section, where it is put into the OpenWeatherMap URL without issue.
constructor(props){
super(props);
}
componentDidMount() {
if(this.props.location) {
this.fetchWeatherData1();
this.fetchWeatherData2();
}
}
componentDidUpdate(prevProps) {
if(this.props.location != prevProps.location) {
this.fetchWeatherData1();
this.fetchWeatherData2();
}
}
fetchWeatherData1 = () => {
let urlOWM = "http://api.openweathermap.org/data/2.5/weather?q=" + this.props.location + "&units=metric&APPID=" + API_KEY_OWM;
$.ajax({
url: urlOWM,
dataType: "jsonp",
success : this.parseFirstResponse,
error : function(req, err){ console.log('API call failed ' + err); }
})
}
parseFirstResponse = (parsed_json) => {
var feelsLike = this.formatTemp(parsed_json['main']['feels_like']);
var humidity = this.formatPercentage(parsed_json['main']['humidity']);
var wind = this.formatSpeed(parsed_json['wind']['speed']);
var visib = this.formatVis(parsed_json['visibility']);
var cloud = this.formatPercentage(parsed_json['clouds']['all']);
var lat = this.formatString(parsed_json['coord']['lat']);
var long = this.formatString(parsed_json['coord']['lon']);
// set states for fields so they could be rendered later on
this.setState({
feelsLike: feelsLike,
humidity: humidity,
windSpeed: wind,
visibility: visib,
cloudCover: cloud,
latitude: lat,
longitude: long
});
}
In parseFirstResponse(), I store the latitude and longitude values for the location. I have to do this because the second API (DarkSky) URL can only take the coordinates for some reason. Here is the code below, where I am placing the state values for latitude and longitude into the URL:
fetchWeatherData2 = () => {
let urlDS = "https://api.darksky.net/forecast/" + API_KEY_DS + "/" + this.state.latitude + "," + this.state.longitude + "?exclude=minutely,hourly,daily,alerts,flags";
$.ajax({
url: urlDS,
dataType: "jsonp",
success : this.parseSecondResponse,
error : function(req, err){ console.log('API call failed ' + err); }
})
}
parseSecondResponse = (parsed_json) => {
var precipChance = parsed_json['currently']['precipProbability'];
var precipType = "";
if (precipChance == 0.0) {
precipType = "Precipitation";
}
else {
precipType = this.capitalize(parsed_json['currently']['precipType']);
}
precipChance = this.formatDecimaltoPercentage(precipChance);
var uv = parsed_json['currently']['uvIndex'];
var dew = this.formatTemp(this.fToC(parsed_json['currently']['dewPoint']));
// set states for fields so they could be rendered later on
this.setState({
precipChance: precipChance,
precipType: precipType,
uvIndex: uv,
dewPoint: dew
});
}
When I run this code and put a location in for the first time, I get an error in the console that says "Failed to load resource: the server responded with a status of 400 ()" and the URL looks like this: https://api.darksky.net/forecast/767ed401d519be925156b6c885fce737/undefined,undefined?exclude=minutely,hourly,daily,alerts,flags&callback=jQuery34106961395668750288_1585829837010&_=1585829837011
When I put a second, different location in, however, without refreshing the page, the URL and API call works without issue.
The coordinates of the location are supposed to be where the words "undefined, undefined" are. I have tried to console.log() the latitude and longitude values in my parseSecondResponse function and gotten the right values. I think this is a synchronization issue, but I'm not too sure.
Putting the fetchWeatherData() functions in the explicit ordering of 1 then 2 in my componentDidMount() function does not seem to help. I read about using Promises but I am not very familiar with React, so I am unsure how to implement them/if they will fix this issue.
I made small changes:
use fetch;
remove fetchWeatherData2 from componentDidMount;
use componentDidUpdate;
import React from 'react';
class Component extends React.Component {
constructor(props) {
super(props);
this.state = {
latitude: null,
longitude: null,
};
}
componentDidMount() {
const { location } = this.props;
if (location) {
this.fetchWeatherData1(location);
//fetchWeatherData2() your cannot call this because latitude and longitude === null
}
}
componentDidUpdate(prevProps, prevState) {
const { location: prevLocation } = prevState;
const { location: currLocation } = this.props;
const {
latitude: prevLatitude,
longitude: prevLongitude,
} = prevState;
const {
latitude: currLatitude,
longitude: currLongitude,
} = this.state;
if (prevLocation !== currLocation) {
this.fetchWeatherData1(currLocation);
}
if (prevLatitude !== currLatitude || prevLongitude !== currLongitude) {
this.fetchWeatherData2(currLatitude, currLongitude);
}
}
fetchWeatherData1(location) {
fetch(http://api.openweathermap.org/data/2.5/weather?q=${location}&units=metric&APPID=${API_KEY_OWM}`)
.then(res => res.json())
.then((data) => {
// parse you data here
this.setState(prevState => ({
latitude: data.coord.lat,
longitude: data.coord.long,
}));
})
.catch(({ message }) => {
console.error(`API call failed: ${message}`);
});
}
fetchWeatherData2(latitude, longitude) {
fetch(https://api.darksky.net/forecast/${API_KEY_DS}/${latitude},${longitude}?exclude=minutely,hourly,daily,alerts,flags`)
.then(res => res.json())
.then((data) => {
// parse you data here
})
.catch(({ message }) => {
console.error(`API call failed: ${message}`);
});
}
}

React Native graphql query with relay not working

I'm kinda lost with this issue so any help would be appreciated! So here is the query that I would like to send:
query {
viewer{
search_places(lat: "40.7127", lng: "-74.0059", searchType:"all", searchTerm:"shopping"){
edges{
node{
id,
name,
lat,
lng
}
}
}
}
}
so far so good, this query is working when I try it on GraphiQL. This query returns a PlaceConnection object. Now I tried to implement the same on React Native with Relay:
class SearchPlacesRoute extends Route {
static paramDefinitions = {
lat : { required: true },
lng : { required: true },
searchType : { required: true },
searchTerm : { required: true }
}
static queries = {
search_places: () => Relay.QL`
query {
viewer{
search_places(lat: $lat, lng: $lng, searchType: $searchType, searchTerm: $searchTerm)
}
}
`
}
static routeName = 'SearchPlacesRoute'
}
class SearchPlacesComponent extends Component {
render () {
const places = this.props.search_places;
console.log(places);
for (var index = 0; index < places.length; index++) {
var element = places[index];
console.log(element);
}
return (
<View>
<Text>email: {places[0].name}</Text>
</View>
)
}
}
SearchPlacesComponent = Relay.createContainer(SearchPlacesComponent, {
fragments: {
search_places: () => Relay.QL`
fragment on PlaceConnection {
edges
{
node
{
id,
name,
lat,
lng
}
}
}
`
}
})
<RootContainer
Component={SearchPlacesComponent}
route={new SearchPlacesRoute({lat:"40.7127", lng: "-74.0059", searchType:"all",searchTerm: "shopping"})}
renderFetched={(data) => <SearchPlaces {...this.props} {...data} />}/>
but when I try to grab the data with this I get the following error:
1. Objects must have selections (field 'search_places' returns PlaceConnection but has no selections)
_search_places40zPNn:search_places(lat:"40.7127",lng:"-7
^^^
2. Fragment F0 on PlaceConnection can't be spread inside Viewer
...F0
^^^
So I examined what's actually sent to the server:
and it seems to me that the fragment is sent as a separate query instead of a selection. Any idea why is this happening or how can I avoid this behaviour?
EDIT:
here is my final code - hope it'll be helpful for some:
https://gist.github.com/adamivancza/586c42ff8b30cdf70a30153944d6ace9
I think the problem is that your Relay Route is too deep. Instead, define your route to just query against viewer:
class SearchPlacesRoute extends Route {
static queries = {
viewer: () => Relay.QL`
query {
viewer
}
`
}
static routeName = 'SearchPlacesRoute'
}
Then, inside of your component, define it as a fragment on viewer, instead of on PlaceConnection:
SearchPlacesComponent = Relay.createContainer(SearchPlacesComponent, {
initialVariables: { lat: "..." /* TODO */ },
fragments: {
viewer: () => Relay.QL`
fragment on viewer {
search_places(lat: $lat, lng: $lng, searchType: $searchType, searchTerm: $searchTerm) {
edges
{
node
{
id,
name,
lat,
lng
}
}
}
}
`
}
})
You'll need to move the variables ($lag, $lng, etc) to be defined as part of the container, instead of being defined as part of the route. But I think that should fix your problem.

Resources