import { init as initNostrLogin, launch as launchNostrLoginDialog } from 'nostr-login' import React, { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from 'react' import { Link } from 'react-router-dom' import { Banner } from '../components/Banner' import { MetadataController, ZapController } from '../controllers' import { useAppDispatch, useAppSelector, useDidMount } from '../hooks' import { appRoutes } from '../routes' import { setIsAuth, setUser } from '../store/reducers/user' import mainStyles from '../styles//main.module.scss' import navStyles from '../styles/nav.module.scss' import '../styles/popup.css' import { copyTextToClipboard, formatNumber, npubToHex, unformatNumber } from '../utils' import { toast } from 'react-toastify' import { PaymentRequest } from '../types' import { LoadingSpinner } from '../components/LoadingSpinner' import { QRCodeSVG } from 'qrcode.react' import Countdown, { CountdownRenderProps } from 'react-countdown' export const Header = () => { const dispatch = useAppDispatch() const userState = useAppSelector((state) => state.user) useEffect(() => { initNostrLogin({ darkMode: true, localSignup: true, noBanner: true, onAuth: (npub, opts) => { if (opts.type === 'logout') { dispatch(setIsAuth(false)) dispatch(setUser(null)) } else { dispatch(setIsAuth(true)) dispatch( setUser({ npub, pubkey: npubToHex(npub)! }) ) MetadataController.getInstance().then((metadataController) => { metadataController.findMetadata(npub).then((userProfile) => { if (userProfile) { dispatch( setUser({ npub, pubkey: npubToHex(npub)!, ...userProfile }) ) } }) }) } } }) }, [dispatch]) const handleLogin = () => { launchNostrLoginDialog() } return (
Submit Mod Write Settings {!userState.isAuth && ( Login )} {userState.isAuth && userState.user && (
{userState.user.image && ( Profile Avatar )} {userState.user.name || userState.user.displayName || userState.user.npub}
)}
Games Mods About Blog
) } const TipButtonWithDialog = React.memo(() => { const [isOpen, setIsOpen] = useState(false) const [isLoading, setIsLoading] = useState(false) const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('') const [amount, setAmount] = useState(0) const [message, setMessage] = useState('') const [paymentRequest, setPaymentRequest] = useState() const userState = useAppSelector((state) => state.user) const handleClose = useCallback(() => { setPaymentRequest(undefined) setIsLoading(false) setIsOpen(false) }, []) const handleQRExpiry = useCallback(() => { setPaymentRequest(undefined) }, []) const handleAmountChange = (event: React.ChangeEvent) => { const unformattedValue = unformatNumber(event.target.value) setAmount(unformattedValue) } const generatePaymentRequest = useCallback(async (): Promise => { let userHexKey: string setIsLoading(true) setLoadingSpinnerDesc('Getting user pubkey') if (userState.isAuth && userState.user?.pubkey) { userHexKey = userState.user.pubkey as string } else { userHexKey = (await window.nostr?.getPublicKey()) as string } if (!userHexKey) { setIsLoading(false) toast.error('Could not get pubkey') return null } setLoadingSpinnerDesc('Getting admin metadata') const metadataController = await MetadataController.getInstance() const adminMetadata = await metadataController.findAdminMetadata() if (!adminMetadata?.lud16) { setIsLoading(false) toast.error('Lighting address (lud16) is missing in admin metadata!') return null } if (!adminMetadata?.pubkey) { setIsLoading(false) toast.error('pubkey is missing in admin metadata!') return null } const zapController = ZapController.getInstance() setLoadingSpinnerDesc('Creating zap request') return await zapController .getLightningPaymentRequest( adminMetadata.lud16, amount, adminMetadata.pubkey as string, userHexKey, message ) .catch((err) => { toast.error(err.message || err) return null }) .finally(() => { setIsLoading(false) }) }, [amount, message, userState]) const handleSend = useCallback(async () => { const pr = await generatePaymentRequest() if (!pr) return setIsLoading(true) setLoadingSpinnerDesc('Sending payment!') const zapController = ZapController.getInstance() if (await zapController.isWeblnProviderExists()) { await zapController .sendPayment(pr.pr) .then(() => { toast.success(`Successfully sent ${amount} sats!`) handleClose() }) .catch((err) => { toast.error(err.message || err) }) } else { toast.warn('Webln is not present. Use QR code to send zap.') setPaymentRequest(pr) } setIsLoading(false) }, [amount, handleClose, generatePaymentRequest]) const handleGenerateQRCode = async () => { const pr = await generatePaymentRequest() if (!pr) return setPaymentRequest(pr) } return ( <> setIsOpen(true)} > Tip {isOpen && (

Tip/Zap DEG Mods

If you want the development and maintenance of DEG Mods to stop, then a tip helps continue it.

setMessage(e.target.value)} />
{paymentRequest && ( )}
)} {isLoading && } ) }) type PresetAmountProps = { label: string value: number setAmount: Dispatch> } const PresetAmount = React.memo( ({ label, value, setAmount }: PresetAmountProps) => { return ( ) } ) type ZapQRProps = { paymentRequest: PaymentRequest handleClose: () => void handleQRExpiry: () => void } const ZapQR = React.memo( ({ paymentRequest, handleClose, handleQRExpiry }: ZapQRProps) => { useDidMount(() => { ZapController.getInstance() .pollZapReceipt(paymentRequest) .then(() => { toast.success(`Successfully sent sats!`) }) .catch((err) => { toast.error(err.message || err) }) .finally(() => { handleClose() }) }) const onQrCodeClicked = async () => { if (!paymentRequest) return const zapController = ZapController.getInstance() if (await zapController.isWeblnProviderExists()) { zapController.sendPayment(paymentRequest.pr) } else { console.warn('Webln provider not present') const href = `lightning:${paymentRequest.pr}` const a = document.createElement('a') a.href = href a.click() } } return (
) } ) const MAX_POLLING_TIME = 2 * 60 * 1000 // 2 minutes in milliseconds const renderer = ({ minutes, seconds }: CountdownRenderProps) => ( {minutes}:{seconds} ) type TimerProps = { onTimerExpired: () => void } const Timer = React.memo(({ onTimerExpired }: TimerProps) => { const expiryTime = useMemo(() => { return Date.now() + MAX_POLLING_TIME }, []) return (
) })