Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
cb6b31c
Add release file notifications to toolkit
pablomendezroyo Mar 4, 2025
530a4a9
join notifications in manifest
pablomendezroyo Mar 4, 2025
ef28963
Merge branch 'develop' into pablo/add-notifications-release-file
pablomendezroyo Mar 4, 2025
ec6dbc9
fix unit test
Mar 4, 2025
c44789f
use with instead of assert
Mar 5, 2025
1b3d0d8
return missing notifications
Mar 5, 2025
84ff200
move writemanifest to installer
Mar 5, 2025
222bb34
return notifications in package manifest endpoint
Mar 5, 2025
16ca504
use assert instead of with
Mar 6, 2025
7d7319a
fix import
Mar 6, 2025
c02c857
implement first approach backend calls
Mar 6, 2025
b7526ee
use node 22 and assert instead of with
Mar 6, 2025
e142802
rollback docker image
Mar 6, 2025
c09b754
rollback to assert
Mar 6, 2025
064859e
initial commit
mateumiralles Mar 6, 2025
c92b6b4
Notifications card & Install notifications pkg
mateumiralles Mar 6, 2025
5ff3e3e
Notifications Settings tab v1
mateumiralles Mar 6, 2025
849efc3
Slider component
mateumiralles Mar 6, 2025
6ae9e39
Notifications Settings tab v2
mateumiralles Mar 6, 2025
5d5b8df
code re-structured
mateumiralles Mar 6, 2025
89ffe3d
Settings components
mateumiralles Mar 6, 2025
9f6ec00
Notifications card component updated
mateumiralles Mar 6, 2025
0ddd830
Avatars in notifications list
mateumiralles Mar 6, 2025
54585f4
slider style fix
mateumiralles Mar 6, 2025
d585c12
add dappmnode custom type
Mar 7, 2025
05806cd
commit for demo
Mar 7, 2025
5526b32
et dnp instead of public
Mar 7, 2025
3a17933
fix commit for demo
Mar 7, 2025
01a2d31
add missing headers
Mar 7, 2025
c6f213a
Installer Notifications step
mateumiralles Mar 10, 2025
103cebf
step btns
mateumiralles Mar 11, 2025
91f5fbd
NotificationCard component styles
mateumiralles Mar 12, 2025
8b1af17
Avoid unecessary re-renders
mateumiralles Mar 13, 2025
8ebec15
Avoid re-rendering
mateumiralles Mar 17, 2025
1e9453d
update `gatusUpdateEndpoints` type
mateumiralles Mar 17, 2025
6bc6af4
Notifications settings components update
mateumiralles Mar 17, 2025
903551f
Installer step fixed
mateumiralles Mar 17, 2025
d3a6b36
implement reload
Mar 17, 2025
2a82114
parse condition in slider
mateumiralles Mar 17, 2025
c49cebf
deprecate unused types
Mar 17, 2025
92f80c4
set endpoints state when slider released
mateumiralles Mar 17, 2025
7958da0
triggrered label in `NotificationCard` component
mateumiralles Mar 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion packages/admin-ui/src/__mock-backend__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,12 @@ export const otherCalls: Omit<Routes, keyof typeof namedSpacedCalls> = {
dockerLatestVersion: "20.10.8"
}),
getIsConnectedToInternet: async () => false,
getCoreVersion: async () => "0.2.92"
getCoreVersion: async () => "0.2.92",
gatusGetEndpoints: async () => {
return { "geth.dnp.dappnode.eth": [] };
},
gatusUpdateEndpoints: async () => {},
gatuGetAllNotifications: async () => []
};

export const calls: Routes = {
Expand Down
39 changes: 39 additions & 0 deletions packages/admin-ui/src/components/Slider.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
.slider-container {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}

.slider-component {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 4px;
background: #c0c0c0;
border-radius: 2px;

&::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
background: var(--dappnode-color);
border-radius: 50%;
cursor: pointer;
}

&::-moz-range-thumb {
width: 16px;
height: 16px;
background: var(--dappnode-color);
border-radius: 50%;
cursor: pointer;
}
}

.slider-value {
color: #c0c0c0;
font-weight: bold;
min-width: fit-content;
}
57 changes: 57 additions & 0 deletions packages/admin-ui/src/components/Slider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { useState } from "react";
import "./Slider.scss";

interface SliderProps {
min?: number;
max?: number;
step?: number;
value?: number;
unit?: string;
onChange?: (value: number) => void;
onChangeComplete?: (value: number) => void;
}

