Skip to content

Commit

Permalink
started implementing showing non-system specific notifications (#2381)
Browse files Browse the repository at this point in the history
  • Loading branch information
foxriver76 authored Feb 23, 2024
1 parent a85e1a1 commit 061fa47
Show file tree
Hide file tree
Showing 16 changed files with 583 additions and 31 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ The icons may not be reused in other projects without the proper flaticon licens
-->
## Changelog
### **WORK IN PROGRESS**
* (foxriver76) new dialog for notifications introduced for non-system notifications (system notifications are still on hosts tab)
* (foxriver76) the new `licenseInformation` icon now changes color correctly with the theme

### 6.14.1 (2024-02-20)
Expand Down
114 changes: 90 additions & 24 deletions packages/admin/src/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
CloudSync as SyncIcon,
SyncDisabled as SyncIconDisabled,
Close as CancelIcon,
Notifications as NotificationsIcon,
} from '@mui/icons-material';

import { AdminConnection as Connection, PROGRESS } from '@iobroker/socket-client';
Expand All @@ -54,6 +55,7 @@ import {
IconExpert,
ToggleThemeMenu,
} from '@iobroker/adapter-react-v5';
import NotificationsDialog from '@/dialogs/NotificationsDialog';
import Utils from './components/Utils'; // adapter-react-v5/Components/Utils';

import CommandDialog from './dialogs/CommandDialog';
Expand Down Expand Up @@ -498,6 +500,12 @@ class App extends Router {
triggerAdapterUpdate: 0,

updating: false, // js controller updating
/** If the notifications dialog should be shown */
notificationsDialog: false,
/** Notifications, excluding the system ones */
notifications: {},
/** Number of new notifications */
noNotifications: 0,
};
this.logsWorker = null;
this.instancesWorker = null;
Expand Down Expand Up @@ -921,6 +929,7 @@ class App extends Router {
this.adaptersWorker.registerRepositoryHandler(this.repoChangeHandler);
this.adaptersWorker.registerHandler(this.adaptersChangeHandler);
this.hostsWorker.registerHandler(this.updateHosts);
this.hostsWorker.registerNotificationHandler(notifications => this.handleNewNotifications(notifications));

this.subscribeOnHostsStatus();

Expand Down Expand Up @@ -986,16 +995,17 @@ class App extends Router {
);

setTimeout(
() =>
this.hostsWorker
.getNotifications(newState.currentHost)
.then(notifications =>
this.showAdaptersWarning(
notifications,
this.socket,
newState.currentHost,
)),
3000,
async () => {
const notifications = await this.hostsWorker.getNotifications(newState.currentHost);
this.showAdaptersWarning(
notifications,
this.socket,
newState.currentHost,
);

this.handleNewNotifications(notifications);
},
3_000,
);
})
.catch(error => {
Expand Down Expand Up @@ -1250,6 +1260,25 @@ class App extends Router {
});
};

/**
* Render the notifications dialog
* @return {React.ReactNode}
*/
renderNotificationsDialog() {
if (!this.state.notificationsDialog) {
return null;
}

return <NotificationsDialog
notifications={this.state.notifications?.notifications || {}}
instances={this.state.notifications?.instances || {}}
onClose={() => this.setState({ notificationsDialog: false })}
ackCallback={(host, name) => this.socket.clearNotifications(host, name)}
dateFormat={this.state.systemConfig.common.dateFormat}
themeType={this.state.themeType}
/>;
}

