diff --git a/src/main/consts/notifications.ts b/src/main/consts/notifications.ts new file mode 100644 index 000000000..6ffd1718e --- /dev/null +++ b/src/main/consts/notifications.ts @@ -0,0 +1,24 @@ +export const NOTIFICATIONS = Object.freeze({ + info: { + SYNC_COMMITTEE: { + title: 'Scheduled for Sync Commitee Duty', + description: 'Validator', + }, + SLASH_REWARD: { + title: 'Reward for slashing another validator', + description: 'Validator', + }, + }, + completed: { + CLIENT_UPDATED: { + title: 'Client successfuly updated', + description: 'consensus client', + }, + }, + download: { + UPDATE_AVAILABLE: { + title: 'Client successfuly updated', + description: 'consensus client', + }, + }, +}); diff --git a/src/main/dialog.ts b/src/main/dialog.ts index 731d539a9..742914d66 100644 --- a/src/main/dialog.ts +++ b/src/main/dialog.ts @@ -1,6 +1,7 @@ +/* eslint-disable consistent-return */ import { BrowserWindow, dialog } from 'electron'; -import { NodeId } from '../common/node'; +import Node, { NodeId } from '../common/node'; import { getNodesDirPath, CheckStorageDetails, @@ -11,6 +12,12 @@ import logger from './logger'; import { getMainWindow } from './main'; import { getNode, updateNode } from './state/nodes'; +export const updateNodeDataDir = async (node: Node, newDataDir: string) => { + node.runtime.dataDir = newDataDir; + node.config.configValuesMap.dataDir = newDataDir; + updateNode(node); +}; + export const openDialogForNodeDataDir = async (nodeId: NodeId) => { const node = getNode(nodeId); if (!node) { @@ -43,12 +50,11 @@ export const openDialogForNodeDataDir = async (nodeId: NodeId) => { } if (result.filePaths) { if (result.filePaths.length > 0) { - const newDataDir = result.filePaths[0]; - node.runtime.dataDir = newDataDir; - node.config.configValuesMap.dataDir = newDataDir; - updateNode(node); + return result.filePaths[0]; } } + // eslint-disable-next-line no-useless-return + return; }; export const openDialogForStorageLocation = async (): Promise< @@ -75,7 +81,6 @@ export const openDialogForStorageLocation = async (): Promise< if (result.filePaths.length > 0) { const folderPath = result.filePaths[0]; const freeStorageGBs = await getSystemFreeDiskSpace(folderPath); - // eslint-disable-next-line consistent-return return { folderPath, freeStorageGBs, diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 269b50f82..97687eb67 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -33,6 +33,7 @@ import installDocker from './docker/install'; import { openDialogForNodeDataDir, openDialogForStorageLocation, + updateNodeDataDir, } from './dialog'; import { getNodeLibrary } from './state/nodeLibrary'; import { @@ -113,6 +114,12 @@ export const initialize = () => { ipcMain.handle('stopNode', (_event, nodeId: NodeId) => { return stopNode(nodeId); }); + ipcMain.handle( + 'updateNodeDataDir', + (_event, node: Node, newDataDir: string) => { + return updateNodeDataDir(node, newDataDir); + } + ); ipcMain.handle('openDialogForNodeDataDir', (_event, nodeId: NodeId) => { return openDialogForNodeDataDir(nodeId); }); diff --git a/src/main/main.ts b/src/main/main.ts index 8638202eb..f6fe6fa8b 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -88,11 +88,15 @@ const createWindow = async () => { mainWindow = new BrowserWindow({ titleBarOverlay: true, titleBarStyle: 'hiddenInset', + show: false, + minWidth: 980, + minHeight: 480, width: 1200, height: 820, icon: getAssetPath('icon.png'), webPreferences: { + enableBlinkFeatures: 'CSSColorSchemeUARendering', nodeIntegration: true, preload: app.isPackaged ? path.join(__dirname, 'preload.js') diff --git a/src/main/notifications.ts b/src/main/notifications.ts new file mode 100644 index 000000000..0b3ef5c55 --- /dev/null +++ b/src/main/notifications.ts @@ -0,0 +1,57 @@ +// import { Notification } from 'electron'; + +export type NotificationType = { + unread: boolean; + status: string; + title: string; + description: string; + timestamp: number; +}; + +export const displayNotification = () => { + // new Notification({ + // title, + // body, + // }).show(); +}; + +export const getNotifications = () => { + if (!localStorage.getItem('notifications')) { + localStorage.setItem('notifications', JSON.stringify([])); + } + return JSON.parse(localStorage.getItem('notifications') || ''); +}; + +export const removeNotifications = () => { + localStorage.setItem('notifications', JSON.stringify([])); + return []; +}; + +export const addNotification = (notification: NotificationType) => { + const notifications = getNotifications(); + notifications.push(notification); + + localStorage.setItem('notifications', JSON.stringify(notifications)); +}; + +export const addNotifications = (notifications: NotificationType[]) => { + notifications.forEach((notification: NotificationType) => { + addNotification(notification); + }); +}; + +export const markAllAsRead = () => { + const notifications = JSON.parse(localStorage.getItem('notifications') || ''); + + notifications.forEach((notification: NotificationType) => { + notification.unread = false; + }); + + localStorage.setItem('notifications', JSON.stringify(notifications)); + + return notifications; +}; + +export const initialize = async () => { + console.log('test initialize'); +}; diff --git a/src/main/preload.ts b/src/main/preload.ts index 35ccb884d..46fde57b3 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -77,6 +77,8 @@ contextBridge.exposeInMainWorld('electron', { stopNode: (nodeId: NodeId) => { ipcRenderer.invoke('stopNode', nodeId); }, + updateNodeDataDir: (node: Node, newDataDir: string) => + ipcRenderer.invoke('updateNodeDataDir', node, newDataDir), openDialogForNodeDataDir: (nodeId: NodeId) => ipcRenderer.invoke('openDialogForNodeDataDir', nodeId), openDialogForStorageLocation: () => diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 51c5ab6be..15be8439a 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -1,35 +1,60 @@ -import { useEffect, useState } from 'react'; -import { MemoryRouter as Router, Routes, Route } from 'react-router-dom'; +import React, { useEffect, useState } from 'react'; +import { MemoryRouter, Routes, Route, Outlet } from 'react-router-dom'; import * as Sentry from '@sentry/electron/renderer'; +import NotificationsWrapper from './Presentational/Notifications/NotificationsWrapper'; +import SystemMonitor from './Presentational/SystemMonitor/SystemMonitor'; import './Generics/redesign/globalStyle.css'; import './reset.css'; import { useAppDispatch } from './state/hooks'; import { initialize as initializeIpcListeners } from './ipc'; -import NodeScreen from './NodeScreen'; +import NodeScreen from './Presentational/NodeScreen/NodeScreen'; import DataRefresher from './DataRefresher'; import electron from './electronGlobal'; import { SidebarWrapper } from './Presentational/SidebarWrapper/SidebarWrapper'; -import NNSplash from './Presentational/NNSplashScreen/NNSplashScreen'; -import { dragWindowContainer } from './app.css'; +import LogsWrapper from './Generics/redesign/LogMessage/LogsWrapper'; +import NodeSetup from './Presentational/NodeSetup/NodeSetup'; +import { + dragWindowContainer, + homeContainer, + contentContainer, +} from './app.css'; import ThemeManager from './ThemeManager'; -import { Modal } from './Generics/redesign/Modal/Modal'; -import AddNodeStepper from './Presentational/AddNodeStepper/AddNodeStepper'; +import ModalManager from './Presentational/ModalManager/ModalManager'; Sentry.init({ dsn: electron.SENTRY_DSN, debug: true, }); -const MainScreen = () => { +const WindowContainer = ({ children }: { children: React.ReactNode }) => { + return ( + <> +
+ {children} + > + ); +}; + +const Main = () => { + return ( +Execution client
-Consensus client
-Recommended execution client
+Recommended consensus client
+{tGeneric('DataLocation')}
-{tGeneric('DataLocation')}
+{t('ChooseYourNetwork')}
-{t('DockerInstallComplete')}
- )} - {/* Docker is installed but not running */} - {isDockerInstalled && !isDockerRunning && ( - <> -{t('DockerInstallComplete')}
+ )} + {/* Docker is installed but not running */} + {isDockerInstalled && !isDockerRunning && ( + <> +Node start command
{nodeStartCommand && ( -{nodeStartCommand}
-+ Node start command (must save changes to take effect) +
+{nodeStartCommand}
++ All settings and data for this node will be removed from your computer. +
++ Optionally you can choose to keep the current chain data to shorten + future sync times for NiceNode or alternative uses. +
+Hey, user! Take a look at this!
> -}; +// Primary.args = { +// screen: { +// route: 'addNode', +// type: 'modal', +// }, +// }; diff --git a/src/stories/Generic/NotificationIcon.stories.tsx b/src/stories/Generic/NotificationIcon.stories.tsx new file mode 100644 index 000000000..2b625058f --- /dev/null +++ b/src/stories/Generic/NotificationIcon.stories.tsx @@ -0,0 +1,21 @@ +import { ComponentStory, ComponentMeta } from '@storybook/react'; + +import { NotificationIcon } from '../../renderer/Generics/redesign/NotificationIcon/NotificationIcon'; + +export default { + title: 'Generic/NotificationIcon', + component: NotificationIcon, + argTypes: { + backgroundColor: { control: 'color' }, + }, +} as ComponentMeta