const Slider: React.FC<SliderProps> = ({
min = 0,
max = 100,
step = 1,
value = 50,
unit = "%",
onChange,
onChangeComplete, // In order to trigger an action when the user releases the slider
}) => {
const [sliderValue, setSliderValue] = useState(value);

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = Number(e.target.value);
setSliderValue(newValue);
if (onChange) onChange(newValue);
};

const handleMouseUp = () => {
if (onChangeComplete) onChangeComplete(sliderValue);
};

const handleTouchEnd = () => {
if (onChangeComplete) onChangeComplete(sliderValue);
};

return (
<div className="slider-container">
<input
type="range"
min={min}
max={max}
step={step}
value={sliderValue}
onChange={handleChange}
onMouseUp={handleMouseUp} // For mouse support
onTouchEnd={handleTouchEnd} // For mobile support
className="slider-component"
/>
<span className="slider-value">{sliderValue} {unit && unit}</span>
</div>
);
};

export default Slider;
8 changes: 8 additions & 0 deletions packages/admin-ui/src/components/sidebar/navbarItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
MdWifi,
MdPeople
} from "react-icons/md";
import { FaRegBell } from "react-icons/fa";
import { SiEthereum } from "react-icons/si";
import { BiGitRepoForked } from "react-icons/bi";
import { GiRolledCloth } from "react-icons/gi";
Expand All @@ -31,6 +32,7 @@ import { relativePath as communityRelativePath } from "pages/community";
import { relativePath as stakersRelativePath } from "pages/stakers";
import { relativePath as rollupsRelativePath } from "pages/rollups";
import { relativePath as repositoryRelativePath } from "pages/repository";
import { relativePath as notificationsRelativePath } from "pages/notifications";

