diff --git a/bun.lock b/bun.lock index c27c08b..9a36ac2 100644 --- a/bun.lock +++ b/bun.lock @@ -30,6 +30,7 @@ "browser-image-compression": "^2.0.2", "dotenv": "^17.2.2", "embla-carousel-react": "^8.6.0", + "facehash": "^0.0.7", "framer-motion": "^12.23.12", "ioredis": "^5.7.0", "pg": "^8.16.3", @@ -710,6 +711,8 @@ "exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="], + "facehash": ["facehash@0.0.7", "", { "peerDependencies": { "@types/react": "", "next": ">=15", "react": ">=18 <20", "react-dom": ">=18 <20" }, "optionalPeers": ["@types/react", "next"] }, "sha512-P4fw6z5DIGMbjtqEaOw7fYvYpQetSOSJOfqy3xuET7cDUI6f9CKlSX0UZIYNrtsPpCoz3LoPP5E8bNbpZBP30A=="], + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], "fast-equals": ["fast-equals@5.4.0", "", {}, "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw=="], diff --git a/package.json b/package.json index c3bac09..2361e11 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "browser-image-compression": "^2.0.2", "dotenv": "^17.2.2", "embla-carousel-react": "^8.6.0", + "facehash": "^0.0.7", "framer-motion": "^12.23.12", "ioredis": "^5.7.0", "pg": "^8.16.3", diff --git a/src/components/player-avatar.tsx b/src/components/player-avatar.tsx new file mode 100644 index 0000000..a8006ca --- /dev/null +++ b/src/components/player-avatar.tsx @@ -0,0 +1,85 @@ +import { Paper, useMantineTheme } from "@mantine/core"; +import { Facehash } from "facehash"; + +interface PlayerAvatarProps { + name?: string; + size?: number; + disableFullscreen?: boolean; + style?: React.CSSProperties; +} + +const PlayerAvatar = ({ + name = "", + size = 35, + disableFullscreen = false, + style, +}: PlayerAvatarProps) => { + const theme = useMantineTheme(); + + const getFacehashSize = (size: number): 32 | 48 | 64 | 80 => { + if (size <= 40) return 32; + if (size <= 56) return 48; + if (size <= 72) return 64; + return 80; + }; + + const facehashSize = getFacehashSize(size); + + const colors = [ + "hsla(314, 100%, 80%, 1)", + "hsla(58, 93%, 72%, 1)", + "hsla(218, 92%, 72%, 1)", + "hsla(19, 99%, 44%, 1)", + "hsla(156, 86%, 40%, 1)", + "hsla(314, 100%, 85%, 1)", + "hsla(58, 92%, 79%, 1)", + "hsla(218, 91%, 78%, 1)", + "hsla(19, 99%, 50%, 1)", + "hsla(156, 86%, 64%, 1)", + ]; + + return ( + { + if (!disableFullscreen) { + e.currentTarget.style.transform = 'scale(1.02)'; + } + }} + onMouseLeave={(e) => { + e.currentTarget.style.transform = 'scale(1)'; + }} + > +
+ +
+
+ ); +}; + +export default PlayerAvatar; diff --git a/src/features/players/components/league-head-to-head.tsx b/src/features/players/components/league-head-to-head.tsx index f8b6b49..449bace 100644 --- a/src/features/players/components/league-head-to-head.tsx +++ b/src/features/players/components/league-head-to-head.tsx @@ -5,7 +5,7 @@ import { useAllPlayerStats } from "../queries"; import { useSheet } from "@/hooks/use-sheet"; import Sheet from "@/components/sheet/sheet"; import PlayerHeadToHeadSheet from "./player-head-to-head-sheet"; -import Avatar from "@/components/avatar"; +import PlayerAvatar from "@/components/player-avatar"; const LeagueHeadToHead = () => { const [player1Id, setPlayer1Id] = useState(null); @@ -89,7 +89,7 @@ const LeagueHeadToHead = () => { {player1Id ? ( <> - + {player1Name} @@ -110,7 +110,7 @@ const LeagueHeadToHead = () => { ) : ( - + Player 1 @@ -145,7 +145,7 @@ const LeagueHeadToHead = () => { {player2Id ? ( <> - + {player2Name} @@ -166,7 +166,7 @@ const LeagueHeadToHead = () => { ) : ( - + Player 2 @@ -241,7 +241,7 @@ const LeagueHeadToHead = () => { }, }} > - + {player.player_name} diff --git a/src/features/players/components/player-list.tsx b/src/features/players/components/player-list.tsx index af50143..ad2cd68 100644 --- a/src/features/players/components/player-list.tsx +++ b/src/features/players/components/player-list.tsx @@ -1,6 +1,6 @@ import { List, ListItem, Skeleton, Text } from "@mantine/core"; import { useNavigate } from "@tanstack/react-router"; -import Avatar from "@/components/avatar"; +import PlayerAvatar from "@/components/player-avatar"; import { Player } from "@/features/players/types"; import { useCallback } from "react"; @@ -29,7 +29,7 @@ const PlayerList = ({ players, loading = false }: PlayerListProps) => { {players?.map((player) => ( } + icon={} style={{ cursor: 'pointer' }} onClick={() => handleClick(player.id)} > diff --git a/src/features/players/components/player-stats-table.tsx b/src/features/players/components/player-stats-table.tsx index f2b9fba..e362c43 100644 --- a/src/features/players/components/player-stats-table.tsx +++ b/src/features/players/components/player-stats-table.tsx @@ -22,7 +22,7 @@ import { InfoIcon, } from "@phosphor-icons/react"; import { PlayerStats } from "../types"; -import Avatar from "@/components/avatar"; +import PlayerAvatar from "@/components/player-avatar"; import { useNavigate } from "@tanstack/react-router"; import { useAllPlayerStats } from "../queries"; @@ -93,7 +93,7 @@ const PlayerListItem = memo(({ stat, onPlayerClick, mmr, onRegisterViewport, onU }} > - + diff --git a/src/features/players/components/profile/header.tsx b/src/features/players/components/profile/header.tsx index ab36c20..9dd8749 100644 --- a/src/features/players/components/profile/header.tsx +++ b/src/features/players/components/profile/header.tsx @@ -4,7 +4,7 @@ import { Flex, Title, ActionIcon, Stack, Button, Box } from "@mantine/core"; import { PencilIcon, FootballHelmetIcon } from "@phosphor-icons/react"; import { useMemo } from "react"; import NameUpdateForm from "./name-form"; -import Avatar from "@/components/avatar"; +import PlayerAvatar from "@/components/player-avatar"; import { useSheet } from "@/hooks/use-sheet"; import { Player } from "../../types"; import PlayerHeadToHeadSheet from "../player-head-to-head-sheet"; @@ -41,7 +41,7 @@ const Header = ({ player }: HeaderProps) => { <> - + {name} diff --git a/vite.config.ts b/vite.config.ts index 2cf6de6..cb47ff5 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,13 +3,13 @@ import { defineConfig } from 'vite' import tsConfigPaths from 'vite-tsconfig-paths' import react from '@vitejs/plugin-react'; -export default defineConfig({ +export default defineConfig(({ mode }) => ({ server: { port: 3000, allowedHosts: ["dev.flexxon.app", "flexxon.app"] }, ssr: { - noExternal: true, + noExternal: mode === 'production' ? true : ['facehash'], }, plugins: [ tsConfigPaths({ @@ -20,4 +20,4 @@ export default defineConfig({ }), react() ] -}) +}))