Skip to content

Commit

Permalink
Added refresh, a notification, fixes and stale_bot
Browse files Browse the repository at this point in the history
  • Loading branch information
IzakJoubert committed Sep 19, 2023
1 parent 1ff9b8c commit 775e640
Show file tree
Hide file tree
Showing 12 changed files with 264 additions and 112 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,8 @@
],
"rules": {
"react/prop-types": "off"
},
"globals": {
"NodeJS": true
}
}
22 changes: 22 additions & 0 deletions .github/workflows/stale_bot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Close inactive issues
on:
schedule:
- cron: "0 0 * * *"

jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
days-before-issue-stale: 30
days-before-issue-close: 14
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
days-before-pr-stale: -1
days-before-pr-close: -1
repo-token: ${{ secrets.GITHUB_TOKEN }}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ packages
out
yarn-error.log
.yarn*
*.log
*.log
.vscode/launch.json
1 change: 0 additions & 1 deletion electron/bridge.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { contextBridge, ipcRenderer } from 'electron';
import ISettings from '../src/interfaces/ISettings';
import { Dayjs } from 'dayjs';

const api = {
getSettings: async () => {
Expand Down
5 changes: 2 additions & 3 deletions electron/main.handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import ISettings from '../src/interfaces/ISettings'
import { CronJob } from 'cron'
import { shutdown } from './utils/shutdown'
import logger from './utils/logger'
import { Dayjs } from 'dayjs'

const store = new Store()
const store = new Store();

export const registerHandlers = (ipcMain: IpcMain, tray: Tray | null) => {
let job: CronJob
Expand All @@ -29,7 +28,7 @@ export const registerHandlers = (ipcMain: IpcMain, tray: Tray | null) => {
ipcMain.handle('saveSettings', (event: any, settings: ISettings) => {
store.set('settings', settings);
app.setLoginItemSettings({
openAtLogin: settings.runAtStartup
openAtLogin: settings.runAtStartup,
});
});

Expand Down
5 changes: 5 additions & 0 deletions electron/utils/shutdown.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import cp from 'child_process';
import { Notification } from 'electron';

export function shutdown() {
const cmdarguments = ['shutdown'];
Expand All @@ -15,5 +16,9 @@ export function shutdown() {
}

const executeCmd = (cmd: string[]) => {
new Notification({
title: 'ShedShield shutdown',
body: 'ShedShield will now shut down your PC'
}).show();
cp.exec(cmd.join(' '));
}
128 changes: 111 additions & 17 deletions src/Pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,85 @@
import { FC, useEffect, useState } from 'react';
import { Settings } from '@mui/icons-material';
import { Box, Button, IconButton, Stack, Typography } from '@mui/material';
import { Settings, Refresh } from '@mui/icons-material';
import { Box, Button, IconButton, Skeleton, Stack, Tooltip, Typography } from '@mui/material';
import { Link } from 'react-router-dom';
import IHomePage from '../interfaces/IHomePage';
import AreaComponent from '../components/AreaComponent';
import dayjs, { Dayjs } from 'dayjs';
import ISettings from '../interfaces/ISettings';
import IAreaInfo from '../interfaces/IAreaInfo';

const HomePage: FC<IHomePage> = () => {
const [settings, setSettings] = useState<ISettings | undefined>();
const [firstTimeSetup, setFirstTimeSetup] = useState(false);
const [areaData, setAreaData] = useState<IAreaInfo[]>([]);
const [refreshing, setRefreshing] = useState(false);
const intervals: NodeJS.Timeout[] = [];

const updateInfo = (id: string): Promise<IAreaInfo> => {
const updatedArea: IAreaInfo = {
id,
events: null,
info: null,
schedule: null,
error: "",
};

return window.Main.getAreaInfo(id)
.then((response: IAreaInfo) => {
updatedArea.events = response.events;
updatedArea.info = response.info;
updatedArea.schedule = response.schedule;
return updatedArea;
})
.catch((error: Error) => {
updatedArea.error = error.message;
window.Main.error(error);
return updatedArea;
});
};

const updateAreaInfo = (id: string, blah?: string ) => {
updateInfo(id).then((updatedArea) => {

setAreaData(prevAreaData => {
const index = prevAreaData.findIndex(arrArea => arrArea.id === id);

if (index > -1) {
const newData = [...prevAreaData];
newData[index] = updatedArea;
return newData;
} else {
return [...prevAreaData, updatedArea];
}
});
});
}

useEffect(() => {
window.Main.getSettings()
.then((res: ISettings) => {
setSettings(res);
setFirstTimeSetup(!res || res?.espAreas.length <= 0 || !res?.apiKey)
res?.espAreas.forEach(area => {
updateAreaInfo(area.id);
});
})
.catch((error: Error) => window.Main.error(error));
}, []);

useEffect(() => {
areaData?.forEach((area, index) => {
const intervalId = setInterval(() => {
updateAreaInfo(area.id);
}, (settings?.updates || 60) * 60000);
intervals.push(intervalId);
});

return () => {
intervals?.forEach((intervalId) => clearInterval(intervalId));
}
}, [areaData]);

const [currentEvent, setCurrentEvent] = useState<Dayjs>();

const handleNextEvent = (date: Dayjs) => {
Expand All @@ -28,9 +89,42 @@ const HomePage: FC<IHomePage> = () => {
}
};

const renderNextEvent = () => {
if (currentEvent) {
return (
<Typography variant="h6" gutterBottom>
{`Shutting down at ${currentEvent?.format('HH:mm, ddd D MMM')}.`}
</Typography>
)
} else if (!refreshing && areaData.length > 0) {
return (
<Typography variant="h6" gutterBottom>
No upcoming loadshedding for today! 🎉
</Typography>
)
} else {
return (
<Typography variant="h6" gutterBottom>
Loading...
</Typography>
)
}
};

return (
<Box>
<Box position="absolute" top={0} right={0}>
<Tooltip title={<>Get latest data from ESP<br />P.S. It uses quota</>}>
<IconButton aria-label="refresh" onClick={() => {
setRefreshing(true);
settings?.espAreas.forEach(area => {
updateAreaInfo(area.id, "testing");
});
setRefreshing(false);
}}>
<Refresh />
</IconButton>
</Tooltip>
<IconButton aria-label="settings" component={Link} to="/settings">
<Settings />
</IconButton>
Expand Down Expand Up @@ -73,21 +167,21 @@ const HomePage: FC<IHomePage> = () => {
)}
{!firstTimeSetup && (
<Stack alignItems="center" p={2}>
{currentEvent && (
<Typography variant="h6" gutterBottom>
{`Shutting down at ${currentEvent?.format('HH:mm, ddd D MMM')}.`}
</Typography>
)}
{settings?.espAreas.map(area => {
return (
<AreaComponent
key={area.id}
id={area.id}
interval={settings?.interval}
handleNextEvent={handleNextEvent}
/>
);
})}
{renderNextEvent()}
{(!refreshing && areaData.length > 0) ? areaData.map(area => {
return (
<AreaComponent
key={area.id}
areaInfo={area}
interval={settings?.interval || 15}
handleNextEvent={handleNextEvent}
/>
);
}) :
<Skeleton>
<AreaComponent />
</Skeleton>
}
</Stack>
)}
</Box>
Expand Down
36 changes: 33 additions & 3 deletions src/Pages/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import { Link, useNavigate } from 'react-router-dom';
import ISettingsPage from '../interfaces/ISettingsPage';

const intervals = [15, 10, 5, 2];
const updates = [30, 60, 120, 300];
const updateText = ['30 minutes', '1 hour', '2 hours', '3 hours'];

const SettingsPage: FC<ISettingsPage> = ({ themeState, setThemeState }) => {
useEffect(() => {
Expand All @@ -43,6 +45,7 @@ const SettingsPage: FC<ISettingsPage> = ({ themeState, setThemeState }) => {
const checkedAreas = settings?.espAreas.map(area => area.id);
setAreas(checkedAreas);
setInterval(settings?.interval || 15);
setUpdate(settings?.updates || 120);
setRunAtStartup(settings?.runAtStartup);
})
.catch((error: Error) => {
Expand All @@ -65,6 +68,7 @@ const SettingsPage: FC<ISettingsPage> = ({ themeState, setThemeState }) => {
const [areasError, setAreasError] = useState("");

const [interval, setInterval] = useState(15);
const [update, setUpdate] = useState(120);

const [runAtStartup, setRunAtStartup] = useState(false);

Expand Down Expand Up @@ -136,6 +140,7 @@ const SettingsPage: FC<ISettingsPage> = ({ themeState, setThemeState }) => {
espAreas: checkedAreas,
theme: themeState,
interval,
updates: update,
runAtStartup,
} as ISettings)
.then(() => {
Expand Down Expand Up @@ -268,8 +273,8 @@ const SettingsPage: FC<ISettingsPage> = ({ themeState, setThemeState }) => {
{areasError && <Alert severity="error">{areasError}</Alert>}
</Box>
<Box py={2}>
<FormControl>
<InputLabel id="interval-label">Interval</InputLabel>
<FormControl sx={{ mr: '5%', width: '45%' }}>
<InputLabel id="interval-label">Duration</InputLabel>
<Select
labelId="interval-label"
value={interval}
Expand All @@ -289,7 +294,32 @@ const SettingsPage: FC<ISettingsPage> = ({ themeState, setThemeState }) => {
})}
</Select>
<FormHelperText>
How early should your PC be switched off?
{`How long before loadshedding \n should your PC be switched off?`}
</FormHelperText>
</FormControl>
<FormControl sx={{ ml: '5%', width: '45%' }}>
<InputLabel id="update-label">Update Interval</InputLabel>
<Select
labelId="update-label"
value={update}
label="Update Interval"
notched
onChange={(event: SelectChangeEvent<number>) => {
setUpdate(+event.target.value);
}}
>
{updates &&
updates.map((item, index) => {
return (
<MenuItem
key={item}
value={item}
>{`${updateText[index]}`}</MenuItem>
);
})}
</Select>
<FormHelperText>
How often should ShedShield check for updates? P.S. This will affect your ESP quota.
</FormHelperText>
</FormControl>
</Box>
Expand Down
Loading

0 comments on commit 775e640

Please sign in to comment.