Skip to content

Commit

Permalink
refactor: converted files to typescript
Browse files Browse the repository at this point in the history
- Removed vite-plugin-eslint because it is not maintained anymore and
its typings are outdated
- Added types to all files and made sure the project fully type checks
  • Loading branch information
stickyPiston committed Jul 21, 2024
1 parent db0a7c8 commit 08718a1
Show file tree
Hide file tree
Showing 33 changed files with 530 additions and 519 deletions.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
<script type="module" src="/src/main.tsx"></script>
<script src="src/snow/snow.js" type="module"></script>
</body>

Expand Down
68 changes: 1 addition & 67 deletions package-lock.json

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

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"typescript": "^5.5.3",
"vite": "^4.4.5",
"vite-plugin-eslint": "^1.8.1"
"vite": "^4.4.5"
}
}
30 changes: 15 additions & 15 deletions src/App.jsx → src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {useEffect} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import { useEffect } from 'react';

import {
Activities,
Expand All @@ -10,9 +9,9 @@ import {
Commits
} from './components';

import {nextState} from './store';
import {contentful} from './store/api';
import {resetQuotes} from './store/state';
import { nextState, useAppDispatch, useAppSelector } from './store';
import { contentful } from './store/api';
import { actions, StateMachineState } from './store/state';

const LOGO = import.meta.env.VITE_LOGO;

Expand All @@ -31,14 +30,15 @@ export default function App() {
}

function StateMachine() {
const dispatch = useDispatch();
const dispatch = useAppDispatch();

// Preload the quotes and initialise the store with the quote indices
useEffect(() => {
const result = dispatch(contentful.endpoints.quotes.initiate());

result.then(({ data: quotes }) => {
dispatch(resetQuotes(quotes.length));
result.then(({ data: quotes, isSuccess }) => {
if (isSuccess)
dispatch(actions.resetQuotes(quotes.length));
});

return result.unsubscribe;
Expand All @@ -48,24 +48,24 @@ function StateMachine() {
useEffect(() => {
const interval = setInterval(() => {
dispatch(nextState);
}, import.meta.env.VITE_NEXT_INTERVAL);
}, Number(import.meta.env.VITE_NEXT_INTERVAL));

return () => clearInterval(interval);
});

// Display the correct component based on state machine's state
const state = useSelector(state => state.state);
const state = useAppSelector(state => state.state);

switch (state.current) {
case 'activities':
case StateMachineState.Activities:
return <Activities current={state.activityIndex} />;
case 'advertisement':
case StateMachineState.Advertisement:
return <Ad current={state.adIndex} />;
case 'boardText':
case StateMachineState.BoardText:
return <BoardText current={state.boardMessageIndex} />;
case 'quotes':
case StateMachineState.Quotes:
return <Quotes current={state.quoteIndex} />;
case 'commits':
case StateMachineState.Commits:
return <Commits />;
default:
return <></>;
Expand Down
16 changes: 7 additions & 9 deletions src/components/Activities.jsx → src/components/Activities.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import Activity from './Activity';
import Poster from './Poster';
import PropTypes from 'prop-types';
import { useActivitiesQuery } from '../store/api';

export default function Activities({ current }) {
type ActivitiesProps = {
current: number
};

export default function Activities({ current }: ActivitiesProps) {
const { data: activities, isSuccess } = useActivitiesQuery(undefined, {
pollingInterval: Number(import.meta.env.VITE_LOAD_INTERVAL)
});
Expand All @@ -21,10 +24,10 @@ export default function Activities({ current }) {
key={i}
{...activity}
active={activity === currentActivity}
/>
/>
)}
</ul>
<Poster poster={currentActivity ? currentActivity.poster : null} />
<Poster src={currentActivity.poster} />
</div>
);
} else {
Expand All @@ -35,8 +38,3 @@ export default function Activities({ current }) {
);
}
}

// Explain expected types, for early error detection
Activities.propTypes = {
current: PropTypes.number.isRequired
};
60 changes: 0 additions & 60 deletions src/components/Activity.jsx

This file was deleted.

52 changes: 52 additions & 0 deletions src/components/Activity.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useEffect, useRef } from 'react';
import moment from 'moment';
import { type Activity } from '../store/api';

function sameDay(d: Date, t: Date): boolean {
return d.getDate() === t.getDate() // getDate returns day number in month...
&& d.getMonth() === t.getMonth()
&& d.getFullYear() === t.getFullYear();
}

function createFormat(has_time: boolean, date: Date, as = new Date()): string {
const format =
(!sameDay(date, as) ? 'dddd DD-MM ' : '')
+ (has_time ? 'HH:mm' : '');
return format || '[]';
}

type ActivityProps = Activity & { active: boolean };

export default function Activity({
active, name, start_date, end_date,
has_start_time, has_end_time, participant_counter
}: ActivityProps) {
const activityRef = useRef<HTMLLIElement | null>(null);

// Ensure that the current activity is visible
useEffect(() => {
if (active && activityRef.current)
activityRef.current.scrollIntoView({
behavior: 'smooth'
});
}, [active]);

const startDate = moment(start_date)
.format(createFormat(has_start_time, new Date(start_date)));
const endDate = end_date
? moment(end_date)
.format(createFormat(has_end_time, new Date(end_date), new Date(start_date)))
: null;
const className = 'activity' + (active ? ' active' : '');

return (
<li className={className} ref={activityRef}>
<h1>
{name}
{participant_counter && ` (${participant_counter})`}
</h1>
<time>{startDate}</time>
{endDate && <> - <time>{endDate}</time></>}
</li>
);
}
22 changes: 12 additions & 10 deletions src/components/Ad.jsx → src/components/Ad.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
import PropTypes from 'prop-types';
import Poster from './Poster';
import { useAdsQuery } from '../store/api';

export default function Ad({ current }) {
type AdProps = {
current: number;
};

export default function Ad({ current }: AdProps) {
const { data: ads, isSuccess } = useAdsQuery();

if (!isSuccess)
return <></>;

const currentAd = ads[current];

if (!currentAd.poster?.fields.file?.url)
throw new Error('Ad without poster');

if (ads.length <= 0) {
return (
<div>
<ul className="advertisement"></ul>
<Poster poster={'https://public.svsticky.nl/.hidden/Backup-Ad.png'} />
<Poster src={'https://public.svsticky.nl/.hidden/Backup-Ad.png'} />
</div>
);
} else if (currentAd.fullscreen) {
return (
<div className="full-advertisement">
<Poster poster={`https:${currentAd.poster.fields.file.url}`}></Poster>
<Poster src={`https:${currentAd.poster.fields.file.url}`}></Poster>
</div>
);
} else {
Expand All @@ -29,13 +36,8 @@ export default function Ad({ current }) {
<h1>{currentAd.title}</h1>
<p>{currentAd.description}</p>
</ul>
<Poster poster={`https:${currentAd.poster.fields.file.url}`}></Poster>
<Poster src={`https:${currentAd.poster.fields.file.url}`}></Poster>
</div>
);
}
}

// Explain expected types, for early error detection
Ad.propTypes = {
current: PropTypes.number.isRequired
};
Loading

0 comments on commit 08718a1

Please sign in to comment.