Skip to content

Commit

Permalink
Merge branch 'main' into faris-extravaganza-2
Browse files Browse the repository at this point in the history
  • Loading branch information
alexzhang1618 authored Mar 31, 2024
2 parents 8b13dcc + a97c090 commit 11f29fa
Show file tree
Hide file tree
Showing 8 changed files with 295 additions and 39 deletions.
2 changes: 1 addition & 1 deletion src/components/events/EventCarousel/style.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
flex-grow: 1;
gap: 1rem;
justify-content: center;
margin: 1rem 0;
margin-top: 1rem;
overflow: hidden;
padding: 2rem 1rem;
text-align: center;
Expand Down
153 changes: 153 additions & 0 deletions src/components/profile/Banner/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { UUID } from '@/lib/types';
import { PublicAttendance } from '@/lib/types/apiResponses';
import { Community } from '@/lib/types/enums';
import { seededRandom, toCommunity } from '@/lib/utils';
import { useEffect, useMemo, useRef, useState } from 'react';
import styles from './style.module.scss';

const communityColors: [Community, string][] = [
[Community.DESIGN, styles.design],
[Community.CYBER, styles.cyber],
[Community.HACK, styles.hack],
[Community.AI, styles.ai],
[Community.GENERAL, styles.general],
];

type PieSlice = {
path: string;
className: string;
};

function computePie(
uuid: UUID,
recentAttendances: PublicAttendance[],
width: number,
height: number
): PieSlice[] {
if (width === 0 && height === 0) {
return [];
}

const hex = uuid.replaceAll('-', '');
const random = seededRandom(
parseInt(hex.slice(0, 8), 16),
parseInt(hex.slice(8, 16), 16),
parseInt(hex.slice(16, 24), 16),
parseInt(hex.slice(24, 32), 16)
);

const communities: Record<Community, number> = {
[Community.HACK]: 0,
[Community.AI]: 0,
[Community.CYBER]: 0,
[Community.DESIGN]: 0,
[Community.GENERAL]: 0,
};
let total = 0;

recentAttendances.forEach(({ event: { committee } }) => {
const community = toCommunity(committee);
communities[community] += 1;
total += 1;
});

const radius = Math.hypot(width / 2, height / 2);
let angle = random() * Math.PI * 2;

return (
communityColors
.map(([community, className]) => {
const portion = communities[community] / total;
if (portion === 0) {
return { path: '', className, portion };
}
if (portion === 1) {
// Draw a circle
return {
path: [
`M ${width / 2 - radius} ${height / 2}`,
`A ${radius} ${radius} 0 0 0 ${width / 2 + radius} ${height / 2}`,
`A ${radius} ${radius} 0 0 0 ${width / 2 - radius} ${height / 2}`,
'z',
].join(''),
className,
portion,
};
}

const endAngle = angle + portion * Math.PI * 2;

const path = [
`M ${width / 2} ${height / 2}`,
`L ${width / 2 + Math.cos(angle) * radius} ${height / 2 + Math.sin(angle) * radius}`,
// A rx ry x-axis-rotation large-arc-flag sweep-flag x y
`A ${radius} ${radius} 0 ${portion > 0.5 ? 1 : 0} 1 ${
width / 2 + Math.cos(endAngle) * radius
} ${height / 2 + Math.sin(endAngle) * radius}`,
'z',
].join('');

angle = endAngle;
return { path, className, portion };
})
// Make the biggest blobs fade in first
.sort((a, b) => b.portion - a.portion)
);
}

interface BannerProps {
uuid: UUID;
recentAttendances: PublicAttendance[];
}

const Banner = ({ uuid, recentAttendances }: BannerProps) => {
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);

const ref = useRef<SVGSVGElement>(null);

useEffect(() => {
const observer = new ResizeObserver(entries => {
const size = entries[0]?.borderBoxSize[0];
if (size) {
setWidth(size.inlineSize);
setHeight(size.blockSize);
}
});
if (ref.current) {
observer.observe(ref.current);
}
return () => {
observer.disconnect();
};
});

const slices = useMemo(
() => computePie(uuid, recentAttendances, width, height),
[uuid, recentAttendances, width, height]
);
const blurRadius = useMemo(() => Math.hypot(width / 2, height / 2) / 4, [width, height]);

return (
<svg ref={ref} className={styles.banner}>
{/* Avoid clipping off the blur: https://stackoverflow.com/a/6556655 */}
<filter id="blur" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation={blurRadius} />
</filter>

{slices.map(({ path, className }, i) =>
path ? (
<path
d={path}
className={`${styles.path} ${className}`}
filter="url(#blur)"
style={{ animationDelay: `${i * 0.7 + 0.5}s` }}
key={className}
/>
) : null
)}
</svg>
);
};

export default Banner;
42 changes: 42 additions & 0 deletions src/components/profile/Banner/style.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@use 'src/styles/vars.scss' as vars;

