From 19bdfe74d02d23ed99b6de1d734c584ac254d391 Mon Sep 17 00:00:00 2001 From: thatmattlove Date: Sun, 24 Mar 2024 16:50:31 -0400 Subject: [PATCH] fix PeeringDB link rendering --- hyperglass/models/config/params.py | 14 ++++- hyperglass/ui/components/footer/footer.tsx | 63 +++++++++------------- hyperglass/ui/types/globals.d.ts | 3 ++ 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/hyperglass/models/config/params.py b/hyperglass/models/config/params.py index 4375ce76..6d67ae85 100644 --- a/hyperglass/models/config/params.py +++ b/hyperglass/models/config/params.py @@ -3,9 +3,10 @@ # Standard Library import typing as t from pathlib import Path +import urllib.parse # Third Party -from pydantic import Field, ConfigDict, ValidationInfo, field_validator +from pydantic import Field, ConfigDict, ValidationInfo, field_validator, HttpUrl # Project from hyperglass.settings import Settings @@ -108,6 +109,17 @@ def validate_plugins(cls: "Params", value: t.List[str]) -> t.List[str]: return [str(f) for f in matching_plugins] return [] + @field_validator("web", mode="after") + @classmethod + def validate_web(cls, web: Web, info: ValidationInfo) -> Web: + """String-format Link URLs.""" + for link in web.links: + url = urllib.parse.unquote(str(link.url), encoding="utf-8", errors="replace").format( + primary_asn=info.data.get("primary_asn", "65000") + ) + link.url = HttpUrl(url) + return web + def common_plugins(self) -> t.Tuple[Path, ...]: """Get all validated external common plugins as Path objects.""" return tuple(Path(p) for p in self.plugins) diff --git a/hyperglass/ui/components/footer/footer.tsx b/hyperglass/ui/components/footer/footer.tsx index 006aa048..17d3fea4 100644 --- a/hyperglass/ui/components/footer/footer.tsx +++ b/hyperglass/ui/components/footer/footer.tsx @@ -2,7 +2,7 @@ import { Flex, HStack, useToken } from '@chakra-ui/react'; import { useMemo } from 'react'; import { useConfig } from '~/context'; import { DynamicIcon } from '~/elements'; -import { useBreakpointValue, useColorValue, useMobile, useStrf } from '~/hooks'; +import { useBreakpointValue, useColorValue, useMobile } from '~/hooks'; import { isLink, isMenu } from '~/types'; import { FooterButton } from './button'; import { ColorModeToggle } from './color-mode'; @@ -11,7 +11,9 @@ import { FooterLink } from './link'; import type { ButtonProps, LinkProps } from '@chakra-ui/react'; import type { Link, Menu } from '~/types'; -function buildItems(links: Link[], menus: Menu[]): [(Link | Menu)[], (Link | Menu)[]] { +type MenuItems = (Link | Menu)[]; + +function buildItems(links: Link[], menus: Menu[]): [MenuItems, MenuItems] { const leftLinks = links.filter(link => link.side === 'left'); const leftMenus = menus.filter(menu => menu.side === 'left'); const rightLinks = links.filter(link => link.side === 'right'); @@ -22,8 +24,23 @@ function buildItems(links: Link[], menus: Menu[]): [(Link | Menu)[], (Link | Men return [left, right]; } +const LinkOnSide = (props: { item: ArrayElement; side: 'left' | 'right' }) => { + const { item, side } = props; + if (isLink(item)) { + const icon: Partial = {}; + + if (item.showIcon) { + icon.rightIcon = ; + } + return ; + } + if (isMenu(item)) { + return ; + } +}; + export const Footer = (): JSX.Element => { - const { web, content, primaryAsn } = useConfig(); + const { web, content } = useConfig(); const footerBg = useColorValue('blackAlpha.50', 'whiteAlpha.100'); const footerColor = useColorValue('black', 'white'); @@ -34,8 +51,6 @@ export const Footer = (): JSX.Element => { const [left, right] = useMemo(() => buildItems(web.links, web.menus), [web.links, web.menus]); - const strF = useStrf(); - return ( { overflowY={{ base: 'auto', lg: 'unset' }} justifyContent={{ base: 'center', lg: 'space-between' }} > - {left.map(item => { - if (isLink(item)) { - const url = strF(item.url, { primaryAsn }, '/'); - const icon: Partial = {}; - - if (item.showIcon) { - icon.rightIcon = ; - } - return ; - } - if (isMenu(item)) { - return ( - - ); - } - })} + {left.map(item => ( + + ))} {!isMobile && } - {right.map(item => { - if (isLink(item)) { - const url = strF(item.url, { primaryAsn }, '/'); - const icon: Partial = {}; - - if (item.showIcon) { - icon.rightIcon = ; - } - return ; - } - if (isMenu(item)) { - return ( - - ); - } - })} + {right.map(item => ( + + ))} {web.credit.enable && ( = Record & Omit; + type ArrayElement = + ArrayType extends readonly (infer ElementType)[] ? ElementType : never; + type RPKIState = 0 | 1 | 2 | 3; type ResponseLevel = 'success' | 'warning' | 'error' | 'danger';