From 5976315595021dd6b9f33ed403681eacafe7c94b Mon Sep 17 00:00:00 2001 From: hzrd149 <github@hzrd149.com> Date: Tue, 11 Apr 2023 07:51:04 -0500 Subject: [PATCH] added slightly better relay picker --- src/components/relay-url-input.tsx | 111 ++++++++++++++++++++++++++--- src/views/login/nip05.tsx | 2 +- src/views/login/npub.tsx | 2 +- src/views/login/nsec.tsx | 2 +- src/views/relays/index.tsx | 2 +- 5 files changed, 105 insertions(+), 14 deletions(-) diff --git a/src/components/relay-url-input.tsx b/src/components/relay-url-input.tsx index 5f896fdf4..350f06cc1 100644 --- a/src/components/relay-url-input.tsx +++ b/src/components/relay-url-input.tsx @@ -1,10 +1,95 @@ -import { Input, InputProps } from "@chakra-ui/react"; +import { + Badge, + Box, + Button, + Flex, + Highlight, + IconButton, + Input, + InputGroup, + InputLeftElement, + InputProps, + InputRightElement, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalHeader, + ModalOverlay, + ModalProps, + Text, + useDisclosure, +} from "@chakra-ui/react"; +import { useState } from "react"; import { useAsync } from "react-use"; import { unique } from "../helpers/array"; +import { RelayIcon, SearchIcon } from "./icons"; + +function RelayPickerModal({ + onSelect, + onClose, + ...props +}: { onSelect: (relay: string) => void } & Omit<ModalProps, "children">) { + const [search, setSearch] = useState(""); + const { value: onlineRelays } = useAsync(async () => + fetch("https://api.nostr.watch/v1/online").then((res) => res.json() as Promise<string[]>) + ); + const { value: paidRelays } = useAsync(async () => + fetch("https://api.nostr.watch/v1/paid").then((res) => res.json() as Promise<string[]>) + ); + const relayList = unique(onlineRelays ?? []); + + const filteredRelays = search ? relayList.filter((url) => url.includes(search)) : relayList; + + return ( + <Modal onClose={onClose} {...props}> + <ModalOverlay /> + <ModalContent> + <ModalHeader>Pick Relay</ModalHeader> + <ModalCloseButton /> + <ModalBody pt="0" px="4" pb="4"> + <InputGroup mb="2"> + <InputLeftElement pointerEvents="none" children={<SearchIcon />} /> + <Input + type="search" + placeholder="Search" + name="relay-search" + value={search} + onChange={(e) => setSearch(e.target.value)} + /> + </InputGroup> + <Flex gap="2" direction="column"> + {filteredRelays.map((url) => ( + <Flex gap="2" alignItems="center"> + <Button + key={url} + value={url} + onClick={() => { + onSelect(url); + onClose(); + }} + variant="outline" + size="sm" + > + {url} + </Button> + {paidRelays?.includes(url) && <Badge colorScheme="green">Paid</Badge>} + </Flex> + ))} + </Flex> + </ModalBody> + </ModalContent> + </Modal> + ); +} export type RelayUrlInputProps = Omit<InputProps, "type">; -export const RelayUrlInput = ({ ...props }: RelayUrlInputProps) => { +export const RelayUrlInput = ({ + onChange, + ...props +}: Omit<RelayUrlInputProps, "onChange"> & { onChange: (url: string) => void }) => { + const { isOpen, onClose, onOpen } = useDisclosure(); const { value: relaysJson } = useAsync(async () => fetch("https://api.nostr.watch/v1/online").then((res) => res.json() as Promise<string[]>) ); @@ -12,14 +97,20 @@ export const RelayUrlInput = ({ ...props }: RelayUrlInputProps) => { return ( <> - <Input list="relay-suggestions" type="url" {...props} /> - <datalist id="relay-suggestions"> - {relaySuggestions.map((url) => ( - <option key={url} value={url}> - {url} - </option> - ))} - </datalist> + <InputGroup> + <Input list="relay-suggestions" type="url" onChange={(e) => onChange(e.target.value)} {...props} /> + <datalist id="relay-suggestions"> + {relaySuggestions.map((url) => ( + <option key={url} value={url}> + {url} + </option> + ))} + </datalist> + <InputRightElement> + <IconButton icon={<RelayIcon />} aria-label="Pick from list" size="sm" onClick={onOpen} /> + </InputRightElement> + </InputGroup> + <RelayPickerModal onClose={onClose} isOpen={isOpen} onSelect={(url) => onChange(url)} size="2xl" /> </> ); }; diff --git a/src/views/login/nip05.tsx b/src/views/login/nip05.tsx index 8e51a9036..92f2d4004 100644 --- a/src/views/login/nip05.tsx +++ b/src/views/login/nip05.tsx @@ -121,7 +121,7 @@ export default function LoginNip05View() { placeholder="wss://nostr.example.com" isRequired value={relayUrl} - onChange={(e) => setRelayUrl(e.target.value)} + onChange={(url) => setRelayUrl(url)} /> <FormHelperText>The first relay to connect to.</FormHelperText> </FormControl> diff --git a/src/views/login/npub.tsx b/src/views/login/npub.tsx index ad511e256..8677d9b99 100644 --- a/src/views/login/npub.tsx +++ b/src/views/login/npub.tsx @@ -46,7 +46,7 @@ export default function LoginNpubView() { placeholder="wss://nostr.example.com" isRequired value={relayUrl} - onChange={(e) => setRelayUrl(e.target.value)} + onChange={(url) => setRelayUrl(url)} /> <FormHelperText>The first relay to connect to.</FormHelperText> </FormControl> diff --git a/src/views/login/nsec.tsx b/src/views/login/nsec.tsx index fd4df736b..c9ea0415e 100644 --- a/src/views/login/nsec.tsx +++ b/src/views/login/nsec.tsx @@ -137,7 +137,7 @@ export default function LoginNsecView() { placeholder="wss://nostr.example.com" isRequired value={relayUrl} - onChange={(e) => setRelayUrl(e.target.value)} + onChange={(url) => setRelayUrl(url)} /> <FormHelperText>The first relay to connect to.</FormHelperText> </FormControl> diff --git a/src/views/relays/index.tsx b/src/views/relays/index.tsx index 68653b6f1..0292934fc 100644 --- a/src/views/relays/index.tsx +++ b/src/views/relays/index.tsx @@ -134,7 +134,7 @@ export default function RelaysView() { <RelayUrlInput id="relay-url-input" value={relayInputValue} - onChange={(e) => setRelayInputValue(e.target.value)} + onChange={(url) => setRelayInputValue(url)} isRequired /> <Button type="submit" isDisabled={saving}>