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
|
|
|
|
|
? `https://unavatar.io/${person.twitter.replace('@', '')}`
|
|
|
|
|
: 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 (
|
2023-04-14 13:51:15 +00:00
|
|
|
<div
|
|
|
|
|
className="PersonWrapper"
|
2024-05-28 16:21:20 +00:00
|
|
|
style={{ contentVisibility: "auto", containIntrinsicHeight: "560px" }}
|
2023-04-14 13:51:15 +00:00
|
|
|
>
|
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">
|
2020-01-20 12:31:19 +00:00
|
|
|
{person.name}
|
2024-05-28 16:21:20 +00:00
|
|
|
</a>{" "}
|
2020-01-20 12:31:19 +00:00
|
|
|
{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>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* If they have a mastodon, and no twitter, show that */}
|
|
|
|
|
{person.mastodon && !person.twitter && (
|
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"
|
2024-11-15 12:26:47 +00:00
|
|
|
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
|
|
|
)}
|
2024-11-15 12:23:39 +00:00
|
|
|
|
|
|
|
|
{/* If they have a bluesky, and no mastodon and no twitter, show that */}
|
|
|
|
|
{person.bluesky && !person.mastodon && !person.twitter && (
|
|
|
|
|
<div className="SocialHandle">
|
2024-11-15 12:26:47 +00:00
|
|
|
<a href={`https://bsky.app/profile/${person.bluesky}`}
|
2024-11-15 12:23:39 +00:00
|
|
|
target="_blank"
|
2024-11-15 12:26:47 +00:00
|
|
|
rel="noopener noreferrer"
|
|
|
|
|
>
|
2024-11-15 12:23:39 +00:00
|
|
|
<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),
|
2020-01-10 11:21:38 +00:00
|
|
|
country: PropTypes.string,
|
2020-01-08 16:25:18 +00:00
|
|
|
computer: PropTypes.oneOf(['apple', 'windows', 'linux']),
|
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
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
};
|