Files
channels-seerr/src/components/UserProfile/ProfileHeader/index.tsx
Gauthier 4e48fdf2cb fix: rewrite avatarproxy and CachedImage (#1016)
* fix: rewrite avatarproxy and CachedImage

Avatar proxy was allowing every request to be proxied, no matter the original ressource's origin or
filetype. This PR fixes it be allowing only relevant resources to be cached, i.e. Jellyfin/Emby
images and TMDB images.

fix #1012, #1013

* fix: resolve CodeQL error

* fix: resolve CodeQL error

* fix: resolve review comments

* fix: resolve review comment

* fix: resolve CodeQL error

* fix: update imageproxy path
2024-10-17 21:24:15 +08:00

124 lines
3.9 KiB
TypeScript

import Button from '@app/components/Common/Button';
import CachedImage from '@app/components/Common/CachedImage';
import type { User } from '@app/hooks/useUser';
import { Permission, useUser } from '@app/hooks/useUser';
import defineMessages from '@app/utils/defineMessages';
import { CogIcon, UserIcon } from '@heroicons/react/24/solid';
import Link from 'next/link';
import { useIntl } from 'react-intl';
const messages = defineMessages('components.UserProfile.ProfileHeader', {
settings: 'Edit Settings',
profile: 'View Profile',
joindate: 'Joined {joindate}',
userid: 'User ID: {userid}',
});
interface ProfileHeaderProps {
user: User;
isSettingsPage?: boolean;
}
const ProfileHeader = ({ user, isSettingsPage }: ProfileHeaderProps) => {
const intl = useIntl();
const { user: loggedInUser, hasPermission } = useUser();
const subtextItems: React.ReactNode[] = [
intl.formatMessage(messages.joindate, {
joindate: intl.formatDate(user.createdAt, {
year: 'numeric',
month: 'long',
day: 'numeric',
}),
}),
];
if (hasPermission(Permission.MANAGE_REQUESTS)) {
subtextItems.push(intl.formatMessage(messages.userid, { userid: user.id }));
}
return (
<div className="relative z-40 mt-6 mb-12 lg:flex lg:items-end lg:justify-between lg:space-x-5">
<div className="flex items-end justify-items-end space-x-5">
<div className="flex-shrink-0">
<div className="relative">
<CachedImage
type="avatar"
className="h-24 w-24 rounded-full bg-gray-600 object-cover ring-1 ring-gray-700"
src={user.avatar}
alt=""
width={96}
height={96}
/>
<span
className="absolute inset-0 rounded-full shadow-inner"
aria-hidden="true"
></span>
</div>
</div>
<div className="pt-1.5">
<h1 className="mb-1 flex flex-col sm:flex-row sm:items-center">
<Link
href={
user.id === loggedInUser?.id ? '/profile' : `/users/${user.id}`
}
className="text-overseerr text-lg font-bold hover:to-purple-200 sm:text-2xl"
>
{user.displayName}
</Link>
{user.email && user.displayName.toLowerCase() !== user.email && (
<span className="text-sm text-gray-400 sm:ml-2 sm:text-lg">
({user.email})
</span>
)}
</h1>
<p className="text-sm font-medium text-gray-400">
{subtextItems.reduce((prev, curr) => (
<>
{prev} | {curr}
</>
))}
</p>
</div>
</div>
<div className="justify-stretch mt-6 flex flex-col-reverse space-y-4 space-y-reverse lg:flex-row lg:justify-end lg:space-y-0 lg:space-x-3 lg:space-x-reverse">
{(loggedInUser?.id === user.id ||
(user.id !== 1 && hasPermission(Permission.MANAGE_USERS))) &&
!isSettingsPage ? (
<Link
href={
loggedInUser?.id === user.id
? `/profile/settings`
: `/users/${user.id}/settings`
}
passHref
legacyBehavior
>
<Button as="a">
<CogIcon />
<span>{intl.formatMessage(messages.settings)}</span>
</Button>
</Link>
) : (
isSettingsPage && (
<Link
href={
loggedInUser?.id === user.id ? `/profile` : `/users/${user.id}`
}
passHref
legacyBehavior
>
<Button as="a">
<UserIcon />
<span>{intl.formatMessage(messages.profile)}</span>
</Button>
</Link>
)
)}
</div>
</div>
);
};
export default ProfileHeader;