team logo compression, play around with style
This commit is contained in:
@@ -32,6 +32,7 @@
|
|||||||
"@tiptap/starter-kit": "^3.4.3",
|
"@tiptap/starter-kit": "^3.4.3",
|
||||||
"@types/bun": "^1.2.22",
|
"@types/bun": "^1.2.22",
|
||||||
"@types/ioredis": "^4.28.10",
|
"@types/ioredis": "^4.28.10",
|
||||||
|
"browser-image-compression": "^2.0.2",
|
||||||
"dotenv": "^17.2.2",
|
"dotenv": "^17.2.2",
|
||||||
"embla-carousel-react": "^8.6.0",
|
"embla-carousel-react": "^8.6.0",
|
||||||
"framer-motion": "^12.23.12",
|
"framer-motion": "^12.23.12",
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ interface HeaderProps extends HeaderConfig {}
|
|||||||
|
|
||||||
const Header = ({ collapsed, title, withBackButton }: HeaderProps) => {
|
const Header = ({ collapsed, title, withBackButton }: HeaderProps) => {
|
||||||
return (
|
return (
|
||||||
<AppShell.Header id='app-header' display={collapsed ? 'none' : 'block'}>
|
<AppShell.Header
|
||||||
|
id='app-header'
|
||||||
|
display={collapsed ? 'none' : 'block'}
|
||||||
|
>
|
||||||
{ withBackButton && <BackButton /> }
|
{ withBackButton && <BackButton /> }
|
||||||
<Flex justify='center' align='center' h='100%' px='md'>
|
<Flex justify='center' align='center' h='100%' px='md'>
|
||||||
<Title order={2}>{title}</Title>
|
<Title order={2}>{title}</Title>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AppShell, ScrollArea, Stack, Group, Paper } from "@mantine/core";
|
import { AppShell, ScrollArea, Stack, Group, Paper, useMantineColorScheme } from "@mantine/core";
|
||||||
import { Link } from "@tanstack/react-router";
|
import { Link } from "@tanstack/react-router";
|
||||||
import { NavLink } from "./nav-link";
|
import { NavLink } from "./nav-link";
|
||||||
import { useIsMobile } from "@/hooks/use-is-mobile";
|
import { useIsMobile } from "@/hooks/use-is-mobile";
|
||||||
@@ -9,11 +9,17 @@ import { memo } from "react";
|
|||||||
const Navbar = () => {
|
const Navbar = () => {
|
||||||
const { user, roles } = useAuth()
|
const { user, roles } = useAuth()
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
|
||||||
const links = useLinks(user?.id, roles);
|
const links = useLinks(user?.id, roles);
|
||||||
|
|
||||||
|
const isDark = colorScheme === 'dark';
|
||||||
|
const borderColor = isDark ? 'var(--mantine-color-dimmed)' : 'black';
|
||||||
|
const boxShadowColor = isDark ? 'var(--mantine-color-dimmed)' : 'black';
|
||||||
|
// boxShadow: `5px 5px ${boxShadowColor}`, borderColor
|
||||||
|
|
||||||
if (isMobile) return (
|
if (isMobile) return (
|
||||||
<Paper component='nav' role='navigation' withBorder radius='lg' h='4rem' w='calc(100% - 1rem)' shadow='sm' pos='fixed' m='0.5rem' bottom='0' style={{ zIndex: 10 }}>
|
<Paper component='nav' role='navigation' withBorder shadow="sm" radius='lg' h='4rem' w='calc(100% - 1.5rem)' pos='fixed' m='0.75rem' bottom='0' style={{ zIndex: 10 }}>
|
||||||
<Group gap='xs' justify='space-around' h='100%' w='100%' px={{ base: 12, sm: 0 }}>
|
<Group gap='xs' justify='space-around' h='100%' w='100%' px={{ base: 12, sm: 0 }}>
|
||||||
{links.map((link) => (
|
{links.map((link) => (
|
||||||
<NavLink key={link.href} {...link} />
|
<NavLink key={link.href} {...link} />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Badge, FileInput, Group, Stack, Text, TextInput } from "@mantine/core";
|
import { FileInput, Stack, TextInput } from "@mantine/core";
|
||||||
import { useForm, UseFormInput } from "@mantine/form";
|
import { useForm, UseFormInput } from "@mantine/form";
|
||||||
import { LinkIcon } from "@phosphor-icons/react";
|
import { LinkIcon } from "@phosphor-icons/react";
|
||||||
import SlidePanel, { SlidePanelField } from "@/components/sheet/slide-panel";
|
import SlidePanel from "@/components/sheet/slide-panel";
|
||||||
import { isNotEmpty } from "@mantine/form";
|
import { isNotEmpty } from "@mantine/form";
|
||||||
import useCreateTeam from "../../hooks/use-create-team";
|
import useCreateTeam from "../../hooks/use-create-team";
|
||||||
import useUpdateTeam from "../../hooks/use-update-team";
|
import useUpdateTeam from "../../hooks/use-update-team";
|
||||||
@@ -13,8 +13,8 @@ import { useCallback } from "react";
|
|||||||
import { TeamInput } from "../../types";
|
import { TeamInput } from "../../types";
|
||||||
import { teamKeys } from "../../queries";
|
import { teamKeys } from "../../queries";
|
||||||
import SongPicker from "./song-picker";
|
import SongPicker from "./song-picker";
|
||||||
import TeamColorPicker from "./color-picker";
|
|
||||||
import PlayersPicker from "./players-picker";
|
import PlayersPicker from "./players-picker";
|
||||||
|
import imageCompression from "browser-image-compression";
|
||||||
|
|
||||||
interface TeamFormProps {
|
interface TeamFormProps {
|
||||||
close: () => void;
|
close: () => void;
|
||||||
@@ -113,9 +113,32 @@ const TeamForm = ({
|
|||||||
|
|
||||||
if (logo && team) {
|
if (logo && team) {
|
||||||
try {
|
try {
|
||||||
|
let processedLogo = logo;
|
||||||
|
|
||||||
|
if (logo.size > 500 * 1024) {
|
||||||
|
const compressionOptions = {
|
||||||
|
maxSizeMB: 0.5,
|
||||||
|
maxWidthOrHeight: 800,
|
||||||
|
useWebWorker: true,
|
||||||
|
fileType: logo.type,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
processedLogo = await imageCompression(logo, compressionOptions);
|
||||||
|
logger.info("image compressed", {
|
||||||
|
originalSize: logo.size,
|
||||||
|
compressedSize: processedLogo.size,
|
||||||
|
reduction: ((logo.size - processedLogo.size) / logo.size * 100).toFixed(1) + "%"
|
||||||
|
});
|
||||||
|
} catch (compressionError) {
|
||||||
|
logger.warn("compression failed, falling back", compressionError);
|
||||||
|
processedLogo = logo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("teamId", team.id);
|
formData.append("teamId", team.id);
|
||||||
formData.append("logo", logo);
|
formData.append("logo", processedLogo);
|
||||||
|
|
||||||
const response = await fetch("/api/teams/upload-logo", {
|
const response = await fetch("/api/teams/upload-logo", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { PKCEState, SpotifyTokenResponse } from './types';
|
import type { SpotifyTokenResponse } from './types';
|
||||||
|
|
||||||
const SPOTIFY_AUTH_BASE = 'https://accounts.spotify.com';
|
const SPOTIFY_AUTH_BASE = 'https://accounts.spotify.com';
|
||||||
const SPOTIFY_SCOPES = [
|
const SPOTIFY_SCOPES = [
|
||||||
|
|||||||
Reference in New Issue
Block a user