Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d8f8e47
Initial commit.
gorDaChris Sep 24, 2024
58fd351
Fixed no-show preview on hover.
gorDaChris Oct 1, 2024
a513fe0
Prettify Code: src/web/src/Constants.ts
actions-user Oct 1, 2024
b4bb54c
Update Constants.ts
Kagiri2 Oct 1, 2024
3f78cec
Created filters popup on button press.
gorDaChris Oct 11, 2024
da48024
Merge branch 'admin-edit-posts' of https://github.com/shimupan/lineup…
gorDaChris Oct 11, 2024
6f9531e
Update to valid filter options.
gorDaChris Oct 15, 2024
1afc069
Filters are now set conditionally (Valorant vs CS2)
gorDaChris Oct 20, 2024
2649df1
Bring filters from Popup to ProfilePage.
gorDaChris Oct 20, 2024
0019dd3
Profile posts now update on submission of filters. Posts displayed va…
gorDaChris Oct 29, 2024
ca4372f
Update posts based on selected game. Fixed bugs with filter list comp…
gorDaChris Nov 1, 2024
2c32059
Updated filters UI, moved to same div as Valorant and CS2.
gorDaChris Nov 8, 2024
4465187
fixed merge conflicts
Kagiri2 Nov 12, 2024
e452ab2
Prettify Code: src/web/src/Components/auth/FiltersPopup.tsx
actions-user Nov 12, 2024
046b9ed
fixed merge conflicts
Kagiri2 Nov 12, 2024
af11467
fixed merge conflicts
Kagiri2 Nov 12, 2024
887f39a
Slight touch up to filters modal popup. Added primitive ProfileSearch…
gorDaChris Nov 15, 2024
5596aec
Merge branch 'admin-edit-posts' of https://github.com/shimupan/lineup…
gorDaChris Nov 15, 2024
a3bd6c6
Prettify Code: src/web/src/Components.ts
actions-user Nov 15, 2024
5b506c8
Added profile banner and image to pfp.
gorDaChris Dec 6, 2024
16d2ee3
Merge branch 'admin-edit-posts' of https://github.com/shimupan/lineup…
gorDaChris Dec 6, 2024
2662460
Prettify Code: src/web/src/Pages/user/ProfileSearch.tsx
actions-user Dec 6, 2024
88c41bc
Added nav bar under profile banner. Added hover animations.
gorDaChris Dec 6, 2024
c556118
Merge branch 'admin-edit-posts' of https://github.com/shimupan/lineup…
gorDaChris Dec 6, 2024
3d0a7b7
Prettify Code: src/web/src/Constants.ts
actions-user Dec 6, 2024
1860388
Update to layout of page. Changed text size, adding padding and weights.
gorDaChris Dec 7, 2024
4c431ed
Updated layout for Current Rating section to be more accurate.
gorDaChris Dec 7, 2024
6b14d65
Prettify Code: src/web/src/Pages/user/ProfileSearch.tsx
actions-user Dec 7, 2024
4f371c9
Update to Overview section with vertical lines and more stats.
gorDaChris Dec 7, 2024
5daf658
Prettify Code: src/web/src/Pages/user/ProfileSearch.tsx
actions-user Dec 7, 2024
f5f30b1
Added second line of stats for Overview section.
gorDaChris Dec 7, 2024
e96bcd2
Merge branch 'admin-edit-posts' of https://github.com/shimupan/lineup…
gorDaChris Dec 7, 2024
67a3fed
Prettify Code: src/web/src/Pages/user/ProfileSearch.tsx
actions-user Dec 7, 2024
f5363c1
Finalized Competitive Overview section.
gorDaChris Dec 7, 2024
b44b99e
Prettify Code: src/web/src/Pages/user/ProfileSearch.tsx
actions-user Dec 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/web/src/Components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,6 @@ export { default as FollowingSideNav } from './Components/global/sidebar/Followi
export { default as PostPageSkeleton } from './Components/post/PostPageSkeleton';
export { default as LeaderboardPosition } from './Components/profile/LeaderboardPosition';
export { default as VerificationMessage } from './Components/profile/VerificationMessage';
export { default as FiltersPopup } from './Components/auth/FiltersPopup';
export { default as MobileComments } from './Components/post/MobileComments';
export { default as ZoomableImage } from './Components/post/ZoomableImage';
198 changes: 198 additions & 0 deletions src/web/src/Components/auth/FiltersPopup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import React, { useState } from 'react';

