profanity filter

This commit is contained in:
fred 2025-08-20 12:04:58 -07:00
parent 2e7d9e6ba6
commit 798e879863
2 changed files with 83 additions and 22 deletions

View file

@ -1,11 +1,12 @@
import React, { useState } from 'react'; import React, { useState } from "react";
import { addRecipe } from "../services/frontendApi.js"; import { addRecipe } from "../services/frontendApi.js";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import AddBulkIngredients from "../components/AddBulkIngredients.tsx" import AddBulkIngredients from "../components/AddBulkIngredients.tsx";
import AddBulkSteps from "../components/AddBulkSteps.tsx" import AddBulkSteps from "../components/AddBulkSteps.tsx";
import StarRating from "../components/StarRating.tsx" import StarRating from "../components/StarRating.tsx";
import DemoModal from '../components/DemoModal.tsx' import DemoModal from "../components/DemoModal.tsx";
import '../css/colorTheme.css'; import "../css/colorTheme.css";
import { isProfane } from "../utils/profanityFilter";
interface Step { interface Step {
step_number: number; step_number: number;
@ -28,14 +29,29 @@ function AddRecipe() {
const addRecipeForm = async (event: React.FormEvent) => { const addRecipeForm = async (event: React.FormEvent) => {
event.preventDefault(); event.preventDefault();
if (process.env.NODE_ENV === 'demo') { if (
isProfane(recipeName) ||
isProfane(recipeCuisine) ||
ingredients.some((ingredient) => isProfane(ingredient)) ||
steps.some((step) => isProfane(step.instruction)) ||
isProfane(author)
) {
alert("Your submission contains inappropriate language.");
return;
}
if (process.env.NODE_ENV === "demo") {
setShowDemoModal(true); setShowDemoModal(true);
return; return;
} }
const stepsHash = Object.fromEntries( const stepsHash = Object.fromEntries(
steps.map(step => [step.step_number, step.instruction]) steps.map((step) => [step.step_number, step.instruction]),
); );
if (recipeName && recipeCuisine && Object.keys(stepsHash).length > 0 && ingredients.length > 0) { if (
recipeName &&
recipeCuisine &&
Object.keys(stepsHash).length > 0 &&
ingredients.length > 0
) {
const recipeData = { const recipeData = {
name: recipeName, name: recipeName,
cuisine: recipeCuisine.toLowerCase(), cuisine: recipeCuisine.toLowerCase(),
@ -44,13 +60,13 @@ function AddRecipe() {
cook_minutes: cookMinutes, cook_minutes: cookMinutes,
stars: stars, stars: stars,
ingredients: ingredients, ingredients: ingredients,
steps: stepsHash steps: stepsHash,
} };
console.log(recipeData) console.log(recipeData);
const data = await addRecipe(recipeData); const data = await addRecipe(recipeData);
setNewRecipeId(data.id); setNewRecipeId(data.id);
} else { } else {
alert('missing required data') alert("missing required data");
} }
}; };
@ -88,7 +104,12 @@ function AddRecipe() {
/> />
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<div> <div>
<label htmlFor="prepTime" className="mr-2 font-bold text-[var(--color-secondaryTextDark)]">Prep Time:</label> <label
htmlFor="prepTime"
className="mr-2 font-bold text-[var(--color-secondaryTextDark)]"
>
Prep Time:
</label>
<input <input
type="number" type="number"
placeholder="prep time in minutes" placeholder="prep time in minutes"
@ -96,10 +117,17 @@ function AddRecipe() {
value={prepMinutes} value={prepMinutes}
onChange={(e) => setPrepMinutes(parseInt(e.target.value))} onChange={(e) => setPrepMinutes(parseInt(e.target.value))}
/> />
<span className="ml-2 text-[var(--color-secondaryTextDark)]">minutes</span> <span className="ml-2 text-[var(--color-secondaryTextDark)]">
minutes
</span>
</div> </div>
<div> <div>
<label htmlFor="cookTime" className="mr-2 font-bold text-[var(--color-secondaryTextDark)]">Cook Time:</label> <label
htmlFor="cookTime"
className="mr-2 font-bold text-[var(--color-secondaryTextDark)]"
>
Cook Time:
</label>
<input <input
type="number" type="number"
placeholder="cook time in minutes" placeholder="cook time in minutes"
@ -107,10 +135,15 @@ function AddRecipe() {
value={cookMinutes} value={cookMinutes}
onChange={(e) => setCookMinutes(parseInt(e.target.value))} onChange={(e) => setCookMinutes(parseInt(e.target.value))}
/> />
<span className="ml-2 text-[var(--color-secondaryTextDark)]">minutes</span> <span className="ml-2 text-[var(--color-secondaryTextDark)]">
minutes
</span>
</div> </div>
<div> <div>
<StarRating rating={stars} onRatingChange={(newRating: number) => setStars(newRating)} /> <StarRating
rating={stars}
onRatingChange={(newRating: number) => setStars(newRating)}
/>
</div> </div>
</div> </div>
<label className="mb-4 flex items-center cursor-pointer"> <label className="mb-4 flex items-center cursor-pointer">
@ -124,7 +157,10 @@ function AddRecipe() {
</div> </div>
</label> </label>
<div> <div>
<AddBulkIngredients ingredients={ingredients} onChange={setIngredients} /> <AddBulkIngredients
ingredients={ingredients}
onChange={setIngredients}
/>
</div> </div>
{/*<ul className="mb-4"> {/*<ul className="mb-4">
{ingredients.map((ing, index) => ( {ingredients.map((ing, index) => (
@ -143,7 +179,10 @@ function AddRecipe() {
</li> </li>
))} ))}
</ul>*/} </ul>*/}
<button type="submit" className="ar-button bg-[var(--color-buttonBg)] text-[var(--color-textLight)] py-2 px-4 rounded hover:bg-[var(--color-buttonBgHover)]"> <button
type="submit"
className="ar-button bg-[var(--color-buttonBg)] text-[var(--color-textLight)] py-2 px-4 rounded hover:bg-[var(--color-buttonBgHover)]"
>
submit submit
</button> </button>
</form> </form>
@ -155,6 +194,6 @@ function AddRecipe() {
/> />
)} )}
</div> </div>
) );
} }
export default AddRecipe export default AddRecipe;

View file

@ -0,0 +1,22 @@
let profaneWords: string[] = [];
const loadProfaneWords = async () => {
try {
const response = await fetch(
"https://raw.githubusercontent.com/web-mech/badwords/master/lib/lang.json",
);
const data = await response.json();
profaneWords = data.words;
} catch (error) {
if (process.env.NODE_ENV === "dev") {
console.log("Error loading profane words:", (error as Error).message);
}
}
};
loadProfaneWords();
export const isProfane = (input: string): boolean => {
const words = input.toLowerCase().split(/\s+/);
return words.some((word) => profaneWords.includes(word));
};