import React, { useState, useEffect, Suspense } from 'react' import styled from 'styled-components' import useFetch from 'fetch-suspense' import { Tooltip } from 'react-tippy' import 'react-tippy/dist/tippy.css' import BarLoader from 'react-spinners/BarLoader' import { GoInfo } from 'react-icons/go' import { ExternalLink } from './Links' import { mobile } from '../util/css' const COLORS = { available: '#6e00ff', unavailable: 'darkgrey', error: '#ff388b', } export function Card({ title, children }) { return ( {title} }> {children} ) } export function Repeater({ items = [], moreItems = [], children }) { const [revealAlternatives, setRevealAlternatives] = useState(false) function onClick() { setRevealAlternatives(true) } useEffect(() => { setRevealAlternatives(false) }, [items, moreItems]) return ( <> {items.map((name) => ( {children(name)} ))} {revealAlternatives ? moreItems.map((name) => ( {children(name)} )) : null} {moreItems.length > 0 && !revealAlternatives ? ( ) : null} ) } export function DedicatedAvailability({ name, message = '', service, link, prefix = '', suffix = '', icon, }) { const response = useFetch(`/availability/${service}/${name}`) if (response.error) { throw new Error(`${service}: ${response.error}`) } return ( ) } export function ExistentialAvailability({ name, message = '', target, link, prefix = '', suffix = '', icon, }) { const response = useFetch(target, null, { metadata: true }) if (response.status !== 404 && response.status !== 200) { throw new Error(`${name}: ${response.status}`) } const availability = response.status === 404 return ( ) } export const Result = ({ title, message = '', link, icon, color = 'inherit', prefix = '', suffix = '', }) => { const content = ( <> {prefix} {title} {suffix} ) return ( {icon} {link ? ( {content} ) : ( content )} ) } class ErrorBoundary extends React.Component { constructor(props) { super(props) this.state = { hasError: false } } static getDerivedStateFromError(error) { return { hasError: true, message: error.message } } render() { if (this.state.hasError) { return ( Error ) } return this.props.children } } const CellError = ({ children }) => ( }> {children} ) const CardContainer = styled.div` padding: 40px; ${mobile} { margin-bottom: 40px; padding: 0px; } ` const CardTitle = styled.div` margin-bottom: 15px; font-size: 1rem; font-weight: bold; ${mobile} { padding-left: 20px; } ` const CardContent = styled.div` border-radius: 2px; ${mobile} { padding: 20px; box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1); background: white; border-radius: 0; } ` const Button = styled.div` margin-top: 5px; display: inline-block; padding: 5px 0; border: none; border-bottom: 1px dashed black; cursor: pointer; font-family: monospace; font-size: 0.8em; ` const ResultContainer = styled.div` display: flex; align-items: center; margin-top: 8px; ` const ResultIcon = styled.div` width: 1em; ` const ResultItem = styled.div` display: flex; flex-direction: row; align-items: flex-start; word-break: break-all; color: ${({ color }) => color}; ` const ResultName = styled.div` margin-left: 6px; line-height: 1em; font-family: monospace; font-size: 1rem; a { text-decoration: none; color: inherit; } `