settings: move remaining settings code

with this change everything related to user settings is now
in settings.ts module.
OFF0 2 years ago
parent 77ac7de716
commit 0b48332bfb
Signed by: offbyn
GPG Key ID: 94A2F643C51F37FA

@ -1,4 +1,4 @@
import {generatePrivateKey, getPublicKey, nip19, signEvent} from 'nostr-tools'; import {nip19, signEvent} from 'nostr-tools';
import {zeroLeadingBitsCount} from './utils/crypto'; import {zeroLeadingBitsCount} from './utils/crypto';
import {elem, elemCanvas, elemShrink, parseTextContent, updateElemHeight} from './utils/dom'; import {elem, elemCanvas, elemShrink, parseTextContent, updateElemHeight} from './utils/dom';
import {bounce, dateTime, formatTime} from './utils/time'; import {bounce, dateTime, formatTime} from './utils/time';
@ -8,7 +8,7 @@ import {sub24hFeed, subNote, subProfile} from './subscriptions'
import {publish} from './relays'; import {publish} from './relays';
import {getReplyTo, hasEventTag, isMention, sortByCreatedAt, sortEventCreatedAt, validatePow} from './events'; import {getReplyTo, hasEventTag, isMention, sortByCreatedAt, sortEventCreatedAt, validatePow} from './events';
import {clearView, getViewContent, getViewElem, setViewElem, view} from './view'; import {clearView, getViewContent, getViewElem, setViewElem, view} from './view';
import {config} from './settings'; import {closeSettingsView, config, toggleSettingsView} from './settings';
// curl -H 'accept: application/nostr+json' https://relay.nostr.ch/ // curl -H 'accept: application/nostr+json' https://relay.nostr.ch/
function onEvent(evt, relay) { function onEvent(evt, relay) {
@ -608,7 +608,6 @@ window.addEventListener('popstate', (event) => {
route(location.pathname); route(location.pathname);
}); });
const settingsView = document.querySelector('#settings');
const publishView = document.querySelector('#newNote'); const publishView = document.querySelector('#newNote');
document.body.addEventListener('click', (e) => { document.body.addEventListener('click', (e) => {
@ -618,9 +617,7 @@ document.body.addEventListener('click', (e) => {
if (a) { if (a) {
if ('nav' in a.dataset) { if ('nav' in a.dataset) {
e.preventDefault(); e.preventDefault();
if (!settingsView.hidden) { closeSettingsView();
settingsView.hidden = true;
}
if (!publishView.hidden) { if (!publishView.hidden) {
publishView.hidden = true; publishView.hidden = true;
} }
@ -646,7 +643,7 @@ document.body.addEventListener('click', (e) => {
upvote(id, pubkey); upvote(id, pubkey);
break; break;
case 'settings': case 'settings':
settingsView.hidden = !settingsView.hidden; toggleSettingsView();
break; break;
case 'new-note': case 'new-note':
if (publishView.hidden) { if (publishView.hidden) {
@ -682,129 +679,3 @@ document.body.addEventListener('click', (e) => {
// hideNewMessage(true); // hideNewMessage(true);
// } // }
// }); // });
// settings
const settingsForm = document.querySelector('form[name="settings"]');
const privateKeyInput = settingsForm.querySelector('#privatekey');
const pubKeyInput = settingsForm.querySelector('#pubkey');
const statusMessage = settingsForm.querySelector('#keystatus');
const generateBtn = settingsForm.querySelector('button[name="generate"]');
const importBtn = settingsForm.querySelector('button[name="import"]');
const privateTgl = settingsForm.querySelector('button[name="privatekey-toggle"]');
generateBtn.addEventListener('click', () => {
const privatekey = generatePrivateKey();
const pubkey = getPublicKey(privatekey);
if (validKeys(privatekey, pubkey)) {
privateKeyInput.value = privatekey;
pubKeyInput.value = pubkey;
statusMessage.textContent = 'private-key created!';
statusMessage.hidden = false;
}
});
importBtn.addEventListener('click', () => {
const privatekey = privateKeyInput.value;
const pubkeyInput = pubKeyInput.value;
if (validKeys(privatekey, pubkeyInput)) {
localStorage.setItem('private_key', privatekey);
localStorage.setItem('pub_key', pubkeyInput);
statusMessage.textContent = 'stored private and public key locally!';
statusMessage.hidden = false;
config.pubkey = pubkeyInput;
}
});
settingsForm.addEventListener('input', () => validKeys(privateKeyInput.value, pubKeyInput.value));
privateKeyInput.addEventListener('paste', (event) => {
if (pubKeyInput.value || !event.clipboardData) {
return;
}
if (privateKeyInput.value === '' || ( // either privatekey field is empty
privateKeyInput.selectionStart === 0 // or the whole text is selected and replaced with the clipboard
&& privateKeyInput.selectionEnd === privateKeyInput.value.length
)) { // only generate the pubkey if no data other than the text from clipboard will be used
try {
pubKeyInput.value = getPublicKey(event.clipboardData.getData('text'));
} catch(err) {} // settings form will call validKeys on input and display the error
}
});
function validKeys(privatekey, pubkey) {
try {
if (getPublicKey(privatekey) === pubkey) {
statusMessage.hidden = true;
statusMessage.textContent = 'public-key corresponds to private-key';
importBtn.removeAttribute('disabled');
return true;
} else {
statusMessage.textContent = 'private-key does not correspond to public-key!'
}
} catch (e) {
statusMessage.textContent = `not a valid private-key: ${e.message || e}`;
}
statusMessage.hidden = false;
importBtn.setAttribute('disabled', true);
return false;
}
privateTgl.addEventListener('click', () => {
privateKeyInput.type = privateKeyInput.type === 'text' ? 'password' : 'text';
});
privateKeyInput.value = localStorage.getItem('private_key');
pubKeyInput.value = localStorage.getItem('pub_key');
// profile
const profileForm = document.querySelector('form[name="profile"]');
const profileSubmit = profileForm.querySelector('button[type="submit"]');
const profileStatus = document.querySelector('#profilestatus');
const onProfileError = err => {
profileStatus.hidden = false;
profileStatus.textContent = err.message
};
profileForm.addEventListener('input', (e) => {
if (e.target.nodeName === 'TEXTAREA') {
updateElemHeight(e.target);
}
const form = new FormData(profileForm);
const name = form.get('name');
const about = form.get('about');
const picture = form.get('picture');
profileSubmit.disabled = !(name || about || picture);
});
profileForm.addEventListener('submit', async (e) => {
e.preventDefault();
const form = new FormData(profileForm);
const newProfile = await powEvent({
kind: 0,
pubkey: config.pubkey,
content: JSON.stringify(Object.fromEntries(form)),
tags: [],
created_at: Math.floor(Date.now() * 0.001),
}, {
difficulty: config.difficulty,
statusElem: profileStatus,
timeout: config.timeout,
}).catch(console.warn);
if (!newProfile) {
profileStatus.textContent = 'publishing profile data canceled';
profileStatus.hidden = false;
return;
}
const privatekey = localStorage.getItem('private_key');
const sig = signEvent(newProfile, privatekey);
// TODO: validateEvent
if (sig) {
publish({...newProfile, sig}, (relay, error) => {
if (error) {
return console.error(error, relay);
}
console.info(`publish request sent to ${relay}`);
profileStatus.textContent = 'profile metadata successfully published';
profileStatus.hidden = false;
profileSubmit.disabled = true;
});
}
});

@ -1,4 +1,13 @@
import {generatePrivateKey, getPublicKey} from 'nostr-tools'; import {generatePrivateKey, getPublicKey, signEvent} from 'nostr-tools';
import {updateElemHeight} from './utils/dom';
import {powEvent} from './system';
import {publish} from './relays';
const settingsView = document.querySelector('#settings') as HTMLElement;
export const closeSettingsView = () => settingsView.hidden = true;
export const toggleSettingsView = () => settingsView.hidden = !settingsView.hidden;
let pubkey: string = ''; let pubkey: string = '';
@ -90,3 +99,141 @@ miningTimeoutInput.addEventListener('input', (e) => {
}); });
timeout = getNumberFromStorage('mining_timeout', 5); timeout = getNumberFromStorage('mining_timeout', 5);
miningTimeoutInput.valueAsNumber = timeout; miningTimeoutInput.valueAsNumber = timeout;
// settings
const settingsForm = document.querySelector('form[name="settings"]') as HTMLFormElement;
const privateKeyInput = settingsForm.querySelector('#privatekey') as HTMLInputElement;
const pubKeyInput = settingsForm.querySelector('#pubkey') as HTMLInputElement;
const statusMessage = settingsForm.querySelector('#keystatus') as HTMLElement;
const generateBtn = settingsForm.querySelector('button[name="generate"]') as HTMLButtonElement;
const importBtn = settingsForm.querySelector('button[name="import"]') as HTMLButtonElement;
const privateTgl = settingsForm.querySelector('button[name="privatekey-toggle"]') as HTMLButtonElement;
const validKeys = (
privatekey: string,
pubkey: string,
) => {
try {
if (getPublicKey(privatekey) === pubkey) {
statusMessage.hidden = true;
statusMessage.textContent = 'public-key corresponds to private-key';
importBtn.removeAttribute('disabled');
return true;
} else {
statusMessage.textContent = 'private-key does not correspond to public-key!'
}
} catch (e) {
statusMessage.textContent = `not a valid private-key: ${e.message || e}`;
}
statusMessage.hidden = false;
importBtn.disabled = true;
return false;
};
generateBtn.addEventListener('click', () => {
const privatekey = generatePrivateKey();
const pubkey = getPublicKey(privatekey);
if (validKeys(privatekey, pubkey)) {
privateKeyInput.value = privatekey;
pubKeyInput.value = pubkey;
statusMessage.textContent = 'private-key created!';
statusMessage.hidden = false;
}
});
importBtn.addEventListener('click', () => {
const privatekey = privateKeyInput.value;
const pubkeyInput = pubKeyInput.value;
if (validKeys(privatekey, pubkeyInput)) {
localStorage.setItem('private_key', privatekey);
localStorage.setItem('pub_key', pubkeyInput);
statusMessage.textContent = 'stored private and public key locally!';
statusMessage.hidden = false;
config.pubkey = pubkeyInput;
}
});
settingsForm.addEventListener('input', () => validKeys(privateKeyInput.value, pubKeyInput.value));
privateKeyInput.addEventListener('paste', (event) => {
if (pubKeyInput.value || !event.clipboardData) {
return;
}
if (privateKeyInput.value === '' || ( // either privatekey field is empty
privateKeyInput.selectionStart === 0 // or the whole text is selected and replaced with the clipboard
&& privateKeyInput.selectionEnd === privateKeyInput.value.length
)) { // only generate the pubkey if no data other than the text from clipboard will be used
try {
pubKeyInput.value = getPublicKey(event.clipboardData.getData('text'));
} catch(err) {} // settings form will call validKeys on input and display the error
}
});
privateTgl.addEventListener('click', () => {
privateKeyInput.type = privateKeyInput.type === 'text' ? 'password' : 'text';
});
privateKeyInput.value = localStorage.getItem('private_key') || '';
pubKeyInput.value = localStorage.getItem('pub_key') || '';
// profile
const profileForm = document.querySelector('form[name="profile"]') as HTMLFormElement;
const profileSubmit = profileForm.querySelector('button[type="submit"]') as HTMLButtonElement;
const profileStatus = document.querySelector('#profilestatus') as HTMLElement;
// const onProfileError = err => {
// profileStatus.hidden = false;
// profileStatus.textContent = err.message
// };
profileForm.addEventListener('input', (e) => {
if (e.target instanceof HTMLElement) {
if (e.target?.nodeName === 'TEXTAREA') {
updateElemHeight(e.target as HTMLTextAreaElement);
}
}
const form = new FormData(profileForm);
const name = form.get('name');
const about = form.get('about');
const picture = form.get('picture');
profileSubmit.disabled = !(name || about || picture);
});
profileForm.addEventListener('submit', async (e) => {
e.preventDefault();
const form = new FormData(profileForm);
const newProfile = await powEvent({
kind: 0,
pubkey: config.pubkey,
content: JSON.stringify(Object.fromEntries(form)),
tags: [],
created_at: Math.floor(Date.now() * 0.001)
}, {
difficulty: config.difficulty,
statusElem: profileStatus,
timeout: config.timeout,
}).catch(console.warn);
if (!newProfile) {
profileStatus.textContent = 'publishing profile data canceled';
profileStatus.hidden = false;
return;
}
const privatekey = localStorage.getItem('private_key');
if (!privatekey) {
profileStatus.textContent = 'no private key to sign';
profileStatus.hidden = false;
return;
}
const sig = signEvent(newProfile, privatekey);
// TODO: validateEvent
if (sig) {
publish({...newProfile, sig}, (relay, error) => {
if (error) {
return console.error(error, relay);
}
console.info(`publish request sent to ${relay}`);
profileStatus.textContent = 'profile metadata successfully published';
profileStatus.hidden = false;
profileSubmit.disabled = true;
});
}
});

Loading…
Cancel
Save