renderHostWarningDialog() {
if (!this.state.showHostWarning) {
return null;
Expand All @@ -1260,20 +1289,47 @@ class App extends Router {
messages={this.state.showHostWarning.result.system.categories}
dateFormat={this.state.systemConfig.common.dateFormat}
themeType={this.state.themeType}
themeName={this.state.themeName}
ackCallback={name => this.socket.clearNotifications(this.state.showHostWarning.host, name)}
onClose={() => this.setState({ showHostWarning: null })}
/>;
}

/**
* Called when notifications detected, updates the notifications indicator
*
* @param {Record<string, any>} notifications
*/
async handleNewNotifications(notifications) {
// console.log(`new notifications: ${JSON.stringify(notifications)}`);
let noNotifications = 0;

for (const hostDetails of Object.values(notifications)) {
for (const [scope, scopeDetails] of Object.entries(hostDetails.result)) {
if (scope === 'system') {
continue;
}

for (const categoryDetails of Object.values(scopeDetails.categories)) {
for (const instanceDetails of Object.values(categoryDetails.instances)) {
noNotifications += instanceDetails.messages.length;
}
}
}
}

const instances = await this.instancesWorker.getInstances();

this.setState({ noNotifications, notifications: { notifications, instances } });
}

showAdaptersWarning = (notifications, socket, host) => {
if (!notifications || !notifications[host] || !notifications[host].result) {
return Promise.resolve();
}

const result = notifications[host].result;

if (result && result.system && Object.keys(result.system.categories).length) {
if (result?.system && Object.keys(result.system.categories).length) {
return this.instancesWorker.getInstances()
.then(instances => this.setState({ showHostWarning: { host, instances, result } }));
}
Expand Down Expand Up @@ -2330,6 +2386,19 @@ class App extends Router {
)}
>
<Toolbar>
<IconButton
size="large"
onClick={() => this.setState({ notificationsDialog: true })}
>
<Tooltip title={I18n.t('Notifications')}>
<Badge
badgeContent={this.state.noNotifications}
color="secondary"
>
<NotificationsIcon />
</Badge>
</Tooltip>
</IconButton>
<IconButton
size="large"
edge="start"
Expand Down Expand Up @@ -2475,24 +2544,20 @@ class App extends Router {
currentHostName: hostName,
currentHost: host,
},
() => {
async () => {
this.logsWorkerChanged(host);
(window._localStorage || window.localStorage).setItem(
'App.currentHost',
host,
);

this.readRepoAndInstalledInfo(host, this.state.hosts).then(
() =>
// read notifications from host
this.hostsWorker
.getNotifications(host)
.then(notifications =>
this.showAdaptersWarning(
notifications,
this.socket,
host,
)),
await this.readRepoAndInstalledInfo(host, this.state.hosts);
// read notifications from host
const notifications = await this.hostsWorker.getNotifications(host);
this.showAdaptersWarning(
notifications,
this.socket,
host,
);
},
);
Expand Down Expand Up @@ -2642,6 +2707,7 @@ class App extends Router {
{this.renderSlowConnectionWarning()}
{this.renderNewsDialog()}
{this.renderHostWarningDialog()}
{this.renderNotificationsDialog()}
{!this.state.connected && !this.state.redirectCountDown && !this.state.updating ? (
<Connecting />
) : null}
Expand Down
10 changes: 5 additions & 5 deletions packages/admin/src/src/Workers/HostsWorker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,22 +174,22 @@ class HostsWorker {

this.notificationTimer = setTimeout(host_ => {
this.notificationTimer = null;
this.notificationPromises[host_] = this._getNotificationsFromHots(host_, true);
this.notificationPromises[host_] = this._getNotificationsFromHosts(host_, true);

this.notificationPromises[host_].then(notifications =>
this.notificationsHandlers.forEach(cb => cb(notifications)));
}, 300, host);
}
};

_getNotificationsFromHots(hostId, update) {
_getNotificationsFromHosts(hostId, update) {
if (!update && this.notificationPromises[hostId]) {
return this.notificationPromises[hostId];
}

this.notificationPromises[hostId] = this.socket.getState(`${hostId}.alive`)
.then(state => {
if (state && state.val) {
if (state?.val) {
return this.socket.getNotifications(hostId)
.then(notifications => ({ [hostId]: notifications }))
.catch(e => {
Expand All @@ -205,12 +205,12 @@ class HostsWorker {

getNotifications(hostId, update) {
if (hostId) {
return this._getNotificationsFromHots(hostId, update);
return this._getNotificationsFromHosts(hostId, update);
}
return this.socket.getCompactHosts(update)
.then(hosts => {
const promises = hosts
.map(host => this._getNotificationsFromHots(host._id, update));
.map(host => this._getNotificationsFromHosts(host._id, update));

return Promise.all(promises)
.then(pResults => {
Expand Down
4 changes: 2 additions & 2 deletions packages/admin/src/src/dialogs/ExpertModeDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import {
FormControlLabel,
Checkbox,
Grid,
DialogTitle, IconButton, Typography
DialogTitle, IconButton, Typography,
} from '@mui/material';
import {
Build as BuildIcon,
Check,
Check as CheckIcon,
} from '@mui/icons-material';

import { I18n, IconExpert } from '@iobroker/adapter-react-v5';
Expand Down
Loading

0 comments on commit 061fa47

Please sign in to comment.