mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-03-26 17:52:18 +01:00
add qr code scanners to signin view
This commit is contained in:
parent
006971409c
commit
59e61844a8
21
src/components/qr-code-scanner-button.tsx
Normal file
21
src/components/qr-code-scanner-button.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { IconButton, Spinner, useDisclosure } from "@chakra-ui/react";
|
||||
import { type QrScannerModalProps } from "./qr-scanner-modal";
|
||||
import { QrCodeIcon } from "./icons";
|
||||
import { Suspense, lazy } from "react";
|
||||
|
||||
const QrScannerModal = lazy(() => import("./qr-scanner-modal"));
|
||||
|
||||
export default function QRCodeScannerButton({ onData }: { onData: QrScannerModalProps["onData"] }) {
|
||||
const modal = useDisclosure();
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton onClick={modal.onOpen} icon={<QrCodeIcon boxSize={6} />} aria-label="Qr Scanner" />
|
||||
{modal.isOpen && (
|
||||
<Suspense fallback={null}>
|
||||
<QrScannerModal isOpen={modal.isOpen} onClose={modal.onClose} onData={onData} />
|
||||
</Suspense>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
@ -2,11 +2,8 @@ import { Button, Modal, ModalBody, ModalContent, ModalFooter, ModalOverlay, Moda
|
||||
import { useState } from "react";
|
||||
import BarcodeScannerComponent from "react-qr-barcode-scanner";
|
||||
|
||||
export default function QrScannerModal({
|
||||
isOpen,
|
||||
onClose,
|
||||
onData,
|
||||
}: { onData: (text: string) => void } & Pick<ModalProps, "isOpen" | "onClose">) {
|
||||
export type QrScannerModalProps = { onData: (text: string) => void } & Pick<ModalProps, "isOpen" | "onClose">;
|
||||
export default function QrScannerModal({ isOpen, onClose, onData }: QrScannerModalProps) {
|
||||
const [stopStream, setStopStream] = useState(false);
|
||||
const handleClose = () => {
|
||||
// Stop the QR Reader stream (fixes issue where the browser freezes when closing the modal) and then dismiss the modal one tick later
|
||||
@ -18,7 +15,7 @@ export default function QrScannerModal({
|
||||
<Modal isOpen={isOpen} onClose={handleClose}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalBody>
|
||||
<ModalBody p="2">
|
||||
<BarcodeScannerComponent
|
||||
stopStream={stopStream}
|
||||
onUpdate={(err, result) => {
|
||||
@ -31,7 +28,7 @@ export default function QrScannerModal({
|
||||
/>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<ModalFooter px="2" pb="2" pt="0">
|
||||
<Button onClick={handleClose}>Cancel</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
@ -1,13 +1,11 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Button, ButtonGroup, Flex, IconButton, Input, Link, useDisclosure } from "@chakra-ui/react";
|
||||
import { Button, ButtonGroup, Flex, IconButton, Input, Link } from "@chakra-ui/react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { SEARCH_RELAYS } from "../../const";
|
||||
import { safeDecode } from "../../helpers/nip19";
|
||||
import { getMatchHashtag } from "../../helpers/regexp";
|
||||
import { CommunityIcon, CopyToClipboardIcon, NotesIcon, QrCodeIcon } from "../../components/icons";
|
||||
import QrScannerModal from "../../components/qr-scanner-modal";
|
||||
import RelaySelectionButton from "../../components/relay-selection/relay-selection-button";
|
||||
import { CommunityIcon, CopyToClipboardIcon, NotesIcon } from "../../components/icons";
|
||||
import RelaySelectionProvider from "../../providers/local/relay-selection-provider";
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import User01 from "../../components/icons/user-01";
|
||||
@ -20,10 +18,10 @@ import PeopleListProvider from "../../providers/local/people-list-provider";
|
||||
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
|
||||
import useRouteSearchValue from "../../hooks/use-route-search-value";
|
||||
import { useBreakpointValue } from "../../providers/global/breakpoint-provider";
|
||||
import QRCodeScannerButton from "../../components/qr-code-scanner-button";
|
||||
|
||||
export function SearchPage() {
|
||||
const navigate = useNavigate();
|
||||
const qrScannerModal = useDisclosure();
|
||||
|
||||
const autoFocusSearch = useBreakpointValue({ base: false, lg: true });
|
||||
|
||||
@ -82,12 +80,10 @@ export function SearchPage() {
|
||||
|
||||
return (
|
||||
<VerticalPageLayout>
|
||||
<QrScannerModal isOpen={qrScannerModal.isOpen} onClose={qrScannerModal.onClose} onData={handleSearchText} />
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Flex gap="2" wrap="wrap">
|
||||
<Flex gap="2" grow={1}>
|
||||
<IconButton onClick={qrScannerModal.onOpen} icon={<QrCodeIcon />} aria-label="Qr Scanner" />
|
||||
<QRCodeScannerButton onData={handleSearchText} />
|
||||
{!!navigator.clipboard?.readText && (
|
||||
<IconButton onClick={readClipboard} icon={<CopyToClipboardIcon />} aria-label="Read clipboard" />
|
||||
)}
|
||||
|
@ -1,5 +1,10 @@
|
||||
import { useState } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import {
|
||||
Alert,
|
||||
AlertDescription,
|
||||
AlertIcon,
|
||||
AlertTitle,
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
CardBody,
|
||||
@ -9,6 +14,7 @@ import {
|
||||
Input,
|
||||
InputGroup,
|
||||
InputRightAddon,
|
||||
Link,
|
||||
LinkBox,
|
||||
Tag,
|
||||
Text,
|
||||
@ -28,6 +34,7 @@ import { useUserMetadata } from "../../../hooks/use-user-metadata";
|
||||
import nostrConnectService from "../../../services/nostr-connect";
|
||||
import accountService from "../../../services/account";
|
||||
import { safeRelayUrls } from "../../../helpers/relay";
|
||||
import { safeJson } from "../../../helpers/parse";
|
||||
|
||||
function ProviderCard({ onClick, provider }: { onClick: () => void; provider: NostrEvent }) {
|
||||
const metadata = JSON.parse(provider.content) as Kind0ParsedContent;
|
||||
@ -68,6 +75,10 @@ export default function LoginNostrAddressCreate() {
|
||||
const providers = useNip05Providers();
|
||||
const [selected, setSelected] = useState<NostrEvent>();
|
||||
const userMetadata = useUserMetadata(selected?.pubkey);
|
||||
const providerMetadata = useMemo<Kind0ParsedContent | undefined>(
|
||||
() => selected && safeJson(selected.content, undefined),
|
||||
[selected],
|
||||
);
|
||||
|
||||
const createAccount: React.FormEventHandler<HTMLDivElement> = async (e) => {
|
||||
e.preventDefault();
|
||||
@ -75,7 +86,6 @@ export default function LoginNostrAddressCreate() {
|
||||
|
||||
try {
|
||||
setLoading("Creating...");
|
||||
const providerMetadata = JSON.parse(selected.content) as Kind0ParsedContent;
|
||||
const metadata: Kind0ParsedContent = { ...userMetadata, ...providerMetadata };
|
||||
if (!metadata.nip05) throw new Error("Provider missing nip05 address");
|
||||
const nip05 = await dnsIdentityService.fetchIdentity(metadata.nip05);
|
||||
@ -109,10 +119,15 @@ export default function LoginNostrAddressCreate() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex gap="2" alignItems="center">
|
||||
<MetadataAvatar metadata={metadata} size="sm" />
|
||||
<Heading size="sm">{metadata.displayName || metadata.name}</Heading>
|
||||
</Flex>
|
||||
<div>
|
||||
<Flex gap="2" alignItems="center">
|
||||
<MetadataAvatar metadata={metadata} size="sm" />
|
||||
<Heading size="sm">{metadata.displayName || metadata.name}</Heading>
|
||||
</Flex>
|
||||
<Link href={providerMetadata?.website || userMetadata?.website} isExternal color="blue.500">
|
||||
{providerMetadata?.website || userMetadata?.website}
|
||||
</Link>
|
||||
</div>
|
||||
<InputGroup>
|
||||
<Input name="name" isRequired value={name} onChange={(e) => setName(e.target.value)} />
|
||||
<InputRightAddon as="button" onClick={() => setSelected(undefined)} cursor="pointer">
|
||||
|
@ -10,6 +10,7 @@ import accountService from "../../../services/account";
|
||||
import { COMMON_CONTACT_RELAY } from "../../../const";
|
||||
import { safeRelayUrls } from "../../../helpers/relay";
|
||||
import { getMatchSimpleEmail } from "../../../helpers/regexp";
|
||||
import QRCodeScannerButton from "../../../components/qr-code-scanner-button";
|
||||
|
||||
export default function LoginNostrAddressView() {
|
||||
const navigate = useNavigate();
|
||||
@ -124,6 +125,7 @@ export default function LoginNostrAddressView() {
|
||||
onChange={(e) => setAddress(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<QRCodeScannerButton onData={(v) => setAddress(v)} />
|
||||
</Flex>
|
||||
</FormControl>
|
||||
{renderStatus()}
|
||||
|
@ -1,9 +1,20 @@
|
||||
import { useState } from "react";
|
||||
import { Button, Flex, FormControl, FormHelperText, FormLabel, Input, Text, useToast } from "@chakra-ui/react";
|
||||
import {
|
||||
Button,
|
||||
Flex,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
IconButton,
|
||||
Input,
|
||||
Text,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import accountService from "../../services/account";
|
||||
import nostrConnectService, { NostrConnectClient } from "../../services/nostr-connect";
|
||||
import QRCodeScannerButton from "../../components/qr-code-scanner-button";
|
||||
|
||||
export default function LoginNostrConnectView() {
|
||||
const navigate = useNavigate();
|
||||
@ -42,15 +53,19 @@ export default function LoginNostrConnectView() {
|
||||
{loading && <Text fontSize="lg">{loading}</Text>}
|
||||
{!loading && (
|
||||
<FormControl>
|
||||
<FormLabel>Connect URI</FormLabel>
|
||||
<Input
|
||||
name="nostr-address"
|
||||
placeholder="bunker://<pubkey>?relay=wss://relay.example.com"
|
||||
isRequired
|
||||
value={uri}
|
||||
onChange={(e) => setUri(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<FormLabel htmlFor="input">Connect URI</FormLabel>
|
||||
<Flex gap="2">
|
||||
<Input
|
||||
id="nostr-connect"
|
||||
name="nostr-connect"
|
||||
placeholder="bunker://<pubkey>?relay=wss://relay.example.com"
|
||||
isRequired
|
||||
value={uri}
|
||||
onChange={(e) => setUri(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<QRCodeScannerButton onData={(v) => setUri(v)} />
|
||||
</Flex>
|
||||
<FormHelperText>A bunker connect URI</FormHelperText>
|
||||
</FormControl>
|
||||
)}
|
||||
|
@ -6,6 +6,7 @@ import { RelayUrlInput } from "../../components/relay-url-input";
|
||||
import { normalizeToHexPubkey } from "../../helpers/nip19";
|
||||
import accountService from "../../services/account";
|
||||
import { COMMON_CONTACT_RELAY } from "../../const";
|
||||
import QRCodeScannerButton from "../../components/qr-code-scanner-button";
|
||||
|
||||
export default function LoginNpubView() {
|
||||
const navigate = useNavigate();
|
||||
@ -31,7 +32,10 @@ export default function LoginNpubView() {
|
||||
<Flex as="form" direction="column" gap="4" onSubmit={handleSubmit} w="full">
|
||||
<FormControl>
|
||||
<FormLabel>Enter user npub</FormLabel>
|
||||
<Input type="text" placeholder="npub1" isRequired value={npub} onChange={(e) => setNpub(e.target.value)} />
|
||||
<Flex gap="2">
|
||||
<Input type="text" placeholder="npub1" isRequired value={npub} onChange={(e) => setNpub(e.target.value)} />
|
||||
<QRCodeScannerButton onData={(v) => setNpub(v)} />
|
||||
</Flex>
|
||||
<FormHelperText>
|
||||
Enter any npub you want.{" "}
|
||||
<Link isExternal href="https://nostr.directory" color="blue.500" target="_blank">
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import { Center } from "@chakra-ui/react";
|
||||
import { Flex } from "@chakra-ui/react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
|
||||
import { Kind0ParsedContent } from "../../helpers/user-metadata";
|
||||
@ -72,8 +72,8 @@ export default function SignupView() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Center w="full" h="full">
|
||||
<Flex direction="column" alignItems="center" gap="2" w="full" px="4" py="10">
|
||||
{renderStep()}
|
||||
</Center>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user