ぱっと見ていたところContributorのプロフィールが気になったので色々調整してみました。
アイコンがない
下記のようにContributorsのアイコンが歯抜けになっています。
GitHubのリンクが設定されているContributorはAPIでアイコンを取ってきているようなのですが、それ以外のユーザーはアイコンがありません。なので埋めることにしました。
こんな感じでアイコンがない人用の画像を準備しておき
export const avatars: string[] = [
"1447555445.png",
"1446262260.png",
"1454668491.png",
"1478953496.png",
"1522414407.png",
"1449057230.png",
"1479466248.png",
"1524969274.png",
"1507063429.png",
"1522154395.png",
"1520346636.png",
"1450533682.png",
"1461581760.png",
"1476945442.png",
"1478445108.png",
"1503360665.png",
];
あとはなんやかんやアイコンがない人の数を調べてアイコンを順に使うようにして…
const getDefaultAvatarUrl = useCallback((contributor: Contributor) => {
const index = contributors.findIndex((c) => c.slug === contributor.slug);
const noIconCount = contributors
.slice(0, index)
.filter((c) => !getGitHubIconUrl(c)).length;
const avatarIndex = noIconCount % avatars.length;
return `/images/avatars/${avatars[avatarIndex]}`;
}, []);
ちゃんと全員表示されるようになりました。
プロフィール詳細ページではちゃんと大きく表示されます。
みんなかっこよくなりましたね!!!
GitHubリンクを設定しなければ表示されるのでこのアバターを試したい人はわざと抜いて楽しみにしてみてください。
アイコンをキャッシュ
上記を実装している途中で気づきましたがアイコンを表示するために毎回GitHubのAPIが呼び出されているのでせめてそのセッション中は何度も呼ばないようキャッシュするようにしておきました。ちなみに対応中にuseEffectの無限ループでAPIを呼びすぎてRate Limitにひっかかったのは内緒です
まずアイコン用のカスタムフックを作成しました。キャッシュの保存にはRecoilを使っています。
const avatarUrlsState = atom<{ [key: string]: string }>({
key: "contributors/avatarUrls",
default: {},
});
export const useContributors = () => {
const [avatarUrls, setAvatarUrls] = useRecoilState(avatarUrlsState);
const loadAvatarUrl = useCallback(
async (contributor: Contributor) => {
if (avatarUrls[contributor.slug]) {
return;
}
const githubUrl = getGitHubIconUrl(contributor);
if (!githubUrl) {
setAvatarUrls((avatarUrls) => ({
...avatarUrls,
[contributor.slug]: getDefaultAvatarUrl(contributor),
}));
return;
}
const user = await getGitHubUser({ githubUrl });
setAvatarUrls((avatarUrls) => ({
...avatarUrls,
[contributor.slug]: user.avatar_url as string,
}));
},
[Object.keys(avatarUrls).length]
);
const getAvatarUrl = useCallback(
(contributor?: Contributor) => {
if (!contributor) {
return undefined;
}
return avatarUrls[contributor.slug];
},
[Object.keys(avatarUrls).length]
);
return { getAvatarUrl, loadAvatarUrl };
};
ちなみにみんな思い思いに色々導入しているので_app.tsxはこんな事になっています。Chakra UIを入れようかと思いましたがやめときました。
function MyApp({ Component, pageProps }: Props) {
const { layout = (page) => page } = Component;
return (
<SessionProvider session={pageProps.session}>
<VFXProvider>
<Provider store={store}>
<RecoilRoot>
<StyletronProvider value={styletron}>
<BaseProvider theme={LightTheme}>
<SnowfallLayout>
{layout(<Component {...pageProps} />)}
</SnowfallLayout>
</BaseProvider>
</StyletronProvider>
</RecoilRoot>
</Provider>
</VFXProvider>
</SessionProvider>
);
}
アイコンを表示している箇所はトップの一覧とプロフィール詳細の2箇所ありますので、このカスタムフックを使えばどちらでも同様のキャッシュを利用できるようになりました。全体として各々のユーザーごとに1回までしかAPIを呼ばないようにできました。
relを調整
下記のようになっていたので
<a target="_blank" rel="noreferer noreferrer" href={link.url}>
下記に調整しました。
<a
target="_blank"
rel="nofollow noreferrer noopener me"
href={link.url}
>
まとめ
以上、良いContributionライフを!
↓ マージされたら反映されます。