Skip to content
This repository has been archived by the owner on Nov 2, 2024. It is now read-only.

Commit

Permalink
✨ Lights control
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverlevay committed Sep 21, 2023
1 parent 62ad044 commit 1bd70d1
Show file tree
Hide file tree
Showing 7 changed files with 318 additions and 0 deletions.
2 changes: 2 additions & 0 deletions frontend/components/Home/Widgets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import EventSet from '../Calendar/UpcomingEventSet';
import Link from '../Link';
import ArticleSet from '../News/articleSet';
import DisplayMeeting from './DisplayMeeting';
import LightsController from '../LightsController';

export default function Widgets() {
const { t } = useTranslation(['homePage']);
Expand All @@ -20,6 +21,7 @@ export default function Widgets() {
<Typography variant="h4" color="secondary">{t('homePage:upcoming_events')}</Typography>
<EventSet perPage={4} small />
<Link href={routes.calendar}>{t('homePage:to_calendar')}</Link>
<LightsController />
</Stack>
<Stack width="100%" spacing={2}>
<Typography variant="h4" color="primary">{t('homePage:prev_meeting')}</Typography>
Expand Down
134 changes: 134 additions & 0 deletions frontend/components/LightsController.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import {
Alert,
Box,
Slider,
Stack,
} from '@mui/material';
import { useSession } from 'next-auth/react';
import {
useCallback, useEffect, useMemo, useState,
} from 'react';
import Wheel from '@uiw/react-color-wheel';
import { hsvaToRgba } from '@uiw/color-convert';
import debounce from 'lodash/debounce';
import { useUser } from '~/providers/UserProvider';
import PageHeader from '~/components/PageHeader';
import { useApiAccess } from '~/providers/ApiAccessProvider';

const DEFAULT_DOWN = 0;
const DEFAULT_UP = 100;

export default function LightsController() {
const { data: session } = useSession();
const { user } = useUser();
const [status, setStatus] = useState('');
const [error, setError] = useState(false);
const [hsva, setHsva] = useState({
h: 0, s: 0, v: 100, a: 1,
});
const [lightUp, setLightUp] = useState(DEFAULT_UP);
const [lightDown, setLightDown] = useState(DEFAULT_DOWN);
const [loaded, setLoaded] = useState(false);
const { hasAccess } = useApiAccess();

const onChange = useCallback(async () => {
const converted = hsvaToRgba(hsva);
const { r: red, g: green, b: blue } = converted;
const data = {
red,
green,
blue,
light_up: lightUp,
light_down: lightDown,
authToken: session?.accessToken,
};
const response = await fetch('/api/lights', {
method: 'POST',
body: JSON.stringify(data),
});
if (response.status !== 500) {
const json = await response.json();
setStatus(json.status);
setError(!json.sent);
}
}, [hsva, lightUp, lightDown, session?.accessToken]);

const debouncedOnChange = useMemo(
() => debounce(onChange, 300),
[onChange],
);

useEffect(() => {
if (loaded) {
debouncedOnChange();
} else {
setLoaded(true);
}
return debouncedOnChange.cancel;
}, [hsva, lightUp, lightDown]);

Check warning on line 68 in frontend/components/LightsController.tsx

View workflow job for this annotation

GitHub Actions / linter_check

React Hook useEffect has missing dependencies: 'debouncedOnChange' and 'loaded'. Either include them or remove the dependency array

if (!hasAccess('lights:change')) {
return null;
}

if (!user?.first_name) {
return (
<>
<PageHeader>lights</PageHeader>
<p>Du måste logga in först!</p>
</>
);
}

return (
<Stack
alignItems={{
xs: 'center',
md: 'flex-start',
}}
>
<Stack
alignItems={{
xs: 'center',
md: 'flex-start',
}}
maxWidth={225}
>
<PageHeader>över baren</PageHeader>
<Slider
value={lightUp}
onChange={(e, value) => setLightUp(value as number)}
aria-label="Default"
valueLabelDisplay="auto"
/>
<PageHeader>under baren</PageHeader>
<Stack direction="row" spacing={1}>
<Wheel color={hsva} onChange={(color) => setHsva({ ...hsva, ...color.hsva })} />
<Box sx={{
height: 200,
}}
>
<Slider
defaultValue={DEFAULT_DOWN}
onChange={(e, value) => {
setTimeout(() => {
setLightDown(value as number);
}, 100);
}}
orientation="vertical"
aria-label="Default"
valueLabelDisplay="auto"
/>
</Box>
</Stack>

<Alert
severity={error ? 'error' : 'success'}
sx={{ display: status ? 'flex' : 'none' }}
>
{status}
</Alert>
</Stack>
</Stack>
);
}
13 changes: 13 additions & 0 deletions frontend/hooks/useDidMountEffect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useEffect, useRef } from 'react';

const useDidMountEffect = (func: () => void, deps: any[]) => {
const didMount = useRef(false);

useEffect(() => {
if (didMount.current) func();
else didMount.current = true;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
};

export default useDidMountEffect;
55 changes: 55 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"scripts": {
"dev": "concurrently \"graphql-codegen --config codegen.yml --watch\" \"next dev\"",
"dev-only": "next dev",
"build": "next build",
"start": "next start",
"start1337": "next start -p 1337",
Expand All @@ -28,6 +29,8 @@
"@types/node": "^20.2.0",
"@types/react": "^17.0.3",
"@types/react-big-calendar": "^1.6.4",
"@uiw/color-convert": "^1.3.3",
"@uiw/react-color-wheel": "^1.3.3",
"chonky": "^2.3.0",
"chonky-icon-fontawesome": "^2.3.2",
"cookie": "^0.5.0",
Expand Down
98 changes: 98 additions & 0 deletions frontend/pages/api/lights.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/* eslint-disable @typescript-eslint/naming-convention */
/* eslint-disable no-console */
import { NextApiRequest, NextApiResponse } from 'next';
import {
ApolloClient, createHttpLink, InMemoryCache,
} from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import {
MeHeaderDocument, MeHeaderQuery, ApiAccessQuery, ApiAccessDocument,
} from '~/generated/graphql';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') {
res.status(404);
}
let studentId;
let name;
const {
red, blue, green, white_up, white_down, authToken,
} = JSON.parse(req.body);
if (process.env.NODE_ENV === 'development') {
studentId = 'ol1662le-s';
name = 'Oliver';
} else {
const httpLink = createHttpLink({
uri: process.env.NEXT_PUBLIC_GRAPHQL_ADDRESS,
});

const authLink = setContext((_, { headers }) => ({
headers: {
...headers,
authorization: authToken ? `Bearer ${authToken}` : '',
},
}));

const client = new ApolloClient({
ssrMode: true,
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
defaultOptions: {
query: {
errorPolicy: 'all',
},
},
});

const [accessResult, userResult] = await Promise.all([
client.query<ApiAccessQuery>({ query: ApiAccessDocument }),
client.query<MeHeaderQuery>({ query: MeHeaderDocument }),
]);

const accessData = accessResult.data;
const userData = userResult.data;

if (!accessData?.apiAccess.map((el) => el.name).includes('lights:change')) {
return res.status(500).json({ sent: false, status: 'You do not have access to change the lights' });
}

studentId = userData?.me?.student_id;
name = userData?.me?.first_name;
}
if (!studentId || !name) {
return res.status(500).json({ sent: false, status: 'Student id not found (try reloading the page)' });
}

console.log(`blajt: ${studentId} changed color to red:${red} green:${green} blue:${blue} white_up:${white_up} white_down:${white_down}`);
let sent = false;
let status;
try {
if (process.env.NODE_ENV !== 'development') {
const response = await fetch('http://blajt:8080/set_all_colors', {
method: 'POST',
body: new URLSearchParams({
red,
green,
blue,
white_down,
white_up,
}),
});
if (response.ok) {
status = 'Successfully updated lights';
sent = true;
}
} else {
throw new Error('Not sending to blajt in development');
}
} catch (e) {
console.error(e);
status = e.message;
sent = false;
}

return res.status(200).json({
sent,
status,
});
}
13 changes: 13 additions & 0 deletions frontend/pages/mojt/lights.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import genGetProps from '~/functions/genGetServerSideProps';
import { useSetPageName } from '~/providers/PageNameProvider';
import LightsController from '~/components/LightsController';

export default function BossPage() {
useSetPageName('lights');

return (
<LightsController />
);
}

export const getStaticProps = genGetProps(['fileBrowser']);

0 comments on commit 1bd70d1

Please sign in to comment.