.banner {
height: 100%;
inset: 0;
position: absolute;
width: 100%;

.path {
animation: fade-in 2s backwards;

@keyframes fade-in {
from {
opacity: 0;
}

to {
opacity: 1;
}
}

&.hack {
fill: vars.$orange-5;
}

&.ai {
fill: vars.$scarlet-4;
}

&.cyber {
fill: vars.$cyan-5;
}

&.design {
fill: vars.$pink-4;
}

&.general {
fill: vars.$blue-5;
}
}
}
16 changes: 16 additions & 0 deletions src/components/profile/Banner/style.module.scss.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export type Styles = {
ai: string;
banner: string;
cyber: string;
design: string;
fadeIn: string;
general: string;
hack: string;
path: string;
};

export type ClassNames = keyof Styles;

declare const styles: Styles;

export default styles;
46 changes: 30 additions & 16 deletions src/components/profile/UserProfilePage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { EditButton, GifSafeImage, Typography } from '@/components/common';
import { EventCarousel } from '@/components/events';
import Banner from '@/components/profile/Banner';
import SocialMediaIcon from '@/components/profile/SocialMediaIcon';
import { UserProgress } from '@/components/profile/UserProgress';
import { config, showToast } from '@/lib';
Expand Down Expand Up @@ -29,8 +30,19 @@ export const UserProfilePage = ({

return (
<div className={styles.profilePage}>
<div className={styles.cardWrapper}>
<div className={styles.banner} />
<div
className={`${styles.cardWrapper} ${recentAttendances.length > 0 ? styles.hasBanner : ''}`}
>
{recentAttendances.length > 0 ? (
<div className={styles.banner}>
{/* Restart the animation when the UUID changes (e.g. navigating from user profile -> my profile) */}
<Banner
uuid={handleUser.uuid}
recentAttendances={recentAttendances}
key={handleUser.uuid}
/>
</div>
) : null}
<div className={styles.profileCard}>
<div className={styles.profilePic}>
<GifSafeImage
Expand Down Expand Up @@ -93,20 +105,22 @@ export const UserProfilePage = ({
<MajorIcon className={styles.icon} />
<Typography variant="h5/regular">{handleUser.major}</Typography>
</div>
<div className={styles.socialIcons}>
{handleUser.userSocialMedia?.map(social => (
<a
href={
social.type === SocialMediaType.EMAIL
? `mailto:${social.url}`
: fixUrl(social.url)
}
key={social.type}
>
<SocialMediaIcon type={social.type} />
</a>
))}
</div>
{handleUser.userSocialMedia && handleUser.userSocialMedia.length > 0 ? (
<div className={styles.socialIcons}>
{handleUser.userSocialMedia.map(social => (
<a
href={
social.type === SocialMediaType.EMAIL
? `mailto:${social.url}`
: fixUrl(social.url)
}
key={social.type}
>
<SocialMediaIcon type={social.type} />
</a>
))}
</div>
) : null}
</div>
<div className={styles.bioSection}>
<Typography variant="h2/bold" className={styles.sectionHeader}>
Expand Down
47 changes: 25 additions & 22 deletions src/components/profile/UserProfilePage/style.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,41 @@
margin: auto;
max-width: vars.$breakpoint-md;
width: 100%;
@media screen and (max-width: vars.$breakpoint-md) {
position: relative;
z-index: 0;
}

.cardWrapper {
padding-top: 6.5rem;
position: relative;
background-color: var(--theme-elevated-background);
border: 1px solid var(--theme-elevated-stroke);
border-radius: 0.75rem;
overflow: hidden;

@media screen and (max-width: vars.$breakpoint-md) {
padding-top: 1.5rem;
&.hasBanner {
@media screen and (max-width: vars.$breakpoint-md) {
margin-top: 8rem;
overflow: unset;
position: relative;
}
}

.banner {
background-color: vars.$pink-2;
border: 1px solid var(--theme-elevated-stroke);
border-bottom: 0;
border-radius: 0.75rem;
height: 100%;
position: absolute;
top: 0;
width: 100%;
background-color: var(--theme-background);
height: 10rem;
position: relative;

@media screen and (max-width: vars.$breakpoint-md) {
border: 0;
border-radius: 0;
margin: -2rem;
width: 100vw;
background-color: var(--theme-elevated-background);
height: auto;
inset: 2rem -2rem;
position: absolute;
top: -10rem;
z-index: -1;
}
}

.profileCard {
background-color: var(--theme-elevated-background);
border: 1px solid var(--theme-elevated-stroke);
border-radius: 0 0 0.75rem 0.75rem;
border-top: 0;
display: flex;
gap: 1rem;
Expand All @@ -46,8 +50,6 @@

@media (max-width: vars.$breakpoint-md) {
align-items: center;
border-radius: 0.75rem;
border-top: 1px solid var(--theme-elevated-stroke);
flex-direction: column;
}

Expand Down Expand Up @@ -137,7 +139,7 @@
display: grid;
gap: 0.56rem;
grid-template-columns: auto 1fr;
margin: 0.81rem 0;
margin-top: 0.81rem;

.icon {
height: 1.313rem;
Expand All @@ -149,6 +151,7 @@
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
margin-top: 0.81rem;

svg {
height: 2rem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type Styles = {
cardWrapper: string;
editWrapper: string;
handle: string;
hasBanner: string;
icon: string;
points: string;
profileCard: string;
Expand Down
Loading

0 comments on commit 11f29fa

Please sign in to comment.