From 6f2a14f7293b0fd12003ac39fa78873b5b55364f Mon Sep 17 00:00:00 2001 From: person973 Date: Thu, 6 Aug 2026 14:55:57 -0400 Subject: [PATCH 01/10] TagDisplay is not appearing --- client/src/api/users.ts | 17 ++- client/src/components/TagDisplay.tsx | 4 +- client/src/components/pages/NewSettings.tsx | 125 +++++++++++++++++++- 3 files changed, 140 insertions(+), 6 deletions(-) diff --git a/client/src/api/users.ts b/client/src/api/users.ts index a12bf384a..866577579 100644 --- a/client/src/api/users.ts +++ b/client/src/api/users.ts @@ -172,7 +172,7 @@ export const getGalleryVideos = async (userId: number): Promise> => { const apiURL = `/me/gallery/${userId}/images`; - + const form = new FormData(); for (const [name, value] of Object.entries(imageData)) { if (value !== null) form.append(name, value); @@ -446,6 +446,21 @@ export const updateTagExclusion = async ( return response as ApiResponse; }; +/** + * Gets a list of all tags + * @returns API response, data is Tag[] + */ +export const getAllTags = async () => { + const URL = `/datasets/tags`; + const res = await GET(URL); + + if (res.error) { + console.log(`Error in getAllTags: ${res.error}`); + } + + return res; +} + //#endregion // #region PROJECT FOLLOWINGS/VISIBILITY diff --git a/client/src/components/TagDisplay.tsx b/client/src/components/TagDisplay.tsx index 442488081..5747c6e25 100644 --- a/client/src/components/TagDisplay.tsx +++ b/client/src/components/TagDisplay.tsx @@ -8,7 +8,7 @@ import { Medium, Skill, Tag } from "@looking-for-group/shared"; * @param type - the broader grouper, in a string format to allow for all type data types * @param category - the more specific grouper, in a string format to allow for all category data types */ -type TagOrSkill = { +export type TagOrSkill = { label: string; id: number; type: string; @@ -114,7 +114,7 @@ const CatOrder: Record = { * @param searchValue - the value of the search bar present in the tag/skill menu * @param searchData - the returned array of tags/skills found by the search bar */ -interface TagDisplayProps { +export interface TagDisplayProps { selected: TagOrSkill[]; toggleTag: (id: number, type: string) => void; tabs: string[]; diff --git a/client/src/components/pages/NewSettings.tsx b/client/src/components/pages/NewSettings.tsx index 7ff596a27..48ee5d49c 100644 --- a/client/src/components/pages/NewSettings.tsx +++ b/client/src/components/pages/NewSettings.tsx @@ -9,9 +9,11 @@ import { Header } from '../Header'; //import PasswordValidator from 'password-validator'; import ToTopButton from '../ToTopButton'; import * as paths from '../../constants/routes'; -import { getUserByEmail, getUserByUsername, getCurrentAccount, deleteUser, editUser } from '../../api/users'; -import { MePrivate, UpdateUserInput } from '@looking-for-group/shared'; +import { getUserByEmail, getUserByUsername, getCurrentAccount, deleteUser, editUser, getTagExclusion, getAllTags } from '../../api/users'; +import { MePrivate, Tag, UpdateUserInput } from '@looking-for-group/shared'; import { ProfileEditPopup } from '../Profile/ProfileEditPopup'; +import TagDisplay from '../TagDisplay'; +import type { TagDisplayProps, TagOrSkill } from '../TagDisplay'; type JsonData = Record; /** @@ -62,6 +64,21 @@ const Settings = (userProfile: any) => { } }, [dataLoaded, userInfo, navigate, location]); + let tagBlacklist: Tag[] = []; + let tags: Tag[] = []; + + const tabsBlacklist: string[] = [ + "Content Warning", + "Context", + "Game Engine", + "Genre", + "Purpose", + "Style" + ]; + + let tabsBlacklistId: number = 0; + let blacklistSearchValue: string = ""; + // -------------------- // Helper functions // -------------------- @@ -94,6 +111,23 @@ const Settings = (userProfile: any) => { setUserInfo(acc.data); } + const tagBlacklistRes = await getTagExclusion(); + + //Success + if (tagBlacklistRes.status === 200) { + if (tagBlacklistRes.data) { + tagBlacklist = tagBlacklistRes.data; + } + } + + //Not Found + //Not else if so it always checks + if (tagBlacklistRes.status === 404 || !tagBlacklistRes.data) { + tagBlacklist = []; + } + + tags = await allTags(); + // Don't call API again even if user isn't logged in setDataLoaded(true); }; @@ -103,6 +137,78 @@ const Settings = (userProfile: any) => { getUserData(); } + /** + * Converts a Tag[] to a TagOrSkill[] because they're different for some reason + * @param tagArray The Tag[] to cast + * @returns Equivalent TagOrSkill[] + */ + const tagArrayToTagOrSkillArray = (tagArray: Tag[]): TagOrSkill[] => { + let tagOrSkillArray: TagOrSkill[] = []; + + //Theres probably a better way of doing this + for (let i = 0; i < tagArray.length; i++) { + const tagOrSkill: TagOrSkill = { + label: tagArray[i].label, + id: tagArray[i].tagId, + type: tagArray[i].type, + category: tagArray[i].category + }; + + tagOrSkillArray.push(tagOrSkill); + } + + return tagOrSkillArray; + } + + const toggleTagBlacklist = (id: number, type: string) => { + //Look for the tag in the blacklist + let found: boolean = false; + let foundIndex: number = -1; + + for (let i = 0; i < tagBlacklist.length; i++) { + if (tagBlacklist[i].tagId === id) { + found = true; + foundIndex = i; + } + } + + //If it's already in the blacklist, remove it + if (found) { + tagBlacklist.splice(foundIndex, 1); + } + + //If it's not in the blacklist, add it + else { + const tagsIds = tags.map((tag) => tag.tagId); + foundIndex = tagsIds.indexOf(id); + tagBlacklist.push(tags[foundIndex]); + } + } + + /** + * Gets all tags + * Seperate function in case it's needed elsewhere + * @returns Tag[] of all tags + */ + const allTags = async (): Promise => { + const getAllTagsRes = await getAllTags(); + let allTagsData: Tag[] = []; + + //Success + if (getAllTagsRes.status === 200) { + allTagsData = getAllTagsRes.data; + } + + //Internal server error + else if (getAllTagsRes.status === 500) { + console.log(`Internal server error in getAlltags: ${getAllTagsRes}`); + } + + return allTagsData; + } + + let blacklistSearchResults: TagOrSkill[] = tagArrayToTagOrSkillArray(tags); + // -------------------- // Components: // -------------------- @@ -502,7 +608,7 @@ const Settings = (userProfile: any) => { {/* Search bar is not used in settings */}

+
+

Content Restriction

+ Hide content that is mature, graphic, etc. + +
{/* Edit Profile — opens the same editor popup used on the profile page */}
From 3ae972e1bea8631da86747173f0f975dc3bafeec Mon Sep 17 00:00:00 2001 From: person973 Date: Fri, 7 Aug 2026 10:30:48 -0400 Subject: [PATCH 02/10] Changed wording --- client/src/components/pages/NewSettings.tsx | 24 ++++++++++++--------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/client/src/components/pages/NewSettings.tsx b/client/src/components/pages/NewSettings.tsx index 48ee5d49c..dedf15210 100644 --- a/client/src/components/pages/NewSettings.tsx +++ b/client/src/components/pages/NewSettings.tsx @@ -834,16 +834,20 @@ const Settings = (userProfile: any) => {

Content Restriction

- Hide content that is mature, graphic, etc. - + Hide content that you're not interested in. + + {/* Not the right id but it's what I'm basing the menu on */} +
+ +
{/* Edit Profile — opens the same editor popup used on the profile page */} From 10979d91f216ab939f0f61b9bcacacad2598e8bc Mon Sep 17 00:00:00 2001 From: person973 Date: Fri, 7 Aug 2026 11:51:54 -0400 Subject: [PATCH 03/10] Added the rest of the tag viewer, still broken but it shows up now --- client/src/components/pages/NewSettings.tsx | 87 ++++++++++++++++++++- 1 file changed, 83 insertions(+), 4 deletions(-) diff --git a/client/src/components/pages/NewSettings.tsx b/client/src/components/pages/NewSettings.tsx index dedf15210..c0fb1396c 100644 --- a/client/src/components/pages/NewSettings.tsx +++ b/client/src/components/pages/NewSettings.tsx @@ -4,7 +4,7 @@ import { Popup, PopupButton, PopupContent } from '../Popup'; import { ThemeContext } from '../../contexts/ThemeContext'; import { ThemeIcon } from '../ThemeIcon'; import { useNavigate } from 'react-router-dom'; -import { useState, useContext, SetStateAction, useEffect } from 'react'; +import { useState, useContext, SetStateAction, useEffect, useCallback } from 'react'; import { Header } from '../Header'; //import PasswordValidator from 'password-validator'; import ToTopButton from '../ToTopButton'; @@ -14,6 +14,7 @@ import { MePrivate, Tag, UpdateUserInput } from '@looking-for-group/shared'; import { ProfileEditPopup } from '../Profile/ProfileEditPopup'; import TagDisplay from '../TagDisplay'; import type { TagDisplayProps, TagOrSkill } from '../TagDisplay'; +import { SearchBar } from '../SearchBar'; type JsonData = Record; /** @@ -77,7 +78,22 @@ const Settings = (userProfile: any) => { ]; let tabsBlacklistId: number = 0; - let blacklistSearchValue: string = ""; + const [currentTagsTab, setCurrentTagsTab] = useState(0); + + // Filtered results from tag search bar + const [searchedTags, setSearchedTags] = useState([]); + const [blacklistSearchValue, setSearchValue] = useState(""); + + // Category color for each tag tab, matching the tag/filter-tab colors. + const tagTabColors: Record = { + Medium: 'blue', + Genre: 'green', + Style: 'pink', + Purpose: 'orange', + "Content Warning": 'red', //TODO: replace this red (and maybe the orange) with updated colors once those colors are decided + 'Game Engine': 'yellow', + 'Project Type': 'blue' + }; // -------------------- // Helper functions @@ -603,6 +619,18 @@ const Settings = (userProfile: any) => { setThemeOption("Light Mode") }, [theme]) + // Callback for the SearchBar component that updates the displayed tags based on search results. + const handleSearch = useCallback((results: unknown[][]) => { + // setSearchResults(results); + if (results.length === 0 && tags.length !== 0) { + // no results or current data set + setSearchedTags([]); + } + else { + setSearchedTags(results[0]); + } + }, [tags.length, setSearchedTags]); + return (
{/* Search bar is not used in settings */} @@ -837,7 +865,7 @@ const Settings = (userProfile: any) => { Hide content that you're not interested in. {/* Not the right id but it's what I'm basing the menu on */} -
+ {/*
{ all={tagArrayToTagOrSkillArray(tags)} searchValue={blacklistSearchValue} searchData={blacklistSearchResults} - > + /> +
*/} +
From 5f67f633897fdf4f973afb2344b830039119b95d Mon Sep 17 00:00:00 2001 From: person973 Date: Fri, 7 Aug 2026 13:06:59 -0400 Subject: [PATCH 04/10] Added apply button --- client/src/components/pages/NewSettings.tsx | 71 +++++++++++++++------ 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/client/src/components/pages/NewSettings.tsx b/client/src/components/pages/NewSettings.tsx index c0fb1396c..a3ff58ffc 100644 --- a/client/src/components/pages/NewSettings.tsx +++ b/client/src/components/pages/NewSettings.tsx @@ -9,8 +9,8 @@ import { Header } from '../Header'; //import PasswordValidator from 'password-validator'; import ToTopButton from '../ToTopButton'; import * as paths from '../../constants/routes'; -import { getUserByEmail, getUserByUsername, getCurrentAccount, deleteUser, editUser, getTagExclusion, getAllTags } from '../../api/users'; -import { MePrivate, Tag, UpdateUserInput } from '@looking-for-group/shared'; +import { getUserByEmail, getUserByUsername, getCurrentAccount, deleteUser, editUser, getTagExclusion, getAllTags, updateTagExclusion } from '../../api/users'; +import { MePrivate, Tag, UpdateTagBlacklistInput, UpdateUserInput } from '@looking-for-group/shared'; import { ProfileEditPopup } from '../Profile/ProfileEditPopup'; import TagDisplay from '../TagDisplay'; import type { TagDisplayProps, TagOrSkill } from '../TagDisplay'; @@ -225,6 +225,44 @@ const Settings = (userProfile: any) => { let blacklistSearchResults: TagOrSkill[] = tagArrayToTagOrSkillArray(tags); + /** + * Update the blacklist on backend + */ + const buttonBlacklistApplyClicked = async () => { + const updateTagBlacklist: UpdateTagBlacklistInput = { + tagBlacklist: tagBlacklist + }; + + const updateTagExclusionRes = await updateTagExclusion(updateTagBlacklist); + + //201 is success + + //Invalid request + if (updateTagExclusionRes.status === 400) { + console.log(`Invalid request for updateTagExclusionRes: ${updateTagExclusionRes}`); + } + + //Failed/missing authentication + else if (updateTagExclusionRes.status === 401) { + console.log(`Failed/missing authentication for updateTagExclusionRes: ${updateTagExclusionRes}`); + } + + //Not found + else if (updateTagExclusionRes.status === 404) { + console.log(`updateTagExclusionRes not found: ${updateTagExclusionRes}`); + } + + //Conflict + else if (updateTagExclusionRes.status === 409) { + console.log(`updateTagExclusionRes already exists: ${updateTagExclusionRes}`); + } + + //Internal server error + else if (updateTagExclusionRes.status === 500) { + console.log(`Internal server error in updateTagExclusionRes: ${updateTagExclusionRes}`); + } + } + // -------------------- // Components: // -------------------- @@ -864,18 +902,6 @@ const Settings = (userProfile: any) => {

Content Restriction

Hide content that you're not interested in. - {/* Not the right id but it's what I'm basing the menu on */} - {/*
- -
*/}
+
{/* Edit Profile — opens the same editor popup used on the profile page */} @@ -958,7 +991,9 @@ const Settings = (userProfile: any) => {
{/* Popup if user presses delete account to show successful delete action */} - Delete Account + + Delete Account +
{ From f0cc358f969cc54d26f7ba2adfd143f45f44cfa2 Mon Sep 17 00:00:00 2001 From: Kevin Zheng Date: Fri, 7 Aug 2026 13:45:17 -0400 Subject: [PATCH 05/10] Update NewSettings.tsx --- client/src/components/pages/NewSettings.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/components/pages/NewSettings.tsx b/client/src/components/pages/NewSettings.tsx index a3ff58ffc..b21b185f2 100644 --- a/client/src/components/pages/NewSettings.tsx +++ b/client/src/components/pages/NewSettings.tsx @@ -66,7 +66,7 @@ const Settings = (userProfile: any) => { }, [dataLoaded, userInfo, navigate, location]); let tagBlacklist: Tag[] = []; - let tags: Tag[] = []; + const [tags, setTags] = useState([]); const tabsBlacklist: string[] = [ "Content Warning", @@ -142,7 +142,7 @@ const Settings = (userProfile: any) => { tagBlacklist = []; } - tags = await allTags(); + setTags(await allTags()); // Don't call API again even if user isn't logged in setDataLoaded(true); From 052d102bb91cfc85beb2751f3bd4c15333ae6124 Mon Sep 17 00:00:00 2001 From: person973 Date: Fri, 7 Aug 2026 13:50:24 -0400 Subject: [PATCH 06/10] Changed tagBlacklist to useState --- client/src/components/pages/NewSettings.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/components/pages/NewSettings.tsx b/client/src/components/pages/NewSettings.tsx index b21b185f2..21b1e7e71 100644 --- a/client/src/components/pages/NewSettings.tsx +++ b/client/src/components/pages/NewSettings.tsx @@ -65,7 +65,7 @@ const Settings = (userProfile: any) => { } }, [dataLoaded, userInfo, navigate, location]); - let tagBlacklist: Tag[] = []; + const [tagBlacklist, setTagBlacklist] = useState([]); const [tags, setTags] = useState([]); const tabsBlacklist: string[] = [ @@ -132,14 +132,14 @@ const Settings = (userProfile: any) => { //Success if (tagBlacklistRes.status === 200) { if (tagBlacklistRes.data) { - tagBlacklist = tagBlacklistRes.data; + setTagBlacklist(tagBlacklistRes.data); } } //Not Found //Not else if so it always checks if (tagBlacklistRes.status === 404 || !tagBlacklistRes.data) { - tagBlacklist = []; + setTagBlacklist([]); } setTags(await allTags()); From 9139677b69803ab9c9f607e6f8dba98e63cb2781 Mon Sep 17 00:00:00 2001 From: person973 Date: Fri, 7 Aug 2026 14:41:24 -0400 Subject: [PATCH 07/10] Fixed patching --- client/src/api/users.ts | 2 +- client/src/components/pages/NewSettings.tsx | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/client/src/api/users.ts b/client/src/api/users.ts index 866577579..a62e5c40a 100644 --- a/client/src/api/users.ts +++ b/client/src/api/users.ts @@ -439,7 +439,7 @@ export const updateTagExclusion = async ( newBlacklist: UpdateTagBlacklistInput ): Promise> => { const url = `/me/tag-blacklist`; - const response = await POST(url, newBlacklist); + const response = await PATCH(url, newBlacklist); if (response.error) console.log(`Error in updateTagBlacklist: ${response.error}`); //console.log(response); diff --git a/client/src/components/pages/NewSettings.tsx b/client/src/components/pages/NewSettings.tsx index 21b1e7e71..5d5a0cb33 100644 --- a/client/src/components/pages/NewSettings.tsx +++ b/client/src/components/pages/NewSettings.tsx @@ -199,6 +199,8 @@ const Settings = (userProfile: any) => { foundIndex = tagsIds.indexOf(id); tagBlacklist.push(tags[foundIndex]); } + + console.log(tagBlacklist); } /** @@ -235,7 +237,7 @@ const Settings = (userProfile: any) => { const updateTagExclusionRes = await updateTagExclusion(updateTagBlacklist); - //201 is success + //Success is 201 //Invalid request if (updateTagExclusionRes.status === 400) { @@ -261,6 +263,8 @@ const Settings = (userProfile: any) => { else if (updateTagExclusionRes.status === 500) { console.log(`Internal server error in updateTagExclusionRes: ${updateTagExclusionRes}`); } + + window.location.reload(); } // -------------------- @@ -946,7 +950,7 @@ const Settings = (userProfile: any) => { selected={tagArrayToTagOrSkillArray(tagBlacklist)} toggleTag={toggleTagBlacklist} tabs={tabsBlacklist} - tabId={tabsBlacklistId} + tabId={currentTagsTab} all={tagArrayToTagOrSkillArray(tags)} searchValue={blacklistSearchValue} searchData={blacklistSearchResults} From ffb0d6cc6c4b32cb2c005227ce471926e99096cf Mon Sep 17 00:00:00 2001 From: person973 Date: Fri, 7 Aug 2026 16:04:12 -0400 Subject: [PATCH 08/10] (somewhat) Fixed the ui not working --- client/src/components/pages/NewSettings.tsx | 30 ++++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/client/src/components/pages/NewSettings.tsx b/client/src/components/pages/NewSettings.tsx index 5d5a0cb33..383479d50 100644 --- a/client/src/components/pages/NewSettings.tsx +++ b/client/src/components/pages/NewSettings.tsx @@ -13,7 +13,7 @@ import { getUserByEmail, getUserByUsername, getCurrentAccount, deleteUser, editU import { MePrivate, Tag, UpdateTagBlacklistInput, UpdateUserInput } from '@looking-for-group/shared'; import { ProfileEditPopup } from '../Profile/ProfileEditPopup'; import TagDisplay from '../TagDisplay'; -import type { TagDisplayProps, TagOrSkill } from '../TagDisplay'; +import type { TagDisplayProps, TagOrSkill, tagToTagOrSkill } from '../TagDisplay'; import { SearchBar } from '../SearchBar'; type JsonData = Record; @@ -77,7 +77,6 @@ const Settings = (userProfile: any) => { "Style" ]; - let tabsBlacklistId: number = 0; const [currentTagsTab, setCurrentTagsTab] = useState(0); // Filtered results from tag search bar @@ -188,16 +187,32 @@ const Settings = (userProfile: any) => { } } + //The duplicates prevent the ui from getting reversed somehow (deselected makes it filled, selected makes it unfilled) + //Not sure if anyone else had this issue + //If it's already in the blacklist, remove it if (found) { - tagBlacklist.splice(foundIndex, 1); + setTagBlacklist(tagBlacklist => { + const newArray = [...tagBlacklist]; + newArray.splice(foundIndex, 1); + return newArray; + }); + + //tagBlacklist.splice(foundIndex, 1); } //If it's not in the blacklist, add it else { const tagsIds = tags.map((tag) => tag.tagId); foundIndex = tagsIds.indexOf(id); - tagBlacklist.push(tags[foundIndex]); + + setTagBlacklist(tagBlacklist => { + const newArray = [...tagBlacklist]; + newArray.push(tags[foundIndex]); + return newArray; + }); + + //tagBlacklist.push(tags[foundIndex]); } console.log(tagBlacklist); @@ -932,7 +947,7 @@ const Settings = (userProfile: any) => { } }} className={`button-reset medium-tag-tab project-editor-tag-search-tab filter-tab-${tagTabColors[ - type as string] ?? 'grey'} ${tabsBlacklistId === index && + type as string] ?? 'grey'} ${currentTagsTab === index && blacklistSearchValue === "" ? "tag-search-tab-active" : ""}`}> {type} @@ -953,13 +968,14 @@ const Settings = (userProfile: any) => { tabId={currentTagsTab} all={tagArrayToTagOrSkillArray(tags)} searchValue={blacklistSearchValue} - searchData={blacklistSearchResults} + searchData={searchedTags as TagOrSkill[]} />

- + {/* Blocklist */}

Blocklist