interface FiltersPopupProps {
onClose: () => void;
selectedGame: string;
onSubmit: (filters: Filters) => void;
}

interface Filters {
[key: string]: string[];
}

type FilterCategories =
| 'mapName'
| 'teamSide'
| 'grenadeType'
| 'jumpThrow'
| 'valorantAgent';

const initialFilters: Filters = {
teamSide: [],
mapName: [],
grenadeType: [],
jumpThrow: [],
valorantAgent: [],
};

const FiltersPopup: React.FC<FiltersPopupProps> = ({
onClose,
selectedGame,
onSubmit,
}) => {
const [filters, setFilters] = useState<Filters>(initialFilters);

// Add value in FilterCategory only if it is not yet toggled
const toggleCheckbox = (category: FilterCategories, value: string) => {
setFilters((prevFilters) => {
const currVal = prevFilters[category];
if (currVal.includes(value)) {
return {
...prevFilters,
[category]: currVal.filter((v) => v !== value),
};
} else {
return {
...prevFilters,
[category]: [...currVal, value],
};
}
});
};

// Format each checkbox - e.g. hover effects
const renderCheckboxes = (
title: string,
category: FilterCategories,
options: string[],
) => (
<div>
<h3 className="font-semibold">{title}</h3>
{options.map((option) => (
<label
key={option}
className="flex items-center mb-2 cursor-pointer"
>
<input
type="checkbox"
checked={filters[category].includes(option)}
onChange={() => toggleCheckbox(category, option)}
className="appearance-none h-4 w-4 border border-gray-300 rounded checked:bg-blue-500 checked:border-transparent focus:outline-none mr-2 cursor-pointer hover:border-blue-500"
/>
<span className="text-gray-700">{option}</span>
</label>
))}
</div>
);

const pressedSubmit = () => {
onSubmit(filters);
onClose();
};

return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50 rounded-xl">
<div className="relative w-full max-w-sm mx-auto bg-white rounded-xl shadow-lg text-gray-800 overflow-hidden">
<div className="flex items-center justify-between p-4 border-b">
<h2 className="text-lg font-semibold">Filters</h2>
<button
className="p-2 rounded-full hover:bg-gray-200"
onClick={onClose}
>
<svg
className="w-5 h-5 text-gray-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
<div className="max-h-96 overflow-y-auto p-4">
{selectedGame === 'Valorant' && (
<>
{renderCheckboxes('Side', 'teamSide', [
'Attacker',
'Defender',
])}
{renderCheckboxes('Map', 'mapName', [
'abyss',
'ascent',
'bind',
'breeze',
'fracture',
'haven',
'icebox',
'lotus',
'pearl',
'split',
'sunset',
])}
{renderCheckboxes('Agent', 'valorantAgent', [
'Brimstone',
'Phoenix',
'Sage',
'Sova',
'Viper',
'Cypher',
'Reyna',
'Killjoy',
'Breach',
'Omen',
'Jett',
'Raze',
'Skye',
'Yoru',
'Astra',
'KAY/O',
'Chamber',
'Neon',
'Fade',
'Harbor',
'Gekko',
'Deadlock',
'Iso',
'Clove',
'Vyse',
])}
</>
)}
{selectedGame === 'CS2' && (
<>
{renderCheckboxes('Side', 'teamSide', ['T', 'CT'])}
{renderCheckboxes('Map', 'mapName', [
'mirage',
'inferno',
'nuke',
'overpass',
'vertigo',
'ancient',
'anubis',
'dust2',
])}
{renderCheckboxes('Grenade', 'grenadeType', [
'he',
'smoke',
'flashbangs',
'decoy',
'molotov',
'incendiary',
])}
{renderCheckboxes('Jumpthrow?', 'jumpThrow', [
'YES',
'NO',
])}
</>
)}
</div>
<div className="p-4 border-t">
<button
onClick={pressedSubmit}
className="w-full p-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
Submit
</button>
</div>
</div>
</div>
);
};