export const fundedBy: { logo: string; text: string; link: string }[] = [
{
Expand Down Expand Up @@ -116,6 +118,12 @@ export const sidenavItems: {
icon: MdSettings,
show: true
},
{
name: "Notifications",
href: notificationsRelativePath,
icon: FaRegBell,
show: true
},
{
name: "Community",
href: communityRelativePath,
Expand Down
4 changes: 3 additions & 1 deletion packages/admin-ui/src/pages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as community from "./community";
import * as stakers from "./stakers";
import * as rollups from "./rollups";
import * as repository from "./repository";
import * as notifications from "./notifications";

export const pages = {
dashboard,
Expand All @@ -23,7 +24,8 @@ export const pages = {
support,
community,
system,
repository
repository,
notifications
};

export const defaultPage = dashboard;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState, useEffect, useRef } from "react";
import { api } from "api";
import { api, useApi } from "api";
import { useDispatch } from "react-redux";
import { Routes, Route, useNavigate, useLocation, useParams, NavLink } from "react-router-dom";
import { isEmpty, throttle } from "lodash-es";
Expand Down Expand Up @@ -28,6 +28,7 @@ import { RequestedDnp, UserSettingsAllDnps } from "@dappnode/types";
import { diff } from "semver";
import Button from "components/Button";
import { pathName as systemPathName, subPaths as systemSubPaths } from "pages/system/data";
import { Notifications } from "./Steps/Notifications";

interface InstallDnpViewProps {
dnp: RequestedDnp;
Expand All @@ -54,6 +55,8 @@ const InstallDnpView: React.FC<InstallDnpViewProps> = ({ dnp, progressLogs }) =>
const dispatch = useDispatch();

const { dnpName, reqVersion, semVersion, settings, manifest, setupWizard, isInstalled, installedVersion } = dnp;
console.log("manifest", manifest);

const updateType = installedVersion && diff(installedVersion, semVersion);
const areUpdateWarnings =
manifest.warnings?.onPatchUpdate || manifest.warnings?.onMinorUpdate || manifest.warnings?.onMajorUpdate;
Expand Down Expand Up @@ -172,13 +175,20 @@ const InstallDnpView: React.FC<InstallDnpViewProps> = ({ dnp, progressLogs }) =>
}
].filter((option) => option.available);

const dnpsRequest = useApi.packagesGet();
const installedDnps = dnpsRequest.data;
const isNotificationsPkgInstalled = installedDnps?.some((dnp) => dnp.dnpName === "notifications.dnp.dappnode.eth");

const dnpNotificationEndpoints = manifest.notifications?.endpoints || [];

const disableInstallation =
!isEmpty(progressLogs) || requiresCoreUpdate || requiresDockerUpdate || packagesToBeUninstalled.length > 0;

const setupSubPath = "setup";
const permissionsSubPath = "permissions";
const warningsSubPath = "warnings";
const disclaimerSubPath = "disclaimer";
const notificationsSubPath = "notifications";
const installSubPath = "install";

const availableRoutes: {
Expand Down Expand Up @@ -228,6 +238,12 @@ const InstallDnpView: React.FC<InstallDnpViewProps> = ({ dnp, progressLogs }) =>
render: () => <Disclaimer disclaimers={disclaimers} onAccept={goNext} goBack={goBack} />,
available: disclaimers.length > 0
},
{
name: "Notifications",
subPath: notificationsSubPath,
render: () => <Notifications endpoints={dnpNotificationEndpoints || []} goBack={goBack} goNext={goNext} dnpName={dnpName}/>,
available: isNotificationsPkgInstalled && dnpNotificationEndpoints && dnpNotificationEndpoints.length > 0
},
// Placeholder for the final step in the horizontal stepper
{
name: "Install",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React, { useMemo } from "react";
import Button from "components/Button";
import { ManagePackageSection } from "pages/notifications/tabs/Settings/components/ManagePackageSection";
import { Endpoint } from "@dappnode/types";
import { useApi } from "api";

interface NotificationsProps {
endpoints: Endpoint[];
dnpName: string;
goNext: () => void;
goBack: () => void;
}

export const Notifications: React.FC<NotificationsProps> = ({ endpoints, goBack, goNext, dnpName }) => {
const endpointsCall = useApi.gatusGetEndpoints();
const pkgEndpointsData = endpointsCall.data?.[dnpName];

// Merge endpoints in order to preserve existing endpoints config when updating a package
const mergedEndpoints = useMemo(() => {
if (!pkgEndpointsData) return endpoints;

const endpointMap = new Map<string, Endpoint>();

endpoints.forEach((endpoint) => {
endpointMap.set(endpoint.name, endpoint);
});

pkgEndpointsData.forEach((retrievedEndpoint: Endpoint) => {
endpointMap.set(retrievedEndpoint.name, {
...endpointMap.get(retrievedEndpoint.name), // Preserve existing config if it exists
...retrievedEndpoint, // Overwrite with new data
});
});

return Array.from(endpointMap.values());
}, [endpoints, pkgEndpointsData]);

return (
<>
<ManagePackageSection dnpName="Enable notifications" endpoints={mergedEndpoints} />
<div className="button-group">
<Button onClick={goBack}>Back</Button>
<Button variant="dappnode" onClick={goNext}>
Next
</Button>
</div>
</>
);
};
67 changes: 67 additions & 0 deletions packages/admin-ui/src/pages/notifications/NotificationsRoot.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from "react";
import { Routes, Route, NavLink } from "react-router-dom";
import { useApi } from "api";
// Own module
import { title, subPaths } from "./data";
import { Inbox } from "./tabs/Inbox/Inbox";
import { NotificationsSettings } from "./tabs/Settings/Settings";
import { InstallNotificationsPkg } from "./tabs/InstallNotifications/InstallNotifications";
// Components
import Title from "components/Title";
import { renderResponse } from "components/SwrRender";

export const NotificationsRoot: React.FC = () => {
const availableRoutes: {
name: string;
subPath: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
component: React.ComponentType;
}[] = [
{
name: "Inbox",
subPath: subPaths.inbox,
component: Inbox
},
{
name: "Settings",
subPath: subPaths.settings,
component: NotificationsSettings
}
];

const dnpsRequest = useApi.packagesGet();

return renderResponse(dnpsRequest, ["Loading notifications"], (dnps) => {
const notificationsDnpName = "notifications.dnp.dappnode.eth";
const isNotificationsPkgInstalled = dnps?.some((dnp) => dnp.dnpName === notificationsDnpName);

return (
<>
<Title title={title} />
{!isNotificationsPkgInstalled ? (
<InstallNotificationsPkg pkgName={notificationsDnpName} />
) : (
<>
<div className="horizontal-navbar">
{availableRoutes.map((route) => (
<button key={route.subPath} className="item-container">
<NavLink to={route.subPath} className="item no-a-style" style={{ whiteSpace: "nowrap" }}>
{route.name}
</NavLink>
</button>
))}
</div>

<div className="section-spacing">
<Routes>
{availableRoutes.map((route) => (
<Route key={route.subPath} path={route.subPath} element={<route.component />} />
))}
</Routes>
</div>
</>
)}
</>
);
});
};
11 changes: 11 additions & 0 deletions packages/admin-ui/src/pages/notifications/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const relativePath = "notifications/inbox"; // default redirect to inbox
export const rootPath = "notifications/*";
export const title = "Notifications";

// Additional data

// SubPaths
export const subPaths = {
inbox: "inbox",
settings: "settings"
};
4 changes: 4 additions & 0 deletions packages/admin-ui/src/pages/notifications/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { NotificationsRoot } from "./NotificationsRoot";

export { rootPath, relativePath } from "./data";
export const RootComponent = NotificationsRoot;
Loading
Loading