Ionic React: How to pass data to detail page? - capacitor

The flow: ParentComponent > ImageItem > DetailPage
Each image contain its own id and data.
What I want to achive: Once the user click an image it will take him to the detail page of that image.
What I have so far is only the dynamic url that changes id when images are clicked.
Since IonImg doesnt contain routerLink option Im stuck.
const ImageItem: React.FC<Images> = ({source, id}) => {
const [imageLoadingState, setimageLoadingState] = useState<ImageLoadingState>(ImageLoadingState.init)
const history = useHistory();
useEffect(()=>{
setimageLoadingState(
//ask if props sources is provided and set loading state
source ? ImageLoadingState.loading : ImageLoadingState.error
)
},[source])
const navigate = ()=>{
history.push(`/detail/${id}`)
}
if(!source){
return null
}
return (
<IonCol size="4"className="ion-align-self-center">
<IonImg className='clickable-img'
src={source}
onLoad={()=>{setimageLoadingState(ImageLoadingState.complete)}}
onError={()=>{setimageLoadingState(ImageLoadingState.error)}}
onClick={()=>{navigate()}}/>
</IonCol>
);
};
export default ImageItem;

Related

Is there a useState concept so I can create a service which holds data that is accessed in multiple components?

I have 2 components who want to access the same data. Instead of each doing an HTTP Request independantly, I wanted to keep the items in parity. When doing react, we can easily do: const [ data, setData ] = useState(undefined) which will allow us to use data in our app and setData to change the global.
I was trying to think of how this might be doable in ReactScala, and Was thinking that there could be some overlap here since you can do something like:
useState[A]( data: A ): Pair[A, A=>A] = {
val d = data
return d, x => {
d = x
return d
}
}
or similar.
I have not seen the documentation on useState in Japgolly as much as defining the property in the component state and then using the state.copy() function to update the value.
The issue which occurred is that to me, state.copy is just 1 component, and wanted to know if there was a way to genericize.
https://github.com/japgolly/scalajs-react/blob/master/doc/HOOKS.md
Under the HOOKS file linked above, the top example shows how useState is translated. I will add it below in case the file is changed or deleted:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
const [fruit, setFruit] = useState("banana");
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
<p>Your favourite fruit is a {fruit}!</p>
</div>
);
}
Compared to:
import japgolly.scalajs.react._
import japgolly.scalajs.react.vdom.html_<^._
import org.scalajs.dom.document
object Example {
val Component = ScalaFnComponent.withHooks[Unit]
.useState(0)
.useEffectBy((props, count) => Callback {
document.title = s"You clicked ${count.value} times"
})
.useState("banana")
.render((props, count, fruit) =>
<.div(
<.p(s"You clicked ${count.value} times"),
<.button(
^.onClick --> count.modState(_ + 1),
"Click me"
),
<.p(s"Your favourite fruit is a ${fruit.value}!")
)
)
}

Add interactivity to POIs from different tilesets

