-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Update notification to snackbar #919
Conversation
modern/src/SocketController.js
Outdated
useEffect(() => { | ||
events.forEach((event) => { | ||
setNotification({ | ||
message: `${devices[event.deviceId]?.name}: ${t(prefixString('event', `${event.type}`))}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we do this?
message: `${devices[event.deviceId]?.name}: ${t(prefixString('event', `${event.type}`))}`, | |
message: `${devices[event.deviceId]?.name}: ${t(prefixString('event', event.type))}`, |
modern/src/SocketController.js
Outdated
horizontal: 'right', | ||
}} | ||
open={notification.show} | ||
autoHideDuration={5000} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have both autoHideDuration
and setTimeout
? Can you please explain how it works.
modern/src/SocketController.js
Outdated
return null; | ||
useEffect(() => { | ||
events.forEach((event) => { | ||
setNotification({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem to support multiple events.
modern/src/SocketController.js
Outdated
message: '', | ||
show: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need two variables? Can't we just use message
?
Can you also please rename issue to something better. |
modern/package.json
Outdated
@@ -17,6 +17,7 @@ | |||
"maplibre-gl": "^1.15.0", | |||
"material-ui-dropzone": "^3.5.0", | |||
"moment": "^2.29.1", | |||
"notistack": "^1.0.10", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like adding new library just for supporting multiple notifications. Can't we just stack default ones?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can, but we basically would have to do that notistack does. There's a discussion about stacking snackbars.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say if it's too complicated or requires too much code, let's only support a single one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, this was my first approach. But since we cannot control when we will receive events from websocket we need to have some kind of list or queue for the notifications that arrive.
Otherwise, if we receive an event from websocket and just update the component state it could lead to too much re-rendering if many events arrive in short time, causing error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would there be an error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because an event can arrive and change the component state, generating a re-rendering just in the same time the component is being rendered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine. Why would cause an error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I might be mistaken:
“Too many re-renderers” is a React error that happens after you have reached an infinite render loop, typically caused by code that in a useEffect hook or the main body of the component itself unconditionally calls state setters.
I'll have to try something like the first approach but keeping in mind the multiple events concern.
Anton, your question about multiple events pointed a big problem in the implementation, so I searched and found notistack package. It uses the same Snackbar from MaterialUI and handle multiple Snackbars by enqueuing the messages and showing them later with a better control. I think this made the implementation more simple. Now, the notification appears without device name, though, because when the notification arrives we cannot guarantee that we already have loaded the devices: |
Not showing device is not acceptable. It basically makes it pointless. |
Right. We need to think in a better way to ensure that we cover multiple events notification and to have the devices when showing the notification. |
I also don't like adding dependency. Is there no way to display multiple toast as is? |
modern/src/SocketController.js
Outdated
@@ -83,7 +74,30 @@ const SocketController = () => { | |||
return null; | |||
}, [authenticated]); | |||
|
|||
return null; | |||
useEffect(() => { | |||
setNotifications(events.map((event) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to save events
in the state? Can't we just do this directly and call setNotifications
?
Also, can we please not reuse event object. It gets confusing. Create a new object for notifications.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to save events in the state? Can't we just do this directly and call setNotifications?
I tested that way, but here we don't have the devices yet:
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.devices) {
dispatch(devicesActions.update(data.devices));
}
if (data.positions) {
dispatch(positionsActions.update(data.positions));
}
if (data.events) {
// setEvents(data.events);
// if we use setNotifications here, we don't have devices yet to construct the message.
// something like that will return undefined for devices name:
setNotifications(events.map((event) => {
event.message = `${devices[event.deviceId]?.name}: ${t(prefixString('event', event.type))}`;
event.show = true;
setTimeout(() => event.show = false, 5000);
return event;
}));
}
};
Also, can we please not reuse event object. It gets confusing. Create a new object for notifications.
Do you mean to rename events
?
const [events, setEvents] = useState([]);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what you mean. In that case:
- Don't reuse the same event object. Create a new object for notification.
- Set devices as a dependency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Oh, I see now. Do you mean that:
setNotifications(events.map((event) => {
const notification = {
message: `${devices[event.deviceId]?.name}: ${t(prefixString('event', event.type))}`,
show: true,
};
setTimeout(() => notification.show = false, 5000);
return notification;
}));
- Devices as dependency will trigger multiple snackbars with the same notification every time devices were updated.
modern/src/SocketController.js
Outdated
setTimeout(() => event.show = false, 5000); | ||
return event; | ||
})); | ||
}, [events]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also depends on the devices.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This uses devices, but we don't want this to be triggered every time devices update because devices are updated frequently. If we add devices to the array the snackbars will be displayed every time new devices arrive even if a new event wasn't arrived.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, wait.. you need to clear notifications once they are dismissed. Looks like that's another thing that's missing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's right. I'll do that.
Now I used |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything else looks good.
modern/src/SocketController.js
Outdated
id: event.id, | ||
message: `${devices[event.deviceId]?.name}: ${t(prefixString('event', event.type))}`, | ||
show: true, | ||
close: () => setEvents(events.filter((e) => e.id !== event.id)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like that there's a callback in a data object. Why do we need it here? We can just call it directly in onClose
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Merged, thank you. |
Snackbar displaying device name and event.