Skip to content

Commit 812aca4

Browse files
breaking(3): configuration (#458)
2 parents 7d30933 + 602f2c3 commit 812aca4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2075
-610
lines changed

.devcontainer/devcontainer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"vscode": {
44
"extensions": [
55
"dbaeumer.vscode-eslint",
6-
"freeCodeCamp.freecodecamp-courses@2.1.0",
6+
"freeCodeCamp.freecodecamp-courses@3.0.0",
77
"freeCodeCamp.freecodecamp-dark-vscode-theme"
88
]
99
}
Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,30 @@
1-
import { ProjectI } from '../types';
1+
import { Events, FreeCodeCampConfigI, ProjectI } from '../types';
22
import FreeCodeCampLogo from '../assets/fcc_primary_large';
3+
import { LanguageList } from './language-list';
34

45
interface HeaderProps {
56
updateProject: (project: ProjectI | null) => void;
7+
freeCodeCampConfig: FreeCodeCampConfigI;
8+
sock: (type: Events, data: {}) => void;
69
}
7-
export const Header = ({ updateProject }: HeaderProps) => {
10+
export const Header = ({
11+
sock,
12+
updateProject,
13+
freeCodeCampConfig
14+
}: HeaderProps) => {
815
function returnToLanding() {
916
updateProject(null);
1017
}
18+
19+
const locales = freeCodeCampConfig?.curriculum?.locales
20+
? Object.keys(freeCodeCampConfig?.curriculum?.locales)
21+
: [];
1122
return (
1223
<header>
1324
<button className='header-btn' onClick={returnToLanding}>
1425
<FreeCodeCampLogo />
1526
</button>
27+
{locales.length > 1 ? <LanguageList {...{ sock, locales }} /> : null}
1628
</header>
1729
);
1830
};
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
export function LanguageGlobe() {
2+
return (
3+
<svg
4+
aria-hidden={true}
5+
height={24}
6+
viewBox='0 0 24 24'
7+
width={24}
8+
xmlns='http://www.w3.org/2000/svg'
9+
>
10+
<path
11+
d='M2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12Z'
12+
stroke='currentColor'
13+
strokeWidth='1.5'
14+
strokeLinecap='round'
15+
strokeLinejoin='round'
16+
/>
17+
<path
18+
d='M13 2.04932C13 2.04932 16 5.99994 16 11.9999C16 17.9999 13 21.9506 13 21.9506'
19+
stroke='currentColor'
20+
strokeWidth='1.5'
21+
strokeLinecap='round'
22+
strokeLinejoin='round'
23+
/>
24+
<path
25+
d='M11 21.9506C11 21.9506 8 17.9999 8 11.9999C8 5.99994 11 2.04932 11 2.04932'
26+
stroke='currentColor'
27+
strokeWidth='1.5'
28+
strokeLinecap='round'
29+
strokeLinejoin='round'
30+
/>
31+
<path
32+
d='M2.62964 15.5H21.3704'
33+
stroke='currentColor'
34+
strokeWidth='1.5'
35+
strokeLinecap='round'
36+
strokeLinejoin='round'
37+
/>
38+
<path
39+
d='M2.62964 8.5H21.3704'
40+
stroke='currentColor'
41+
strokeWidth='1.5'
42+
strokeLinecap='round'
43+
strokeLinejoin='round'
44+
/>
45+
</svg>
46+
);
47+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { useRef, useState } from 'react';
2+
import { LanguageGlobe } from './language-globe';
3+
import { Events } from '../types';
4+
5+
type LanguageListProps = {
6+
locales: string[];
7+
sock: (type: Events, data: {}) => void;
8+
};
9+
10+
export function LanguageList({ locales, sock }: LanguageListProps) {
11+
const [showList, setShowList] = useState(false);
12+
const listRef = useRef<HTMLUListElement>(null);
13+
14+
const handleClick = (): void => {
15+
if (listRef.current) {
16+
if (showList) {
17+
listRef.current.classList.add('hidden');
18+
setShowList(false);
19+
return;
20+
}
21+
listRef.current.classList.remove('hidden');
22+
setShowList(true);
23+
}
24+
};
25+
26+
const handleLanguageChange = (
27+
event: React.MouseEvent<HTMLButtonElement>
28+
): void => {
29+
event.preventDefault();
30+
const selectedLanguage = event.currentTarget.dataset.value;
31+
if (selectedLanguage === undefined) return;
32+
sock(Events.CHANGE_LANGUAGE, { locale: selectedLanguage });
33+
};
34+
35+
return (
36+
<>
37+
<button
38+
id='toggle-lang-button'
39+
aria-expanded={showList}
40+
onClick={handleClick}
41+
>
42+
<LanguageGlobe />
43+
</button>
44+
<ul
45+
id='nav-lang-list'
46+
ref={listRef}
47+
className='hidden'
48+
aria-labelledby='toggle-lang-button'
49+
>
50+
{locales.map(lang => (
51+
<li key={'lang-' + lang}>
52+
<button data-value={lang} onClick={handleLanguageChange}>
53+
{lang}
54+
</button>
55+
</li>
56+
))}
57+
</ul>
58+
</>
59+
);
60+
}

.freeCodeCamp/client/components/output.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Hints } from './hints';
77

88
interface OutputProps {
99
isLoading: boolean;
10-
hints?: string[];
10+
hints: string[];
1111
tests: TestType[];
1212
cons: ConsoleError[];
1313
}
@@ -40,7 +40,7 @@ export const Output = ({ isLoading, hints, tests, cons }: OutputProps) => {
4040
Console
4141
</button>
4242
</li>
43-
{hints && (
43+
{hints.length ? (
4444
<li>
4545
<button
4646
className='output-btn'
@@ -52,7 +52,7 @@ export const Output = ({ isLoading, hints, tests, cons }: OutputProps) => {
5252
Hints
5353
</button>
5454
</li>
55-
)}
55+
) : null}
5656
</ul>
5757
{isLoading ? (
5858
<Loader />
@@ -65,7 +65,7 @@ export const Output = ({ isLoading, hints, tests, cons }: OutputProps) => {
6565
case 'console':
6666
return <Console cons={cons} />;
6767
case 'hints':
68-
return <Hints hints={hints!} />;
68+
return <Hints hints={hints} />;
6969
default:
7070
return <div>No content</div>;
7171
}

.freeCodeCamp/client/index.tsx

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import './styles.css';
1616
import { E44o5 } from './components/error';
1717

1818
// Dynamically construct the socket url based on `window.location`
19-
const socket = new WebSocket(
19+
let socket = new WebSocket(
2020
`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${
2121
window.location.host
2222
}`
@@ -30,6 +30,7 @@ const App = () => {
3030

3131
const [lessonNumber, setLessonNumber] = useState(1);
3232
const [description, setDescription] = useState('');
33+
const [locale, setLocale] = useState('english');
3334
const [tests, setTests] = useState<TestType[]>([]);
3435
const [hints, setHints] = useState<string[]>([]);
3536
const [cons, setCons] = useState<ConsoleError[]>([]);
@@ -40,9 +41,10 @@ const App = () => {
4041
const [debouncers, setDebouncers] = useState<string[]>([]);
4142
const [connected, setConnected] = useState<boolean>(false);
4243

43-
useEffect(() => {
44+
function connectToWebSocket() {
4445
socket.onopen = function (_event) {
4546
setConnected(true);
47+
setAlertCamper(null);
4648
sock(Events.CONNECT);
4749
};
4850
socket.onmessage = function (event) {
@@ -54,13 +56,24 @@ const App = () => {
5456
socket.onclose = function (_event) {
5557
setAlertCamper('Client has disconnected from local server');
5658
setConnected(false);
59+
// Try to reconnect
60+
setTimeout(() => {
61+
socket = new WebSocket(
62+
`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${
63+
window.location.host
64+
}`
65+
);
66+
connectToWebSocket();
67+
}, 1000);
5768
};
5869

5970
return () => {
6071
console.log('socket closing');
6172
socket.close();
6273
};
63-
}, []);
74+
}
75+
76+
useEffect(connectToWebSocket, []);
6477

6578
const handle = {
6679
'handle-project-finish': handleProjectFinish,
@@ -76,6 +89,7 @@ const App = () => {
7689
'update-freeCodeCamp-config': setFreeCodeCampConfig,
7790
'update-error': updateError,
7891
'reset-tests': resetTests,
92+
'update-locale': setLocale,
7993
RESPONSE: debounce
8094
};
8195

@@ -182,7 +196,7 @@ const App = () => {
182196
if (alertCamper) {
183197
return (
184198
<>
185-
<Header updateProject={updateProject} />
199+
<Header {...{ sock, updateProject, freeCodeCampConfig }} />
186200
<E44o5 text={alertCamper} error={error} />
187201
</>
188202
);
@@ -191,7 +205,7 @@ const App = () => {
191205
return (
192206
<>
193207
<Suspense fallback={<Loader />}>
194-
<Header updateProject={updateProject} />
208+
<Header {...{ sock, updateProject, freeCodeCampConfig }} />
195209
{project ? (
196210
<Project
197211
{...{
@@ -210,7 +224,7 @@ const App = () => {
210224
}}
211225
/>
212226
) : (
213-
<Landing {...{ sock, projects, freeCodeCampConfig }} />
227+
<Landing {...{ locale, sock, projects, freeCodeCampConfig }} />
214228
)}
215229
</Suspense>
216230
</>

.freeCodeCamp/client/styles.css

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -74,37 +74,37 @@ p {
7474
}
7575

7676
.loader {
77-
--b: 10px; /* border thickness */
78-
--n: 10; /* number of dashes*/
79-
--g: 10deg; /* gap between dashes*/
80-
--c: red; /* the color */
81-
82-
width: 100px; /* size */
77+
--b: 10px;
78+
/* border thickness */
79+
--n: 10;
80+
/* number of dashes*/
81+
--g: 10deg;
82+
/* gap between dashes*/
83+
--c: red;
84+
/* the color */
85+
86+
width: 100px;
87+
/* size */
8388
margin: 0 auto;
8489
aspect-ratio: 1;
8590
border-radius: 50%;
86-
padding: 1px; /* get rid of bad outlines */
91+
padding: 1px;
92+
/* get rid of bad outlines */
8793
background: conic-gradient(#0000, var(--c)) content-box;
88-
-webkit-mask: /* we use +/-1deg between colors to avoid jagged edges */ repeating-conic-gradient(
89-
#0000 0deg,
94+
-webkit-mask:
95+
/* we use +/-1deg between colors to avoid jagged edges */
96+
repeating-conic-gradient(#0000 0deg,
9097
#000 1deg calc(360deg / var(--n) - var(--g) - 1deg),
91-
#0000 calc(360deg / var(--n) - var(--g)) calc(360deg / var(--n))
92-
),
93-
radial-gradient(
94-
farthest-side,
98+
#0000 calc(360deg / var(--n) - var(--g)) calc(360deg / var(--n))),
99+
radial-gradient(farthest-side,
95100
#0000 calc(98% - var(--b)),
96-
#000 calc(100% - var(--b))
97-
);
98-
mask: repeating-conic-gradient(
99-
#0000 0deg,
101+
#000 calc(100% - var(--b)));
102+
mask: repeating-conic-gradient(#0000 0deg,
100103
#000 1deg calc(360deg / var(--n) - var(--g) - 1deg),
101-
#0000 calc(360deg / var(--n) - var(--g)) calc(360deg / var(--n))
102-
),
103-
radial-gradient(
104-
farthest-side,
104+
#0000 calc(360deg / var(--n) - var(--g)) calc(360deg / var(--n))),
105+
radial-gradient(farthest-side,
105106
#0000 calc(98% - var(--b)),
106-
#000 calc(100% - var(--b))
107-
);
107+
#000 calc(100% - var(--b)));
108108
-webkit-mask-composite: destination-in;
109109
mask-composite: intersect;
110110
animation: load 1s infinite steps(var(--n));
@@ -210,3 +210,39 @@ details {
210210
position: relative;
211211
top: 1px;
212212
}
213+
214+
#toggle-lang-button {
215+
margin: 0 0.5rem;
216+
position: absolute;
217+
right: 0;
218+
top: 0.28rem;
219+
}
220+
221+
#toggle-lang-button:hover {
222+
cursor: pointer;
223+
}
224+
225+
#nav-lang-list {
226+
position: absolute;
227+
right: 0;
228+
background-color: var(--dark-1);
229+
border-radius: 0.3rem;
230+
padding: 0.3rem;
231+
margin-top: 2.4rem;
232+
z-index: 1;
233+
}
234+
#nav-lang-list > li {
235+
padding: 0.28rem;
236+
list-style: none;
237+
}
238+
239+
#nav-lang-list > li > button {
240+
width: 100%;
241+
color: var(--dark-3);
242+
}
243+
244+
#nav-lang-list > li > button:hover {
245+
color: var(--light-2);
246+
background-color: var(--dark-3);
247+
cursor: pointer;
248+
}

0 commit comments

Comments
 (0)