import browser from 'webextension-polyfill' import React, {useState, useCallback, useEffect} from 'react' import {render} from 'react-dom' import {generatePrivateKey, getPublicKey, nip19} from 'nostr-tools' import QRCode from 'react-qr-code' import {removePermissions, PERMISSION_NAMES} from './common' function Options() { let [pubKey, setPubKey] = useState('') let [privKey, setPrivKey] = useState('') let [relays, setRelays] = useState([]) let [newRelayURL, setNewRelayURL] = useState('') let [policies, setPermissions] = useState() let [protocolHandler, setProtocolHandler] = useState(null) let [hidingPrivateKey, hidePrivateKey] = useState(true) let [message, setMessage] = useState('') let [showQR, setShowQR] = useState('') const showMessage = useCallback(msg => { setMessage(msg) setTimeout(setMessage, 3000) }) useEffect(() => { browser.storage.local .get(['private_key', 'relays', 'protocol_handler']) .then(results => { if (results.private_key) { setPrivKey(nip19.nsecEncode(results.private_key)) let hexKey = getPublicKey(results.private_key) let npubKey = nip19.npubEncode(hexKey) setPubKey(npubKey) } if (results.relays) { let relaysList = [] for (let url in results.relays) { relaysList.push({ url, policy: results.relays[url] }) } setRelays(relaysList) } if (results.protocol_handler) { setProtocolHandler(results.protocol_handler) } }) }, []) useEffect(() => { loadPermissions() }, []) async function loadPermissions() { let {policies = {}} = await browser.storage.local.get('policies') let list = [] Object.entries(policies).forEach(([host, accepts]) => { Object.entries(accepts).forEach(([accept, types]) => { Object.entries(types).forEach(([type, {conditions, created_at}]) => { list.push({ host, type, accept: {true: 'allow', false: 'deny'}[accept], conditions, created_at }) }) }) }) setPermissions(list) } return ( <>

nos2x

nostr signer extension

options

preferred relays:
{relays.map(({url, policy}, i) => (
))}
setNewRelayURL(e.target.value)} onBlur={addNewRelay} />
{policies?.length > 0 && ( <>

policies

{policies.map( ({host, type, accept, conditions, created_at}) => ( ) )}
domain permission answer conditions since
{host} {PERMISSION_NAMES[type]} {accept} {JSON.stringify(conditions).slice(1, -1)} {new Date(created_at * 1000) .toISOString() .split('.')[0] .split('T') .join(' ')}
)}

handle{' '} nostr:{' '} links:

{protocolHandler !== null && (
{`
  {hex} = hex pubkey for npub or nprofile, hex event id for note or nevent
  {p_or_e} = "p" for npub or nprofile, "e" for note or nevent
  {u_or_n} = "u" for npub or nprofile, "n" for note or nevent
  {relay0} = first relay in a nprofile or nevent
  {relay1} = second relay in a nprofile or nevent
  {relay2} = third relay in a nprofile or nevent
  {raw} = anything after the colon, i.e. the full nip19 bech32 string
  {hrp} = human-readable prefix of the nip19 string

  examples:
    - https://nostr.guru/{p_or_e}/{hex}
    - https://brb.io/{u_or_n}/{hex}
    - https://notes.blockcore.net/{p_or_e}/{hex}
              `}
)}
{message}
) async function handleKeyChange(e) { let key = e.target.value.toLowerCase().trim() setPrivKey(key) } async function generate() { setPrivKey(nip19.nsecEncode(generatePrivateKey())) } async function saveKey() { if (!isKeyValid()) return let hexOrEmptyKey = privKey try { let {type, data} = nip19.decode(privKey) if (type === 'nsec') hexOrEmptyKey = data } catch (_) {} await browser.storage.local.set({ private_key: hexOrEmptyKey }) if (hexOrEmptyKey !== '') { setPrivKey(nip19.nsecEncode(hexOrEmptyKey)) } showMessage('saved private key!') } function isKeyValid() { if (privKey === '') return true if (privKey.match(/^[a-f0-9]{64}$/)) return true try { if (nip19.decode(privKey).type === 'nsec') return true } catch (_) {} return false } function changeRelayURL(i, ev) { setRelays([ ...relays.slice(0, i), {url: ev.target.value, policy: relays[i].policy}, ...relays.slice(i + 1) ]) } function toggleRelayPolicy(i, cat) { setRelays([ ...relays.slice(0, i), { url: relays[i].url, policy: {...relays[i].policy, [cat]: !relays[i].policy[cat]} }, ...relays.slice(i + 1) ]) } function addNewRelay() { relays.push({ url: newRelayURL, policy: {read: true, write: true} }) setRelays(relays) setNewRelayURL('') } async function handleRevoke(e) { let {host, accept, type} = e.target.dataset if ( window.confirm( `revoke all ${ accept ? 'accept' : 'deny' } ${type} policies from ${host}?` ) ) { await removePermissions(host, accept, type) showMessage('removed policies') loadPermissions() } } async function saveRelays() { await browser.storage.local.set({ relays: Object.fromEntries( relays .filter(({url}) => url.trim() !== '') .map(({url, policy}) => [url.trim(), policy]) ) }) showMessage('saved relays!') } function handleChangeProtocolHandler(e) { if (e.target.type === 'text') setProtocolHandler(e.target.value) else switch (e.target.value) { case 'no': setProtocolHandler(null) break case 'yes': setProtocolHandler('') break } } async function saveNostrProtocolHandlerSettings() { await browser.storage.local.set({protocol_handler: protocolHandler}) showMessage('saved protocol handler!') } } render(, document.getElementById('main'))