awesome-uses/src/components/Person.js

175 lines
5.4 KiB
JavaScript
Raw Normal View History

2020-01-22 17:14:25 +00:00
import React, { useState, useEffect, useRef } from 'react';
2020-01-08 16:25:18 +00:00
import PropTypes from 'prop-types';
2020-01-07 20:23:53 +00:00
import { name } from 'country-emoji';
2023-02-20 20:41:29 +00:00
import { useParams } from '@remix-run/react';
2023-04-14 13:51:15 +00:00
import * as icons from '../util/icons';
2020-01-07 20:23:53 +00:00
2023-02-20 20:41:29 +00:00
export default function Person({ person }) {
2020-01-07 20:23:53 +00:00
const url = new URL(person.url);
2023-04-14 13:51:15 +00:00
const twitter = person.twitter
2024-10-31 21:33:09 +00:00
? `https://unavatar.io/x/${person.twitter.replace('@', '')}`
2023-04-14 13:51:15 +00:00
: null;
2023-01-09 19:05:02 +00:00
const website = `https://unavatar.io/${url.host}`;
2023-04-14 13:51:15 +00:00
const unavatar = person.twitter
? `${twitter}?fallback=${website}&ttl=28d`
: website;
2024-05-28 16:21:20 +00:00
const [_, mastodonHandle, mastodonServer] = person.mastodon?.split('@') || [];
2023-02-20 20:41:29 +00:00
const { tag: currentTag } = useParams();
2020-01-07 20:23:53 +00:00
return (
<div className="PersonWrapper">
2023-02-20 20:41:29 +00:00
<div className="PersonInner">
2020-01-07 22:58:15 +00:00
<header>
2020-01-30 20:04:30 +00:00
<img
width="50"
height="50"
2024-05-28 16:21:20 +00:00
src={unavatar}
2020-01-30 20:04:30 +00:00
alt={person.name}
2023-03-25 01:06:57 +00:00
onError={({ currentTarget }) => {
currentTarget.onerror = null; // prevents looping
2024-05-28 16:21:20 +00:00
currentTarget.src = "/default.png";
2023-03-25 01:06:57 +00:00
}}
2020-01-30 20:04:30 +00:00
loading="lazy"
/>
2020-01-07 21:38:31 +00:00
<h3>
<a href={person.url} target="_blank" rel="noopener noreferrer">
{person.name}
2024-05-28 16:21:20 +00:00
</a>{" "}
{person.emoji}
2020-01-07 21:38:31 +00:00
</h3>
<a
2020-01-10 15:12:12 +00:00
target="_blank"
rel="noopener noreferrer"
2020-01-07 21:38:31 +00:00
className="displayLink"
href={person.url}
2020-01-17 20:40:06 +00:00
>
{url.host}
2024-05-28 16:21:20 +00:00
{url.pathname.replace(/\/$/, "")}
2020-01-17 20:40:06 +00:00
</a>
2020-01-07 21:38:31 +00:00
</header>
2020-01-07 20:23:53 +00:00
<p>{person.description}</p>
2023-02-20 20:41:29 +00:00
<ul className="Tags">
2023-04-14 13:51:15 +00:00
{person.tags.map((tag) => (
<li
2024-05-28 16:21:20 +00:00
className={`Tag small ${tag === currentTag ? "currentTag" : ""}`}
2023-04-14 13:51:15 +00:00
key={tag}
>
2020-01-07 20:23:53 +00:00
{tag}
2023-02-20 20:41:29 +00:00
</li>
2020-01-07 20:23:53 +00:00
))}
2023-02-20 20:41:29 +00:00
</ul>
</div>
<div className="PersonDeets">
2020-01-07 20:23:53 +00:00
<span className="country" title={name(person.country)}>
{person.country}
</span>
{person.computer && (
<span title={`Computer: ${person.computer}`}>
<img
height="40"
src={icons[person.computer]}
alt={person.computer}
/>
</span>
)}
{person.phone && (
<span title={`Uses an ${person.phone}`}>
<img height="50" src={icons[person.phone]} alt={person.phone} />
</span>
)}
2024-05-28 16:21:20 +00:00
{person.twitter && (
<div className="SocialHandle">
<a
href={`https://twitter.com/${person.twitter.replace("@", "")}`}
target="_blank"
2024-11-15 12:27:23 +00:00
rel="noopener noreferrer"
>
2024-05-28 16:21:20 +00:00
<span className="at">@</span>
{person.twitter.replace("@", "")}
</a>
</div>
)}
2024-10-31 21:33:09 +00:00
{/* If they have a bluesky, and no twitter/mastodon, show that */}
{person.bluesky && !person.twitter && (
<div className="SocialHandle">
<a
href={`https://bsky.app/profile/${person.bluesky.replace("@", "")}`}
target="_blank"
rel="noopener noreferrer"
>
<span className="at">@</span>
{person.bluesky.substring(1)}
</a>
</div>
)}
2025-02-14 03:12:56 +00:00
2024-05-28 16:21:20 +00:00
{/* If they have a mastodon, and no twitter, show that */}
2024-10-31 21:33:09 +00:00
{person.mastodon && !person.twitter && !person.bluesky && (
2024-05-23 20:23:09 +00:00
<div className="SocialHandle">
2024-11-15 12:28:05 +00:00
<a
href={`https://${mastodonServer}/@${mastodonHandle}`}
2020-01-07 21:07:32 +00:00
target="_blank"
rel="noopener noreferrer"
>
2020-01-07 20:23:53 +00:00
<span className="at">@</span>
2024-05-28 16:21:20 +00:00
{mastodonHandle}
2020-01-07 20:23:53 +00:00
</a>
2023-02-20 20:41:29 +00:00
</div>
2020-01-07 20:23:53 +00:00
)}
{/* If they have a bluesky, and no mastodon and no twitter, show that */}
{person.bluesky && !person.mastodon && !person.twitter && (
<div className="SocialHandle">
<a href={`https://bsky.app/profile/${person.bluesky}`}
target="_blank"
rel="noopener noreferrer"
>
<span className="at">@</span>
{person.bluesky}
</a>
</div>
)}
2023-02-20 20:41:29 +00:00
</div>
</div>
2020-01-07 20:23:53 +00:00
);
}
2020-01-07 22:58:15 +00:00
2020-01-08 16:25:18 +00:00
Person.propTypes = {
person: PropTypes.shape({
github: PropTypes.string,
name: PropTypes.string,
url: PropTypes.string,
emoji: PropTypes.string,
description: PropTypes.string,
tags: PropTypes.arrayOf(PropTypes.string),
country: PropTypes.string,
2025-02-20 09:46:35 +00:00
computer: PropTypes.oneOf(['apple', 'windows', 'linux', 'bsd']),
2023-02-20 20:41:29 +00:00
phone: PropTypes.oneOf(['iphone', 'android', 'windowsphone', 'flipphone']),
2020-01-08 16:25:18 +00:00
twitter(props, propName, componentName) {
if (!/^@?(\w){1,15}$/.test(props[propName])) {
return new Error(
`Invalid prop \`${propName}\` supplied to` +
2024-05-23 20:23:09 +00:00
` \`${componentName}\`. This isn't a legit Twitter handle.`
);
}
},
mastodon(props, propName, componentName) {
if (!/^@(\w){1,30}@(\w)+\.(\w)+$/.test(props[propName])) {
return new Error(
`Invalid prop \`${propName}\` supplied to` +
` \`${componentName}\`. This isn't a legit Mastodon handle.`
2020-01-08 16:25:18 +00:00
);
}
},
bluesky(props, propName, componentName) {
if (!/^(\w)+\.(\w)+\.(\w)+$/.test(props[propName])) {
return new Error(
`Invalid prop \`${propName}\` supplied to` +
` \`${componentName}\`. This isn't a legit Bluesky handle.`
);
}
},
2020-01-08 16:25:18 +00:00
}),
};