consolidate save buttons

This commit is contained in:
monlovesmango
2023-07-30 20:19:38 -05:00
committed by fiatjaf_
parent 20ffa75f19
commit 06f1167ff1

View File

@@ -10,15 +10,19 @@ function Options() {
let [privKey, setPrivKey] = useState('')
let [relays, setRelays] = useState([])
let [newRelayURL, setNewRelayURL] = useState('')
let [policies, setPermissions] = useState()
let [protocolHandler, setProtocolHandler] = useState(null)
let [policies, setPermissions] = useState([])
let [protocolHandler, setProtocolHandler] = useState('')
let [hidingPrivateKey, hidePrivateKey] = useState(true)
let [showNotifications, setNotifications] = useState(false)
let [message, setMessage] = useState('')
let [messages, setMessages] = useState([])
let [handleNostrLinks, setHandleNostrLinks] = useState(false)
let [showProtocolHandlerHelp, setShowProtocolHandlerHelp] = useState(false)
let [unsavedChanges, setUnsavedChanges] = useState([])
const showMessage = useCallback(msg => {
setMessage(msg)
setTimeout(setMessage, 3000)
messages.push(msg)
setMessages(messages)
setTimeout(() => setMessages([]), 3000)
})
useEffect(() => {
@@ -40,6 +44,8 @@ function Options() {
}
if (results.protocol_handler) {
setProtocolHandler(results.protocol_handler)
setHandleNostrLinks(true)
setShowProtocolHandlerHelp(false)
}
if (results.notifications) {
setNotifications(true)
@@ -74,65 +80,32 @@ function Options() {
return (
<>
<h1>nos2x</h1>
<p>nostr signer extension</p>
<h2>options</h2>
<div style={{marginBottom: '10px'}}>
<div style={{display: 'flex', alignItems: 'center'}}>
<span>preferred relays:</span>
<button style={{marginLeft: '20px'}} onClick={saveRelays}>
save
</button>
</div>
<div style={{marginLeft: '10px'}}>
{relays.map(({url, policy}, i) => (
<div key={i} style={{display: 'flex'}}>
<input
style={{marginRight: '10px', width: '400px'}}
value={url}
onChange={changeRelayURL.bind(null, i)}
/>
<label>
read
<input
type="checkbox"
checked={policy.read}
onChange={toggleRelayPolicy.bind(null, i, 'read')}
/>
</label>
<label>
write
<input
type="checkbox"
checked={policy.write}
onChange={toggleRelayPolicy.bind(null, i, 'write')}
/>
</label>
<button style={{marginLeft: '10px'}} onClick={removeRelay.bind(null, i)}>
remove
</button>
</div>
))}
<div style={{display: 'flex'}}>
<input
style={{width: '400px'}}
value={newRelayURL}
onChange={e => setNewRelayURL(e.target.value)}
onBlur={addNewRelay}
/>
</div>
</div>
</div>
<div style={{marginBottom: '10px'}}>
<label>
<h1 style={{fontSize: '25px', marginBlockEnd: '0px'}}>nos2x</h1>
<p style={{marginBlockStart: '0px'}}>nostr signer extension</p>
<h2 style={{marginBlockStart: '20px', marginBlockEnd: '5px'}}>options</h2>
<div style={{marginBottom: '10px', display: 'flex', flexDirection: 'column', gap: '10px', width: 'fit-content'}}>
<div>
<div>private key:&nbsp;</div>
<div style={{marginLeft: '10px'}}>
{!hidingPrivateKey && (
<div style={{marginLeft: '10px', display: 'flex', flexDirection: 'column', gap: '10px'}}>
<div style={{display: 'flex', gap: '10px'}}>
<input
type={hidingPrivateKey ? 'password' : 'text'}
style={{width: '600px'}}
value={privKey}
onChange={handleKeyChange}
/>
{privKey === '' && <button onClick={generate}>generate</button>}
{privKey && hidingPrivateKey && <button onClick={() => hidePrivateKey(false)}>show key</button>}
{privKey && !hidingPrivateKey && <button onClick={() => hidePrivateKey(true)}>hide key</button>}
</div>
{privKey && !isKeyValid() && <div style={{color: 'red'}}>private key is invalid!</div>}
{!hidingPrivateKey && isKeyValid() && (
<div
style={{
height: 'auto',
maxWidth: 256,
width: '100%'
width: '100%',
marginTop: '5px'
}}
>
<QRCode
@@ -143,170 +116,182 @@ function Options() {
/>
</div>
)}
<div style={{display: 'flex'}}>
<input
type={hidingPrivateKey ? 'password' : 'text'}
style={{width: '600px'}}
value={privKey}
onChange={handleKeyChange}
onFocus={() => hidePrivateKey(false)}
onBlur={() => hidePrivateKey(true)}
/>
{privKey === '' && <button onClick={generate}>generate</button>}
</div>
<button disabled={!isKeyValid()} onClick={saveKey}>
save
</button>
</div>
</label>
{policies?.length > 0 && (
<>
<div style={{display: 'flex', alignItems: 'center'}}>
<h2>policies</h2>
<label
style={{
display: 'flex',
alignItems: 'center',
marginLeft: '14px'
}}
>
show a notification when a permission is used by a page
</div>
<div>
<div>preferred relays:</div>
<div style={{marginLeft: '10px', display: 'flex', flexDirection: 'column', gap: '1px'}}>
{relays.map(({url, policy}, i) => (
<div key={i} style={{display: 'flex', alignItems: 'center', gap: '15px'}}>
<input
type="checkbox"
checked={showNotifications}
onClick={handleNotifications}
style={{width: '400px'}}
value={url}
onChange={changeRelayURL.bind(null, i)}
/>
</label>
<div style={{display: 'flex', gap: '5px'}}>
<label style={{display: 'flex', alignItems: 'center'}}>
read
<input
type="checkbox"
checked={policy.read}
onChange={toggleRelayPolicy.bind(null, i, 'read')}
/>
</label>
<label style={{display: 'flex', alignItems: 'center'}}>
write
<input
type="checkbox"
checked={policy.write}
onChange={toggleRelayPolicy.bind(null, i, 'write')}
/>
</label>
</div>
<button onClick={removeRelay.bind(null, i)}>
remove
</button>
</div>
))}
<div style={{display: 'flex', gap: '10px', marginTop: '5px'}}>
<input
style={{width: '400px'}}
value={newRelayURL}
onChange={e => setNewRelayURL(e.target.value)}
onKeyDown={e => { if (e.key === 'Enter') addNewRelay()}}
/>
<button disabled={!newRelayURL} onClick={addNewRelay}>add relay</button>
</div>
<table>
<thead>
<tr>
<th>domain</th>
<th>permission</th>
<th>answer</th>
<th>conditions</th>
<th>since</th>
<th></th>
</tr>
</thead>
<tbody>
{policies.map(
({host, type, accept, conditions, created_at}) => (
<tr key={host + type + accept + JSON.stringify(conditions)}>
<td>{host}</td>
<td>{type}</td>
<td>{accept}</td>
<td>
{conditions.kinds
? `kinds: ${Object.keys(conditions.kinds).join(', ')}`
: 'always'}
</td>
<td>
{new Date(created_at * 1000)
.toISOString()
.split('.')[0]
.split('T')
.join(' ')}
</td>
<td>
<button
onClick={handleRevoke}
data-host={host}
data-accept={accept}
data-type={type}
>
revoke
</button>
</td>
</tr>
)
)}
</tbody>
</table>
</>
)}
</div>
<div>
<h2>
handle{' '}
<span style={{padding: '2px', background: 'silver'}}>nostr:</span>{' '}
links:
</h2>
<div style={{marginLeft: '10px'}}>
<div>
<label>
<input
type="radio"
name="ph"
value="no"
checked={protocolHandler === null}
onChange={handleChangeProtocolHandler}
/>{' '}
no
</label>
</div>
</div>
<div>
<label style={{ display: 'flex', alignItems: 'center' }}>
<div>
<label>
<input
type="radio"
name="ph"
value="yes"
checked={protocolHandler !== null}
onChange={handleChangeProtocolHandler}
/>
yes
</label>
handle{' '}
<span style={{padding: '2px', background: 'silver'}}>nostr:</span>{' '}
links:
</div>
{protocolHandler !== null && (
<div>
<input
placeholder="url template"
value={protocolHandler}
onChange={handleChangeProtocolHandler}
style={{width: '680px', maxWidth: '90%'}}
/>
<pre>{`
{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
<input
type="checkbox"
checked={handleNostrLinks}
onChange={changeHandleNostrLinks}
/>
</label>
<div style={{marginLeft: '10px'}}>
{handleNostrLinks && (
<div>
<div style={{display: 'flex'}}>
<input
placeholder="url template"
value={protocolHandler}
onChange={handleChangeProtocolHandler}
style={{width: '680px', maxWidth: '90%'}}
/>
{!showProtocolHandlerHelp && <button onClick={changeShowProtocolHandlerHelp}>?</button>}
</div>
{showProtocolHandlerHelp && (<pre>{`
{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}
`}</pre>
</div>
)}
<button
style={{marginTop: '10px'}}
onClick={saveNostrProtocolHandlerSettings}
>
save
</button>
examples:
- https://nostr.guru/{p_or_e}/{hex}
- https://brb.io/{u_or_n}/{hex}
- https://notes.blockcore.net/{p_or_e}/{hex}
`}</pre>)}
</div>
)}
</div>
</div>
<label style={{ display: 'flex', alignItems: 'center' }}>
show notifications when permissions are used:
<input
type="checkbox"
checked={showNotifications}
onChange={handleNotifications}
/>
</label>
<button disabled={!unsavedChanges.length} onClick={saveChanges} style={{padding: '5px 20px'}}>
save
</button>
<div style={{fontSize: '120%'}}>
{messages.map((message, i) => (
<div key={i}>{message}</div>
))}
</div>
</div>
<div style={{marginTop: '12px', fontSize: '120%'}}>{message}</div>
<div>
<h2>permissions</h2>
<table>
<thead>
<tr>
<th>domain</th>
<th>permission</th>
<th>answer</th>
<th>conditions</th>
<th>since</th>
<th></th>
</tr>
</thead>
<tbody>
{policies.map(
({host, type, accept, conditions, created_at}) => (
<tr key={host + type + accept + JSON.stringify(conditions)}>
<td>{host}</td>
<td>{type}</td>
<td>{accept}</td>
<td>
{conditions.kinds
? `kinds: ${Object.keys(conditions.kinds).join(', ')}`
: 'always'}
</td>
<td>
{new Date(created_at * 1000)
.toISOString()
.split('.')[0]
.split('T')
.join(' ')}
</td>
<td>
<button
onClick={handleRevoke}
data-host={host}
data-accept={accept}
data-type={type}
>
revoke
</button>
</td>
</tr>
)
)}
{!policies.length && <tr>{Array(5).fill('N/A').map((v, i) => (<td key={i}>{v}</td>))}</tr>}
</tbody>
</table>
{!policies.length && <div style={{marginTop: '5px'}}>no permissions have been granted yet</div>}
</div>
</>
)
async function handleKeyChange(e) {
let key = e.target.value.toLowerCase().trim()
setPrivKey(key)
addUnsavedChanges('private_key')
}
async function generate() {
setPrivKey(nip19.nsecEncode(generatePrivateKey()))
addUnsavedChanges('private_key')
}
async function saveKey() {
if (!isKeyValid()) return
if (!isKeyValid()) {
showMessage('PRIVATE KEY IS INVALID! did not save private key.')
return
}
let hexOrEmptyKey = privKey
@@ -341,6 +326,7 @@ function Options() {
{url: ev.target.value, policy: relays[i].policy},
...relays.slice(i + 1)
])
addUnsavedChanges('relays')
}
function toggleRelayPolicy(i, cat) {
@@ -352,6 +338,7 @@ function Options() {
},
...relays.slice(i + 1)
])
addUnsavedChanges('relays')
}
function removeRelay(i) {
@@ -359,6 +346,7 @@ function Options() {
...relays.slice(0, i),
...relays.slice(i + 1)
])
addUnsavedChanges('relays')
}
function addNewRelay() {
@@ -368,6 +356,7 @@ function Options() {
policy: {read: true, write: true}
})
setRelays(relays)
addUnsavedChanges('relays')
setNewRelayURL('')
}
@@ -386,19 +375,22 @@ function Options() {
}
}
async function handleNotifications() {
if (showNotifications) {
await browser.storage.local.set({notifications: false})
setNotifications(false)
} else {
let granted = await browser.permissions.request({
permissions: ['notifications']
})
if (granted) {
await browser.storage.local.set({notifications: true})
setNotifications(true)
}
}
function handleNotifications() {
setNotifications(!showNotifications)
addUnsavedChanges('notifications')
if (!showNotifications) requestBrowserNotificationPermissions()
}
async function requestBrowserNotificationPermissions() {
let granted = await browser.permissions.request({
permissions: ['notifications']
})
if (!granted) setNotifications(false)
}
async function saveNotifications() {
await browser.storage.local.set({notifications: showNotifications})
showMessage('saved notifications!')
}
async function saveRelays() {
@@ -412,23 +404,54 @@ function Options() {
showMessage('saved relays!')
}
function changeShowProtocolHandlerHelp() {
setShowProtocolHandlerHelp(true)
}
function changeHandleNostrLinks() {
if (handleNostrLinks) {
setProtocolHandler('')
addUnsavedChanges('protocol_handler')
} else setShowProtocolHandlerHelp(true)
setHandleNostrLinks(!handleNostrLinks)
}
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
}
setProtocolHandler(e.target.value)
addUnsavedChanges('protocol_handler')
}
async function saveNostrProtocolHandlerSettings() {
await browser.storage.local.set({protocol_handler: protocolHandler})
showMessage('saved protocol handler!')
}
function addUnsavedChanges(section) {
if (!unsavedChanges.find(s => s === section)) {
unsavedChanges.push(section)
setUnsavedChanges(unsavedChanges)
}
}
async function saveChanges() {
for (let section of unsavedChanges) {
switch (section) {
case 'private_key':
await saveKey()
break
case 'relays':
await saveRelays()
break
case 'protocol_handler':
await saveNostrProtocolHandlerSettings()
break
case 'notifications':
await saveNotifications()
break
}
}
setUnsavedChanges([])
}
}
render(<Options />, document.getElementById('main'))