Added prettier config. All web files (js/html) should be formatted using prettier!

This commit is contained in:
Ryan Breen
2023-01-22 22:17:07 -05:00
parent 16589e2313
commit ea3805dd02
9 changed files with 216 additions and 137 deletions

7
.prettierrc Normal file
View File

@@ -0,0 +1,7 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": true,
"singleQuote": true,
"arrowParens": "avoid"
}

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"editor.formatOnSave": true
}

View File

@@ -1,90 +1,98 @@
import { generatePrivateKey, getPublicKey, signEvent, nip04, nip19 } from "nostr-tools"; import {
generatePrivateKey,
getPublicKey,
signEvent,
nip04,
nip19,
} from 'nostr-tools';
const storage = browser.storage.local; const storage = browser.storage.local;
browser.runtime.onInstalled.addListener(async ({reason}) => { browser.runtime.onInstalled.addListener(async ({ reason }) => {
// I would like to be able to skip this for development purposes // I would like to be able to skip this for development purposes
let ignoreHook = (await storage.get('ignoreInstallHook')).ignoreInstallHook let ignoreHook = (await storage.get('ignoreInstallHook')).ignoreInstallHook;
if (ignoreHook === true) { if (ignoreHook === true) {
return; return;
} }
if (['install'].includes(reason)) { if (['install'].includes(reason)) {
browser.tabs.create({ browser.tabs.create({
url: 'https://ursus.camp/nostore' url: 'https://ursus.camp/nostore',
}) });
} }
}); });
browser.runtime.onMessage.addListener(async (message, _sender, sendResponse) => { browser.runtime.onMessage.addListener(
console.log(message); async (message, _sender, sendResponse) => {
console.log(message);
switch (message.kind) { switch (message.kind) {
case 'init': case 'init':
await initialize(); await initialize();
break; break;
case 'setProfileIndex': case 'setProfileIndex':
await setProfileIndex(message.payload); await setProfileIndex(message.payload);
break; break;
case 'getProfileIndex': case 'getProfileIndex':
let profileIndex = await getProfileIndex(); let profileIndex = await getProfileIndex();
sendResponse(profileIndex); sendResponse(profileIndex);
break; break;
case 'getNsecKey': case 'getNsecKey':
let nsecKey = await getNsecKey(); let nsecKey = await getNsecKey();
sendResponse(nsecKey); sendResponse(nsecKey);
break; break;
case 'getNpubKey': case 'getNpubKey':
let npubKey = await getNpubKey(); let npubKey = await getNpubKey();
sendResponse(npubKey); sendResponse(npubKey);
break; break;
case 'getPubKey': case 'getPubKey':
let pubKey = await getPubKey(); let pubKey = await getPubKey();
sendResponse(pubKey); sendResponse(pubKey);
break; break;
case 'getHosts': case 'getHosts':
let hosts = await getHosts(); let hosts = await getHosts();
sendResponse(hosts); sendResponse(hosts);
break; break;
case 'getName': case 'getName':
let name = await getName(); let name = await getName();
sendResponse(name); sendResponse(name);
break; break;
case 'getProfileNames': case 'getProfileNames':
let profileNames = await getProfileNames(); let profileNames = await getProfileNames();
sendResponse(profileNames); sendResponse(profileNames);
break; break;
case 'newProfile': case 'newProfile':
let newIndex = await newProfile(); let newIndex = await newProfile();
sendResponse(newIndex); sendResponse(newIndex);
break; break;
case 'saveProfile': case 'saveProfile':
await saveProfile(message.payload); await saveProfile(message.payload);
break; break;
case 'clearData': case 'clearData':
await browser.storage.local.clear(); await browser.storage.local.clear();
break; break;
case 'deleteProfile': case 'deleteProfile':
await deleteProfile(); await deleteProfile();
break; break;
case 'signEvent': case 'signEvent':
let event = await signEvent_(message.payload); let event = await signEvent_(message.payload);
sendResponse(event); sendResponse(event);
break; break;
case 'nip04.encrypt': case 'nip04.encrypt':
let cipherText = await nip04Encrypt(message.payload); let cipherText = await nip04Encrypt(message.payload);
sendResponse(cipherText); sendResponse(cipherText);
break; break;
case 'nip04.decrypt': case 'nip04.decrypt':
let plainText = await nip04Decrypt(message.payload); let plainText = await nip04Decrypt(message.payload);
sendResponse(plainText); sendResponse(plainText);
break; break;
case 'getRelays': case 'getRelays':
sendResponse({}); sendResponse({});
break; break;
default: default:
break; break;
}
} }
}); );
async function get(item) { async function get(item) {
return (await storage.get(item))[item]; return (await storage.get(item))[item];
@@ -93,7 +101,7 @@ async function get(item) {
async function getOrSetDefault(key, def) { async function getOrSetDefault(key, def) {
let val = (await storage.get(key))[key]; let val = (await storage.get(key))[key];
if (val == null || val == undefined) { if (val == null || val == undefined) {
await storage.set({[key]: def}); await storage.set({ [key]: def });
return def; return def;
} }
@@ -102,7 +110,9 @@ async function getOrSetDefault(key, def) {
async function initialize() { async function initialize() {
await getOrSetDefault('profileIndex', 0); await getOrSetDefault('profileIndex', 0);
await getOrSetDefault('profiles', [{name: 'Default', privKey: generatePrivateKey(), hosts: []}]); await getOrSetDefault('profiles', [
{ name: 'Default', privKey: generatePrivateKey(), hosts: [] },
]);
} }
async function getNsecKey() { async function getNsecKey() {
@@ -145,7 +155,7 @@ async function getProfileNames() {
} }
async function setProfileIndex(profileIndex) { async function setProfileIndex(profileIndex) {
await storage.set({profileIndex}); await storage.set({ profileIndex });
} }
async function getProfileIndex() { async function getProfileIndex() {
@@ -162,9 +172,13 @@ async function currentProfile() {
async function newProfile() { async function newProfile() {
let profiles = await get('profiles'); let profiles = await get('profiles');
const newProfile = {name: 'New Profile', privKey: generatePrivateKey(), hosts: []}; const newProfile = {
name: 'New Profile',
privKey: generatePrivateKey(),
hosts: [],
};
profiles.push(newProfile); profiles.push(newProfile);
await storage.set({profiles}); await storage.set({ profiles });
return profiles.length - 1; return profiles.length - 1;
} }
@@ -175,7 +189,7 @@ async function saveProfile(profile) {
let index = await getProfileIndex(); let index = await getProfileIndex();
let profiles = await get('profiles'); let profiles = await get('profiles');
profiles[index] = profile; profiles[index] = profile;
await storage.set({profiles}); await storage.set({ profiles });
} }
async function deleteProfile() { async function deleteProfile() {
@@ -183,22 +197,22 @@ async function deleteProfile() {
let profiles = await get('profiles'); let profiles = await get('profiles');
profiles.splice(index, 1); profiles.splice(index, 1);
let profileIndex = Math.max(index - 1, 0); let profileIndex = Math.max(index - 1, 0);
await storage.set({profiles, profileIndex}); await storage.set({ profiles, profileIndex });
} }
async function signEvent_(event) { async function signEvent_(event) {
event = {...event}; event = { ...event };
let privKey = await getPrivKey(); let privKey = await getPrivKey();
event.sig = signEvent(event, privKey); event.sig = signEvent(event, privKey);
return event; return event;
} }
async function nip04Encrypt({pubKey, plainText}) { async function nip04Encrypt({ pubKey, plainText }) {
let privKey = await getPrivKey(); let privKey = await getPrivKey();
return nip04.encrypt(privKey, pubKey, plainText); return nip04.encrypt(privKey, pubKey, plainText);
} }
async function nip04Decrypt({pubKey, cipherText}) { async function nip04Decrypt({ pubKey, cipherText }) {
let privKey = await getPrivKey(); let privKey = await getPrivKey();
return nip04.decrypt(privKey, pubKey, cipherText); return nip04.decrypt(privKey, pubKey, cipherText);
} }

View File

@@ -2,16 +2,20 @@ let script = document.createElement('script');
script.setAttribute('src', browser.runtime.getURL('nostr.build.js')); script.setAttribute('src', browser.runtime.getURL('nostr.build.js'));
document.body.appendChild(script); document.body.appendChild(script);
window.addEventListener('message', async (message) => { window.addEventListener('message', async message => {
const validEvents = ['getPubKey', 'signEvent', 'getRelays', 'nip04.encrypt', 'nip04.decrypt']; const validEvents = [
let {kind, reqId, payload} = message.data; 'getPubKey',
if (!validEvents.includes(kind)) 'signEvent',
return; 'getRelays',
'nip04.encrypt',
console.log(`Event ${reqId}: Content script received message kind ${kind}, payload: `, payload); 'nip04.decrypt',
payload = await browser.runtime.sendMessage({kind, payload}); ];
let { kind, reqId, payload } = message.data;
if (!validEvents.includes(kind)) return;
kind = `return_${kind}`; payload = await browser.runtime.sendMessage({ kind, payload });
window.postMessage({kind, reqId, payload}, '*'); kind = `return_${kind}`;
});
window.postMessage({ kind, reqId, payload }, '*');
});

View File

@@ -4,7 +4,7 @@ window.nostr = {
async getPublicKey() { async getPublicKey() {
return await this.broadcast('getPubKey'); return await this.broadcast('getPubKey');
}, },
async signEvent(event) { async signEvent(event) {
return await this.broadcast('signEvent', event); return await this.broadcast('signEvent', event);
}, },
@@ -16,7 +16,7 @@ window.nostr = {
// This is here for Alby comatibility. This is not part of the NIP-07 standard. // This is here for Alby comatibility. This is not part of the NIP-07 standard.
// I have found at least one site, nostr.band, which expects it to be present. // I have found at least one site, nostr.band, which expects it to be present.
async enable() { async enable() {
return {enabled: true} return { enabled: true };
}, },
broadcast(kind, payload) { broadcast(kind, payload) {
@@ -24,30 +24,40 @@ window.nostr = {
return new Promise((resolve, _reject) => { return new Promise((resolve, _reject) => {
this.requests[reqId] = resolve; this.requests[reqId] = resolve;
console.log(`Event ${reqId}: ${kind}, payload: `, payload); console.log(`Event ${reqId}: ${kind}, payload: `, payload);
window.postMessage({kind, reqId, payload}, '*'); window.postMessage({ kind, reqId, payload }, '*');
}); });
}, },
nip04: { nip04: {
async encrypt(pubKey, plainText) { async encrypt(pubKey, plainText) {
return await window.nostr.broadcast('nip04.encrypt', {pubKey, plainText}); return await window.nostr.broadcast('nip04.encrypt', {
pubKey,
plainText,
});
}, },
async decrypt(pubKey, cipherText) { async decrypt(pubKey, cipherText) {
return await window.nostr.broadcast('nip04.decrypt', {pubKey, cipherText}); return await window.nostr.broadcast('nip04.decrypt', {
} pubKey,
} cipherText,
} });
},
},
};
window.addEventListener('message', (message) => { window.addEventListener('message', message => {
const validEvents = ['getPubKey', 'signEvent', 'getRelays', 'nip04.encrypt', 'nip04.decrypt'].map(e => `return_${e}`); const validEvents = [
let {kind, reqId, payload} = message.data; 'getPubKey',
'signEvent',
'getRelays',
'nip04.encrypt',
'nip04.decrypt',
].map(e => `return_${e}`);
let { kind, reqId, payload } = message.data;
if (!validEvents.includes(kind)) return;
if (!validEvents.includes(kind))
return;
console.log(`Event ${reqId}: Received payload:`, payload); console.log(`Event ${reqId}: Received payload:`, payload);
window.nostr.requests[reqId](payload); window.nostr.requests[reqId](payload);
delete window.nostr.requests[reqId]; delete window.nostr.requests[reqId];
}); });

View File

@@ -1,11 +1,13 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="stylesheet" href="popup.css"> <link rel="stylesheet" href="popup.css">
<script defer src="popup.build.js"></script> <script defer src="popup.build.js"></script>
</head> </head>
<body x-data="popup"> <body x-data="popup">
<div class="profiles"> <div class="profiles">
<label for="profile">Active Profile</label> <label for="profile">Active Profile</label>
@@ -16,7 +18,8 @@
</template> </template>
</select> </select>
<button @click="newProfile">New</button> <button @click="newProfile">New</button>
<button @click="confirmDelete = true" x-show="!confirmDelete" :disabled="profileNames.length <= 1">Delete</button> <button @click="confirmDelete = true" x-show="!confirmDelete"
:disabled="profileNames.length <= 1">Delete</button>
<button @click="await deleteProfile()" x-show="confirmDelete">Confirm Delete</button> <button @click="await deleteProfile()" x-show="confirmDelete">Confirm Delete</button>
</div> </div>
</div> </div>
@@ -25,7 +28,7 @@
<label for="profile-name">Profile Name</label> <label for="profile-name">Profile Name</label>
<input type="text" id="profile-name" x-model="name"> <input type="text" id="profile-name" x-model="name">
</div> </div>
<div class="key"> <div class="key">
<label for="priv-key">Private Key</label> <label for="priv-key">Private Key</label>
<input id="priv-key" x-model="privKey" :type="visibleKey ? 'text' : 'password'"> <input id="priv-key" x-model="privKey" :type="visibleKey ? 'text' : 'password'">
@@ -59,10 +62,11 @@
<div class="help"> <div class="help">
<button @click='window.open("https://ursus.camp/nostore", "_blank")'>Get Help</button> <button @click='window.open("https://ursus.camp/nostore", "_blank")'>Get Help</button>
</div> </div>
<div class="disclaimer"> <div class="disclaimer">
No user data is collected or transmitted. No user data is collected or transmitted.
All private keys are stored in the extension's sequestered local browser storage. All private keys are stored in the extension's sequestered local browser storage.
</div> </div>
</body> </body>
</html>
</html>

View File

@@ -1,4 +1,4 @@
import Alpine from "alpinejs"; import Alpine from 'alpinejs';
window.Alpine = Alpine; window.Alpine = Alpine;
Alpine.data('popup', () => ({ Alpine.data('popup', () => ({
@@ -15,8 +15,8 @@ Alpine.data('popup', () => ({
confirmDelete: false, confirmDelete: false,
async init() { async init() {
console.log("Initializing backend."); console.log('Initializing backend.');
await browser.runtime.sendMessage({kind: 'init'}); await browser.runtime.sendMessage({ kind: 'init' });
this.$watch('profileIndex', async () => { this.$watch('profileIndex', async () => {
await this.setProfileIndex(); await this.setProfileIndex();
@@ -46,57 +46,71 @@ Alpine.data('popup', () => ({
// Becauset the popup state resets every time it open, we use null as a guard. That way // Becauset the popup state resets every time it open, we use null as a guard. That way
// whenever the user opens the popup, it doesn't automatically reset the current profile // whenever the user opens the popup, it doesn't automatically reset the current profile
if (this.profileIndex !== null) { if (this.profileIndex !== null) {
await browser.runtime.sendMessage({kind: 'setProfileIndex', payload: this.profileIndex}); await browser.runtime.sendMessage({
kind: 'setProfileIndex',
payload: this.profileIndex,
});
} }
}, },
async getNsecKey() { async getNsecKey() {
this.privKey = await browser.runtime.sendMessage({kind: 'getNsecKey'}); this.privKey = await browser.runtime.sendMessage({
kind: 'getNsecKey',
});
this.pristinePrivKey = this.privKey; this.pristinePrivKey = this.privKey;
}, },
async getNpubKey() { async getNpubKey() {
this.pubKey = await browser.runtime.sendMessage({kind: 'getNpubKey'}); this.pubKey = await browser.runtime.sendMessage({ kind: 'getNpubKey' });
}, },
async getHosts() { async getHosts() {
this.hosts = await browser.runtime.sendMessage({kind: 'getHosts'}); this.hosts = await browser.runtime.sendMessage({ kind: 'getHosts' });
}, },
async getProfileNames() { async getProfileNames() {
this.profileNames = await browser.runtime.sendMessage({kind: 'getProfileNames'}); this.profileNames = await browser.runtime.sendMessage({
kind: 'getProfileNames',
});
}, },
async getName() { async getName() {
this.name = await browser.runtime.sendMessage({kind: 'getName'}); this.name = await browser.runtime.sendMessage({ kind: 'getName' });
this.pristineName = this.name; this.pristineName = this.name;
}, },
async getProfileIndex() { async getProfileIndex() {
this.profileIndex = await browser.runtime.sendMessage({kind: 'getProfileIndex'}); this.profileIndex = await browser.runtime.sendMessage({
kind: 'getProfileIndex',
});
}, },
async newProfile() { async newProfile() {
let newIndex = await browser.runtime.sendMessage({kind: 'newProfile'}); let newIndex = await browser.runtime.sendMessage({
kind: 'newProfile',
});
await this.refreshProfile(); await this.refreshProfile();
this.profileIndex = newIndex; this.profileIndex = newIndex;
}, },
async saveProfile() { async saveProfile() {
let {name, privKey, hosts} = this; let { name, privKey, hosts } = this;
let profile = {name, privKey, hosts}; let profile = { name, privKey, hosts };
await browser.runtime.sendMessage({kind: 'saveProfile', payload: profile}); await browser.runtime.sendMessage({
kind: 'saveProfile',
payload: profile,
});
await this.refreshProfile(); await this.refreshProfile();
}, },
async clearData() { async clearData() {
await browser.runtime.sendMessage({kind: 'clearData'}); await browser.runtime.sendMessage({ kind: 'clearData' });
await this.init(); // Re-initialize after clearing await this.init(); // Re-initialize after clearing
this.confirmClear = false; this.confirmClear = false;
}, },
async deleteProfile() { async deleteProfile() {
await browser.runtime.sendMessage({kind: 'deleteProfile'}); await browser.runtime.sendMessage({ kind: 'deleteProfile' });
await this.init(); await this.init();
this.confirmDelete = false; this.confirmDelete = false;
}, },
@@ -104,13 +118,15 @@ Alpine.data('popup', () => ({
// Properties // Properties
get hasValidPubKey() { get hasValidPubKey() {
return typeof(this.pubKey) === 'string' && this.pubKey.length > 0; return typeof this.pubKey === 'string' && this.pubKey.length > 0;
}, },
get needsSaving() { get needsSaving() {
return (this.privKey !== this.pristinePrivKey || this.name !== this.pristineName); return (
} this.privKey !== this.pristinePrivKey ||
this.name !== this.pristineName
);
},
})); }));
Alpine.start(); Alpine.start();

18
package-lock.json generated
View File

@@ -12,6 +12,9 @@
"alpinejs": "^3.10.5", "alpinejs": "^3.10.5",
"esbuild": "^0.16.17", "esbuild": "^0.16.17",
"nostr-tools": "^1.1.1" "nostr-tools": "^1.1.1"
},
"devDependencies": {
"prettier": "^2.8.3"
} }
}, },
"node_modules/@esbuild/android-arm": { "node_modules/@esbuild/android-arm": {
@@ -492,6 +495,21 @@
"@scure/bip32": "^1.1.1", "@scure/bip32": "^1.1.1",
"@scure/bip39": "^1.1.0" "@scure/bip39": "^1.1.0"
} }
},
"node_modules/prettier": {
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz",
"integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
} }
} }
} }

View File

@@ -19,5 +19,8 @@
"alpinejs": "^3.10.5", "alpinejs": "^3.10.5",
"esbuild": "^0.16.17", "esbuild": "^0.16.17",
"nostr-tools": "^1.1.1" "nostr-tools": "^1.1.1"
},
"devDependencies": {
"prettier": "^2.8.3"
} }
} }