import * as React from 'react';
import { useEffect, useState } from 'react';
import * as classNames from 'classnames';
import { fetchGet, ResponseError } from '../fetch';
enum MetadataLoadState {
Inactive,
Loading,
Success,
Failed,
}
type Metadata = {
url: string;
title: string;
description: string;
image: string;
expires_at: string | null;
tags: {
name: string;
}[];
};
type Suggestion = {
name: string;
used: boolean;
};
type MetadataPreviewProps = {
link: string;
tags: string[];
onClickTag: (tag: string) => void;
};
const MetadataLoading = () => (
);
const MetadataLoadFailed = () => (
);
export const MetadataPreview: React.FC = ({ link, tags, onClickTag }) => {
const [state, setState] = useState(MetadataLoadState.Inactive);
const [metadata, setMetadata] = useState(null);
useEffect(() => {
if (link.trim() === '' || !/^https?:\/\//.test(link)) {
setState(MetadataLoadState.Inactive);
setMetadata(null);
return;
}
setState(MetadataLoadState.Loading);
fetchGet('/api/checkin/card', { url: link })
.then((response) => {
if (!response.ok) {
throw new ResponseError(response);
}
return response.json();
})
.then((data) => {
setState(MetadataLoadState.Success);
setMetadata(data);
})
.catch(() => {
setState(MetadataLoadState.Failed);
setMetadata(null);
});
}, [link]);
if (state === MetadataLoadState.Inactive) {
return null;
}
const hasImage = metadata !== null && metadata.image !== '';
const descClasses = classNames({
'col-8': hasImage,
'col-12': !hasImage,
});
const tagClasses = (s: Suggestion) =>
classNames({
'list-inline-item': true,
badge: true,
'badge-primary': !s.used,
'badge-secondary': s.used,
'metadata-tag-item': true,
});
const suggestions =
metadata?.tags.map((t) => ({
name: t.name,
used: tags.indexOf(t.name) !== -1,
})) ?? [];
return (
{state === MetadataLoadState.Loading ? (
) : state === MetadataLoadState.Success ? (
{hasImage && (
)}
{metadata?.title}
{suggestions.length > 0 && (
<>
タグ候補
(クリックするとタグ入力欄にコピーできます)
{suggestions.map((tag) => (
- onClickTag(tag.name)}
>
{tag.name}
))}
>
)}
) : (
)}
);
};