I'm working on Mapbox Studio Tutorial and practicing adding interactivity on POIs on map.
https://docs.mapbox.com/help/tutorials/add-points-pt-3/
map.on('click', (event) => {
// If the user clicked on one of your markers, get its information.
const features = map.queryRenderedFeatures(event.point, {
layers: ['layer1',"layer2","layer3"]
});
if (!features.length) {
return;
}
const feature = features[0];
/*
Create a popup, specify its options
and properties, and add it to the map.
*/
const popup = new mapboxgl.Popup({ offset: [0, -15] })
.setLngLat(feature.geometry.coordinates)
.setHTML(
`<h3>${feature.properties.title}</h3><p>${feature.properties.description}</p>`
)
.addTo(map);
});
The error I get is that the title of POIs from layer2 and layer3 is shown as "undefined" while layer1's title can be shown when clicking it on the map.
I think "undefined" comes because the title is not stored in the feature property but have no clear idea how to do that correctly.
I tried some codes I got from the internet such as below:
if (features.length > 0) {
// Loop feature and concatenate property as HTML strings
let propertiesHTML = '';
features.forEach(feature => {
Object.entries(feature.properties).forEach(([key, value]) => {
propertiesHTML += `<p><strong>${key}:</strong> ${value}</p>`;
});
});
// Create and add a popup
const popup = new mapboxgl.Popup({ offset: [0, -15] })
.setLngLat(features[0].geometry.coordinates)
.setHTML(propertiesHTML)
.addTo(map);
With code at least map is shown, but there is no interactive popup shown when I click it.

Draft.js Mention Plugin is not working after rendering contentState to the editor

I am using mentions with the draft.js (like this #yourname) and sending to the database to save and fetching it to render on the web page but things are not working as expected.
On Saving to the database ->
const contentState = editorState.getCurrentContent();
const currentStateData = convertToRaw(contentState);
const richStringifyValue = JSON.stringify(currentStateData);
// sending richStringifyValue to save in Mongo DB
On Fetch and set in editor ->
const [editorState, setEditorState] = React.useState(() => EditorState.createEmpty());
const parsedData = JSON.parse(post.contentStyled);
const fromRawData = convertFromRaw(parsedData );
EditorState.createWithContent(fromRawData);
// here is the view rendered part -
<Editor
readOnly={true}
editorState={editorState}
/>
But after setting in editor (after the data fetched from API) my mentions (#... #... #...) lost the CSS. What should we do?
On Using Edit ->
On fetch and setting again in Editor ->
I don't know why is that happening, please help to resolve this issue!
You should do the following:
const [editorState, setEditorState] = React.useState(() => {
const parsedData = JSON.parse(post.contentStyled);
const fromRawData = convertFromRaw(parsedData );
return EditorState.createWithContent(fromRawData);
});
// ...
<Editor
readOnly={true}
editorState={editorState}
/>
If you override the editorState with a new created state you are removing all the decorators which were added by the plugins.
Dominic's answer made me realize what was going on with the decorators, but his approach didn't work for me either.
What I ended up doing instead was to avoid mounting the editor altogether until I have the data inside the EditorState:
const [editorState, setEditorState] = React.useState(null);
useEffect(() => {
const parsedData = JSON.parse(post.contentStyled);
const fromRawData = convertFromRaw(parsedData );
setEditorState(() => EditorState.createWithContent(fromRawData));
}, []);
editorState && <Editor readOnly={true} editorState={editorState}/>
This way you insert your persisted data into the state before instantiating the component. And afterwards any other plugin adding decorators will work as intended.

How to add properties to leaflet-geoman layer when using the toolbar

I need to add custom props to my created polys. To do so currently when the user select in the toolbar the polygon and create a shape, on the create event I convert it to json remove it from the map add the custom props to the json and reload the newly created layer.
this.map.on('pm:create', e => {
const id = getUID();
const leafId = e.layer._leaflet_id;
const featureGroup = L.featureGroup().addLayer(e.layer);
this.map.eachLayer(layer => {
if (layer._leaflet_id === leafId) {
this.map.removeLayer(layer);
}
});
const data = featureGroup.toGeoJSON();
data.features[0].properties = {
id,
name: `Zone ${id}`
};
this.zoneService.add({id, data: JSON.stringify(data)})
.pipe(
switchMap((res) => this.zoneService.getAll().pipe(this.addToMap(this.map)))
).subscribe();
});
This is working but I feel I am not doing something right here. Adding removing Adding, there must be a better way. Thanks for any help

Draft.js insert image at dropped position

I'm trying to drop image from outside of draft-js editor but it's always inserted at last position of the cursor/selection in editor (or at end if cursor/selection not set).
This is my wrap around draft-js-drag-n-drop-plugin
const droppableBlockDndPlugin = {
...blockDndPlugin,
handleDrop: (
selection,
dataTransfer,
isInternal,
{getEditorState, setEditorState}
) => {
const editorState = getEditorState();
const raw = dataTransfer.data.getData('text');
const data = raw ? raw.split(IMAGE_BLOCK_TYPE_SEPARATOR) : [];
if (data.length > 1 && data[0] === IMAGE_BLOCK_TYPE_PURE) {
const url = data[1];
if (url) {
const newState = imagePlugin.addImage(editorState, url);
setEditorState(newState);
}
}
return blockDndPlugin.handleDrop(selection, dataTransfer, isInternal, {
getEditorState,
setEditorState
});
}
};
Basically I'm just doing extra logic before base handleDrop occurs where I insert image using imagePlugin.addImage. Is there way to drop image to dragged position?
Actually it was quite obvious solution - you should just use passed selection and create new state with it and then add image to that new state:
const newState = imagePlugin.addImage(EditorState.forceSelection(editorState, selection), url);
setEditorState(newState);