Skip to content

Commit a979750

Browse files
committed
refactor: Cleanup code and add integration to backend themes
1 parent 1fe2b42 commit a979750

24 files changed

+490
-376
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
/coverage
1010

1111
# env
12-
.env
12+
.env*
1313

1414
# production
1515
/build

src/components/NavigationBar/NavigationBar.tsx

+37-28
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
1-
import { useState } from 'react'
2-
import { Link, useNavigate } from 'react-router-dom'
3-
import { useAuth } from '../../context/AuthContext'
4-
import logo from '../../assets/images/logo.png'
5-
import { handleLogin } from '../../services/authService'
6-
import { SiteConfig } from '../../constants/SiteConfig'
1+
import { useState } from 'react';
2+
import { Link, useNavigate } from 'react-router-dom';
73

4+
import { handleLogin } from '../../services/authService';
5+
import { useAuth } from '../../context/AuthContext';
6+
import { SiteConfig } from '../../constants/SiteConfig';
7+
import logo from '../../assets/images/logo.png';
8+
9+
/**
10+
* Navigation bar for users to navigate between pages.
11+
*/
812
const NavigationBar = () => {
9-
const navigate = useNavigate()
13+
// context for handling user data
1014
const { isLoggedIn, setIsLoggedIn, setUserData } = useAuth()
15+
16+
// handles page navigation
17+
const navigate = useNavigate()
18+
19+
// menu used for mobile view
1120
const [menuOpen, setMenuOpen] = useState(false)
1221

1322
const toggleMenu = () => {
@@ -17,7 +26,7 @@ const NavigationBar = () => {
1726
return (
1827
<nav
1928
className="fixed top-0 w-full z-50 opacity-80 bg-black
20-
text-white py-2 px-6 flex justify-between items-center"
29+
text-white py-2 px-6 flex justify-between items-center"
2130
style={{ height: '8vh' }}
2231
>
2332
<Link to="/" className="flex items-center">
@@ -53,15 +62,15 @@ const NavigationBar = () => {
5362
{menuOpen && (
5463
<div
5564
className="absolute right-0 mt-2 bg-gray-800 border
56-
border-gray-600 rounded-md shadow-lg py-1 w-48 z-10"
65+
border-gray-600 rounded-md shadow-lg py-1 w-48 z-10"
5766
>
5867
<ul className="px-2">
5968
{/* needs improvement */}
6069
{/* <li className="my-1">
61-
<Link to="/about" className="block px-4 py-2 hover:bg-gray-700">
62-
About
63-
</Link>
64-
</li> */}
70+
<Link to="/about" className="block px-4 py-2 hover:bg-gray-700">
71+
About
72+
</Link>
73+
</li> */}
6574
<li className="my-1">
6675
<Link
6776
to="/plugins"
@@ -106,13 +115,13 @@ const NavigationBar = () => {
106115
</li>
107116
{/* needs improvement */}
108117
{/* <li className="my-1">
109-
<Link
110-
to="/terms-of-service"
111-
className="block px-4 py-2 hover:bg-gray-700"
112-
>
113-
Terms of Service
114-
</Link>
115-
</li> */}
118+
<Link
119+
to="/terms-of-service"
120+
className="block px-4 py-2 hover:bg-gray-700"
121+
>
122+
Terms of Service
123+
</Link>
124+
</li> */}
116125
{isLoggedIn ? (
117126
<>
118127
<li className="my-1">
@@ -150,10 +159,10 @@ const NavigationBar = () => {
150159
<ul className="hidden md:flex pr-20">
151160
{/* needs improvement */}
152161
{/* <li className="mr-8">
153-
<Link to="/about" className="hover:text-blue-500">
154-
About
155-
</Link>
156-
</li> */}
162+
<Link to="/about" className="hover:text-blue-500">
163+
About
164+
</Link>
165+
</li> */}
157166
<li className="mr-8">
158167
<Link to="/plugins" className="hover:text-blue-500">
159168
Plugins
@@ -189,10 +198,10 @@ const NavigationBar = () => {
189198
</li>
190199
{/* needs improvement */}
191200
{/* <li className="mr-8">
192-
<Link to="/terms-of-service" className="hover:text-blue-500">
193-
Terms of Service
194-
</Link>
195-
</li> */}
201+
<Link to="/terms-of-service" className="hover:text-blue-500">
202+
Terms of Service
203+
</Link>
204+
</li> */}
196205
{isLoggedIn ? (
197206
<>
198207
<li className="mr-8">

src/components/SearchBar/SearchBar.tsx

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1-
import React, { useState } from 'react'
1+
import React, { useState } from 'react';
22

33
type Props = {
4-
onSearch: (query: string) => void
4+
onSearch: (query: string) => void
55
}
66

7+
/**
8+
* Searchbar for users to input their search query.
9+
*/
710
const SearchBar: React.FC<Props> = ({ onSearch }) => {
11+
// tracks current user search query
812
const [query, setQuery] = useState('')
913

14+
// todo: perhaps only search when enter is pressed while search is focused
15+
// to reduce the api calls to backend incurred from every change
1016
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
1117
const { value } = e.target
1218
setQuery(value)

src/components/Themes/ThemeCard.tsx

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import React, { useState } from 'react'
2-
import { Link } from 'react-router-dom'
1+
import React, { useState } from 'react';
32

4-
import ThemeModal from './ThemeModal'
5-
import { Theme } from '../../interfaces/Theme'
6-
import FavIcon from '../../assets/images/icon_favorite.svg'
7-
import GitHubIcon from '../../assets/images/icon_github_white.svg'
3+
import { Link } from 'react-router-dom';
4+
5+
import ThemeModal from './ThemeModal';
6+
import { Theme } from '../../interfaces/Theme';
7+
import FavIcon from '../../assets/images/icon_favorite.svg';
8+
import GitHubIcon from '../../assets/images/icon_github_white.svg';
89

910
import '../../styles/theme_card.css'
1011

@@ -14,6 +15,9 @@ type Props = {
1415
onPreview: (name: string) => void
1516
}
1617

18+
/**
19+
* Theme card component to hold the details of each theme in the themes page.
20+
*/
1721
const ThemeCard: React.FC<Props> = ({ theme, isPreviewed, onPreview }) => {
1822
const [isFav, setIsFav] = useState(false)
1923
const [viewDetails, setViewDetails] = useState(false)

src/components/Themes/ThemeGallery.tsx

-115
This file was deleted.

src/components/Themes/ThemeModal.tsx

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
import React, { useEffect, useRef } from 'react'
2-
import ReactDOM from 'react-dom'
3-
import { Theme } from '../../interfaces/Theme'
4-
import { downloadThemeContent } from '../../utils'
1+
import React, { useEffect, useRef } from 'react';
2+
3+
import ReactDOM from 'react-dom';
4+
5+
import { downloadThemeContent } from '../../utils';
6+
import { Theme } from '../../interfaces/Theme';
57

68
type ThemeModalProps = {
79
isOpen: boolean
810
onClose: () => void
911
theme: Theme
1012
}
1113

14+
/**
15+
* Modal to popup for showing theme details.
16+
*/
1217
const ThemeModal: React.FC<ThemeModalProps> = ({ isOpen, onClose, theme }) => {
1318
const modalRef = useRef<HTMLDivElement>(null)
1419

src/constants/Endpoints.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// endpoints for making requests
2+
export const Endpoints = {
3+
fetchApiThemes: `${import.meta.env.VITE_GALLERY_API_URL}/api/v1/themes`,
4+
fetchUserData: `${import.meta.env.VITE_GALLERY_API_URL}/api/v1/auth/login/process`,
5+
fetchCacheThemes: import.meta.env.VITE_GITHUB_THEMES_CACHE_URL,
6+
gitHubLoginUrl: "https://github.com/login/oauth/authorize"
7+
}

src/hooks/useFetchThemes.tsx

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { useEffect, useRef, useState } from "react";
2+
3+
import { ApiTheme } from "../interfaces/ApiTheme";
4+
import { getGitHubThemeData } from "../services/themeService";
5+
import { Theme } from "../interfaces/Theme";
6+
7+
/**
8+
* Fetches themes from the backend api.
9+
*
10+
* @param url url to fetch themes from
11+
* @param pageSize number of themes to fetch each page
12+
* @param pageNum page number to fetch for
13+
* @param searchQuery search query for filtering themes
14+
*/
15+
const useFetchThemes = (
16+
url: string,
17+
pageSize: number,
18+
pageNum: number,
19+
searchQuery?: string
20+
) => {
21+
const [themes, setThemes] = useState<Theme[]>([]);
22+
const [loading, setLoading] = useState<boolean>(true);
23+
const [error, setError] = useState<Error | null>(null);
24+
const fetchInitiated = useRef<boolean>(false);
25+
26+
useEffect(() => {
27+
// prevents duplicate fetching
28+
if (fetchInitiated.current) {
29+
return;
30+
}
31+
32+
const fetchData = async () => {
33+
try {
34+
let finalUrl = `${url}?pageSize=${pageSize}&pageNum=${pageNum}`;
35+
if (searchQuery) {
36+
finalUrl += `&searchQuery=${encodeURIComponent(searchQuery)}`;
37+
}
38+
39+
const response = await fetch(finalUrl);
40+
const result = await response.json();
41+
const themes = await fetchThemesFromGitHub(result);
42+
setThemes(themes);
43+
} catch (err: unknown) {
44+
setError(err as Error);
45+
} finally {
46+
setLoading(false);
47+
}
48+
};
49+
50+
fetchData();
51+
fetchInitiated.current = true;
52+
}, [url, pageSize, pageNum, searchQuery]);
53+
54+
return { themes, loading, error };
55+
};
56+
57+
/**
58+
* Fetches themes information from github (or more accurately, jsdelivr cache).
59+
*/
60+
const fetchThemesFromGitHub = async (apiThemes: ApiTheme[]): Promise<Theme[]> => {
61+
// todo: good to cache themes already fetched to reduce calls to cdn
62+
return await Promise.all(apiThemes.map(apiTheme => getGitHubThemeData(apiTheme)));
63+
}
64+
65+
export default useFetchThemes;

0 commit comments

Comments
 (0)