export default FiltersPopup;
122 changes: 120 additions & 2 deletions src/web/src/Pages/user/ProfilePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
UnapprovedPostsPopup,
LeaderboardPosition,
VerificationMessage,
FiltersPopup,
} from '../../Components';
import { getUserByUsername } from '../../util/getUser';
import { follow } from '../../util/followStatus';
Expand Down Expand Up @@ -54,12 +55,18 @@ const ProfilePage = () => {
});
const [loading, setLoading] = useState(true);
const [showFollowerPopup, setShowFollowerPopup] = useState(false);
const [showFiltersPopup, setShowFiltersPopup] = useState(false);
const [showFollowingPopup, setShowFollowingPopup] = useState(false);
const [followingCount, setFollowingCount] = useState(0);
const [following, setFollowing] = useState<Set<string>>();
const [followerCount, setFollowerCount] = useState(0);
const [followers, setFollowers] = useState<Set<string>>();
const [posts, setPosts] = useState<PostType[][]>([[]]);
// Will be modified when filters change
const [ValorantGlobalPosts, setValorantGlobalPosts] = useState<PostType[]>(
[],
);
const [CSGlobalPosts, setCSGlobalPosts] = useState<PostType[]>([]);
const [open, setOpen] = useState(false);
const [selectedTab, setSelectedTab] = useState('Posts');
const Auth = useContext(AuthContext);
Expand All @@ -79,7 +86,7 @@ const ProfilePage = () => {

// Gets called twice during dev mode
// So there should be 2 error messages
// If you search for an non exisitant user
// If you search for an non-existent user
useEffect(() => {
// Fetch Users
if (!Auth && !id) {
Expand Down Expand Up @@ -133,6 +140,8 @@ const ProfilePage = () => {
.slice(2 * numGames)
.map((response) => response.data);

setCSGlobalPosts(allPosts[0]);
setValorantGlobalPosts(allPosts[1]);
setPosts(allPosts);
setUnapprovedPosts(unapprovedPosts.flat());
setSavedPosts(savedPosts.flat());
Expand Down Expand Up @@ -225,6 +234,71 @@ const ProfilePage = () => {
}
};

const handleFiltersSubmit = (filters: any) => {
// Go through all posts, then check each filter for each post
// As we parse, we only add posts that match the filters to a subset
if (selectedGame === 'CS2') {
var csFilteredSubset = [];
for (
let post_index = 0;
post_index < CSGlobalPosts.length;
post_index++
) {
if (filters.teamSide.includes(CSGlobalPosts[post_index].teamSide)) {
csFilteredSubset.push(CSGlobalPosts[post_index]);
} else if (
filters.mapName.includes(CSGlobalPosts[post_index].mapName)
) {
csFilteredSubset.push(CSGlobalPosts[post_index]);
} else if (
filters.grenadeType.includes(
CSGlobalPosts[post_index].grenadeType,
)
) {
csFilteredSubset.push(CSGlobalPosts[post_index]);
} else if (
(filters.jumpThrow.includes('YES') &&
CSGlobalPosts[post_index].jumpThrow) ||
(filters.jumpThrow.includes('NO') &&
!CSGlobalPosts[post_index].jumpThrow)
) {
csFilteredSubset.push(CSGlobalPosts[post_index]);
}
// Update state of posts with filtered subset
const updatedPosts = [csFilteredSubset, ValorantGlobalPosts];
setPosts(updatedPosts);
}
} else if (selectedGame === 'Valorant') {
var valorantFilteredSubset = [];
for (
let post_index = 0;
post_index < ValorantGlobalPosts.length;
post_index++
) {
if (
filters.teamSide.includes(
ValorantGlobalPosts[post_index].teamSide,
)
) {
valorantFilteredSubset.push(ValorantGlobalPosts[post_index]);
} else if (
filters.mapName.includes(ValorantGlobalPosts[post_index].mapName)
) {
valorantFilteredSubset.push(ValorantGlobalPosts[post_index]);
} else if (
filters.valorantAgent.includes(
ValorantGlobalPosts[post_index].valorantAgent,
)
) {
valorantFilteredSubset.push(ValorantGlobalPosts[post_index]);
}
}
// Update state of posts with filtered subset
const updatedPosts = [CSGlobalPosts, valorantFilteredSubset];
setPosts(updatedPosts);
}
};

if (loading) return <Loading />;

return (
Expand Down Expand Up @@ -311,6 +385,18 @@ const ProfilePage = () => {
{Auth?.username === user.username && (
<>
<div className="flex items-center justify-center space-x-4">
<button
className="flex items-center justify-center px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-full transition duration-300 ease-in-out" // Made padding consistent with the first button
onClick={() =>
navigate(
`/manage-posts/${Auth?.username}`,
)
}
>
<div className="flex text-center items-center gap-x-1">
Manage Posts
</div>
</button>
{/* <button
className="flex items-center justify-center px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-full transition duration-300 ease-in-out" // Adjusted padding to match
onClick={() => setOpen(!open)}
Expand Down Expand Up @@ -399,11 +485,35 @@ const ProfilePage = () => {
<button
key={game}
onClick={() => setSelectedGame(game)}
className="group group-hover:before:duration-500 group-hover:after:duration-500 after:duration-500 hover:border-rose-300 hover:before:[box-shadow:_20px_20px_20px_30px_#a21caf] duration-500 before:duration-500 hover:duration-500 underline underline-offset-2 hover:after:-right-8 hover:before:right-12 hover:before:-bottom-8 hover:before:blur hover:underline hover:underline-offset-4 origin-left hover:decoration-2 hover:text-rose-300 relative bg-neutral-800 h-16 w-64 border text-left p-3 text-gray-50 text-base font-bold rounded-lg overflow-hidden before:absolute before:w-12 before:h-12 before:content[''] before:right-1 before:top-1 before:z-10 before:bg-violet-500 before:rounded-full before:blur-lg after:absolute after:z-10 after:w-20 after:h-20 after:content[''] after:bg-rose-300 after:right-8 after:top-3 after:rounded-full after:blur-lg"
className="group group-hover:before:duration-500 group-hover:after:duration-500 after:duration-500 hover:border-rose-300
hover:before:[box-shadow:_20px_20px_20px_30px_#a21caf] duration-500 before:duration-500 hover:duration-500 underline underline-offset-2
hover:after:-right-8 hover:before:right-12 hover:before:-bottom-8 hover:before:blur hover:underline hover:underline-offset-4 origin-left
hover:decoration-2 hover:text-rose-300 relative bg-neutral-800 h-16 w-64 border text-left p-3 text-gray-50 text-base font-bold rounded-lg overflow-hidden
before:absolute before:w-12 before:h-12 before:content[''] before:right-1 before:top-1 before:z-10 before:bg-violet-500 before:rounded-full before:blur-lg
after:absolute after:z-10 after:w-20 after:h-20 after:content[''] after:bg-rose-300 after:right-8 after:top-3 after:rounded-full after:blur-lg"
>
{game}
</button>
))}
<button
onClick={() => {
setShowFiltersPopup(true);
}}
className="flex text-blue-700 hover:text-white border border-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center me-2 mb-2 dark:border-blue-500 dark:text-blue-500 dark:hover:text-white dark:hover:bg-blue-500 dark:focus:ring-blue-800"
>
<svg
className="w-6 h-6"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M5.05 3C3.291 3 2.352 5.024 3.51 6.317l5.422 6.059v4.874c0 .472.227.917.613 1.2l3.069 2.25c1.01.742 2.454.036 2.454-1.2v-7.124l5.422-6.059C21.647 5.024 20.708 3 18.95 3H5.05Z" />
</svg>
Filters
</button>
</div>
{GAMES.map((game, index) => {
if (game === selectedGame) {
Expand Down Expand Up @@ -603,6 +713,14 @@ const ProfilePage = () => {
onClose={() => setUnapprovedPostsPopup(false)}
/>
)}

{showFiltersPopup && (
<FiltersPopup
onClose={() => setShowFiltersPopup(false)}
selectedGame={selectedGame}
onSubmit={handleFiltersSubmit}
/>
)}
</>
);
};
Expand Down