This commit is contained in:
Philippe Torrel
2026-11-04 11:35:00 +01:00
parent c356167f97
commit 6b58d09ec9
8 changed files with 3488 additions and 168 deletions
File diff suppressed because it is too large Load Diff
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { HeartFilled, HeartIcon } from './Icons';
import { FAVORITE_WORDS } from '../lib/constants';
interface FavoriteWordProps {
word: string;
@@ -11,35 +12,30 @@ const FavoriteWord: React.FC<FavoriteWordProps> = ({ word }) => {
const [isFavorite, setIsFavorite] = useState(false);
const toggleFavorite = () => {
const favorites = JSON.parse(localStorage.getItem('favoriteWords') || '[]');
const favorites = JSON.parse(localStorage.getItem(FAVORITE_WORDS) || '[]');
if (favorites.includes(word)) {
const filteredFavorites = favorites.filter(
(item: string) => item !== word
);
localStorage.setItem('favoriteWords', JSON.stringify(filteredFavorites));
const filteredFavorites = favorites.filter((item: string) => item !== word);
localStorage.setItem(FAVORITE_WORDS, JSON.stringify(filteredFavorites));
} else {
favorites.push(word);
localStorage.setItem('favoriteWords', JSON.stringify(favorites));
localStorage.setItem(FAVORITE_WORDS, JSON.stringify(favorites));
}
setIsFavorite(!isFavorite);
};
// BUG: Missing useEffect
/* useEffect(() => {
const favoriteWords = JSON.parse(
localStorage.getItem('favoriteWords') || '[]'
);
useEffect(() => {
const favoriteWords = JSON.parse(localStorage.getItem(FAVORITE_WORDS) || '[]');
setIsFavorite(favoriteWords.includes(word));
}, [word]); */
}, [word]);
return (
<div className="fave-word-wrapper">
<Link
// to={`/?word=${word}`}
to="/" // BUG
className="fave-word-link"
>
to={`/?word=${word}`}
// to="/" // BUG
className="fave-word-link">
<p>{word}</p>
</Link>
@@ -1,6 +1,7 @@
import React, { useState, useEffect, useRef } from 'react';
import { PlayIcon, HeartIcon, HeartFilled } from './Icons';
import { Phonetic } from '../lib/datatypes';
import { FAVORITE_WORDS } from '../lib/constants';
interface WordHeroProps {
data: {
@@ -14,11 +15,11 @@ const WordHero: React.FC<WordHeroProps> = ({ data }) => {
const [isFavorite, setIsFavorite] = useState(false);
const americanEnglishPhonetic = phonetics.find((phonetic) =>
phonetic.audio?.includes('-us')
);
const americanEnglishPhonetic = phonetics.find((phonetic) => phonetic.audio?.includes('-us'));
const finalPhonetic = americanEnglishPhonetic || phonetics[0];
console.log(finalPhonetic?.audio);
const audioRef = useRef<HTMLAudioElement | null>(null);
const playAudio = () => {
@@ -28,16 +29,14 @@ const WordHero: React.FC<WordHeroProps> = ({ data }) => {
};
const toggleFavorite = () => {
const favorites = JSON.parse(localStorage.getItem('favoriteWords') || '[]');
const favorites = JSON.parse(localStorage.getItem(FAVORITE_WORDS) || '[]');
if (favorites.includes(word)) {
const filteredFavorites = favorites.filter(
(item: string) => item !== word
);
localStorage.setItem('favoriteWords', JSON.stringify(filteredFavorites));
const filteredFavorites = favorites.filter((item: string) => item !== word);
localStorage.setItem(FAVORITE_WORDS, JSON.stringify(filteredFavorites));
} else {
favorites.push(word);
localStorage.setItem('favoriteWords', JSON.stringify(favorites));
localStorage.setItem(FAVORITE_WORDS, JSON.stringify(favorites));
}
setIsFavorite(!isFavorite);
@@ -45,10 +44,11 @@ const WordHero: React.FC<WordHeroProps> = ({ data }) => {
useEffect(() => {
const favoriteWords = JSON.parse(
localStorage.getItem('favortieWords') || '[]' // BUG favortieWords instead of (favoriteWords)
localStorage.getItem(FAVORITE_WORDS) || '[]', // BUG favortieWords instead of (favoriteWords)
);
// setIsFavorite(favoriteWords.includes(word)); // BUG
}, [word]);
setIsFavorite(favoriteWords.includes(word)); // BUG
}, [word]); // did update
return (
<div className="wordhero-wrapper">
@@ -62,9 +62,7 @@ const WordHero: React.FC<WordHeroProps> = ({ data }) => {
</button>
<h1 className="wordhero-word">{word}</h1>
{finalPhonetic?.text && (
<p className="wordhero-word-phonetic">{finalPhonetic.text}</p>
)}
{finalPhonetic?.text && <p className="wordhero-word-phonetic">{finalPhonetic.text}</p>}
</div>
{finalPhonetic?.audio && (
@@ -1,5 +1,6 @@
export const SERIF_FONTS =
'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif';
export const SERIF_FONTS = 'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif';
export const SANS_SERIF_FONTS =
'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"';
export const FAVORITE_WORDS = 'favoriteWords';
@@ -3,16 +3,14 @@ import { useState, useEffect } from 'react';
import Header from '../components/Header';
import FavoriteWord from '../components/FavoriteWord';
import { SERIF_FONTS } from '../lib/constants';
import { FAVORITE_WORDS, SERIF_FONTS } from '../lib/constants';
function Favorites() {
const [font, setFont] = useState<string>(SERIF_FONTS);
const [favoriteWords, setFavoriteWords] = useState<string[]>([]);
useEffect(() => {
const loadedFavorites = JSON.parse(
localStorage.getItem('favoriteWords') || '[]'
);
const loadedFavorites = JSON.parse(localStorage.getItem(FAVORITE_WORDS) || '[]');
setFavoriteWords(loadedFavorites);
}, []);
@@ -21,17 +19,13 @@ function Favorites() {
className="app-wrapper"
style={{
fontFamily: font,
}}
>
}}>
<Header font={font} setFont={setFont} />
{favoriteWords.length > 0 ? (
<ul className="favorites-list">
{favoriteWords.map((word, index) => (
<li
className={`favorites-item ${index > 0 ? 'with-divider' : ''}`}
key={index}
>
<li className={`favorites-item ${index > 0 ? 'with-divider' : ''}`} key={index}>
<FavoriteWord word={word} />
</li>
))}
@@ -8,10 +8,13 @@ import WordDefinitions from '../components/WordDefinitions';
import SourceFooter from '../components/SourceFooter';
import { SERIF_FONTS } from '../lib/constants';
import { useSearchParams } from 'react-router-dom';
function Root() {
const [params] = useSearchParams();
const [font, setFont] = useState<string>(SERIF_FONTS);
const [word, setWord] = useState<string>('course');
const [word, setWord] = useState<string>(params.get('word') || 'course');
const [wordData, setWordData] = useState<WordData | null>(null);
const fetchData = async () => {
@@ -40,8 +43,7 @@ function Root() {
className="app-wrapper"
style={{
fontFamily: font,
}}
>
}}>
<Header font={font} setFont={setFont} />
<Searchbar word={word} setWord={setWord} handleSubmit={handleSubmit} />