import { Box, Flex, Link, Menu, MenuButton, MenuList, Text, Tooltip } from '@chakra-ui/react'; import dayjs from 'dayjs'; import relativeTimePlugin from 'dayjs/plugin/relativeTime'; import utcPlugin from 'dayjs/plugin/utc'; import { forwardRef } from 'react'; import { Else, If, Then } from 'react-if'; import { useConfig } from '~/context'; import { DynamicIcon } from '~/elements'; import { useColorValue, useOpposingColor } from '~/hooks'; import type { TextProps } from '@chakra-ui/react'; interface ActiveProps { isActive: boolean; } interface MonoFieldProps extends TextProps { v: React.ReactNode; } interface AgeProps extends TextProps { inSeconds: number; } interface WeightProps extends TextProps { weight: number; winningWeight: 'low' | 'high'; } interface ASPathProps { path: number[]; active: boolean; } interface CommunitiesProps { communities: string[]; } interface RPKIStateProps { state: | 0 // Invalid | 1 // Valid | 2 // Unknown | 3; // Unverified active: boolean; } dayjs.extend(relativeTimePlugin); dayjs.extend(utcPlugin); export const MonoField = (props: MonoFieldProps): JSX.Element => { const { v, ...rest } = props; return ( {v} ); }; export const Active = (props: ActiveProps): JSX.Element => { const { isActive } = props; const color = useColorValue(['gray.500', 'green.500'], ['whiteAlpha.300', 'blackAlpha.500']); return ( ); }; export const Age = (props: AgeProps): JSX.Element => { const { inSeconds, ...rest } = props; const now = dayjs.utc(); const then = now.subtract(inSeconds, 'second'); return ( {now.to(then, true)} ); }; export const Weight = (props: WeightProps): JSX.Element => { const { weight, winningWeight, ...rest } = props; const fixMeText = winningWeight === 'low' ? 'Lower Weight is Preferred' : 'Higher Weight is Preferred'; return ( {weight} ); }; export const ASPath = (props: ASPathProps): JSX.Element => { const { path, active } = props; const color = useColorValue( // light: inactive, active ['blackAlpha.500', 'blackAlpha.500'], // dark: inactive, active ['whiteAlpha.600', 'blackAlpha.700'], ); if (path.length === 0) { return ( ); } const paths = [] as JSX.Element[]; path.map((asn, i) => { const asnStr = String(asn); i !== 0 && paths.push( , ); paths.push( // biome-ignore lint/suspicious/noArrayIndexKey: index makes sense in this case. {asnStr} , ); }); return {paths}; }; export const Communities = (props: CommunitiesProps): JSX.Element => { const { communities } = props; const { web } = useConfig(); return ( {communities.join('\n')} ); }; const _RPKIState: React.ForwardRefRenderFunction = ( props: RPKIStateProps, ref, ) => { const { state, active } = props; const { web } = useConfig(); const bg = useColorValue( [ ['red.400', 'green.500', 'yellow.400', 'gray.500'], ['red.500', 'green.500', 'yellow.600', 'gray.600'], ], [ ['red.300', 'green.300', 'yellow.300', 'gray.300'], ['red.500', 'green.600', 'yellow.600', 'gray.800'], ], ); const color = useOpposingColor(bg[+active][state]); const icon = [ { md: 'MdCancel' }, { fa: 'FaCheckCircle' }, { bi: 'BiError' }, { bs: 'BsQuestionCircleFill' }, ] as Record[]; const text = [ web.text.rpkiInvalid, web.text.rpkiValid, web.text.rpkiUnknown, web.text.rpkiUnverified, ]; return ( ); }; export const RPKIState = forwardRef(_RPKIState);