Merge pull request #5 from ursuscamp/nip26

Added page for Experimental features, with NIP-26 support.
This commit is contained in:
Ryan Breen
2023-02-11 23:31:07 -05:00
committed by GitHub
15 changed files with 371 additions and 52 deletions

View File

@@ -77,6 +77,18 @@
944A6DD72988BD230032C2E3 /* permission.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DD52988BD230032C2E3 /* permission.js */; };
944A6DD92988D7900032C2E3 /* permission.build.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DD82988D7900032C2E3 /* permission.build.js */; };
944A6DDA2988D7900032C2E3 /* permission.build.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DD82988D7900032C2E3 /* permission.build.js */; };
944A6DE02991DFC60032C2E3 /* delegation_wizard.build.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DDC2991DFC60032C2E3 /* delegation_wizard.build.js */; };
944A6DE12991DFC60032C2E3 /* delegation_wizard.build.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DDC2991DFC60032C2E3 /* delegation_wizard.build.js */; };
944A6DE22991DFC60032C2E3 /* delegation_wizard.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DDD2991DFC60032C2E3 /* delegation_wizard.js */; };
944A6DE32991DFC60032C2E3 /* delegation_wizard.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DDD2991DFC60032C2E3 /* delegation_wizard.js */; };
944A6DE52991E4550032C2E3 /* delegation_wizard.html in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DE42991E4550032C2E3 /* delegation_wizard.html */; };
944A6DE62991E4550032C2E3 /* delegation_wizard.html in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DE42991E4550032C2E3 /* delegation_wizard.html */; };
944A6DEA299682EF0032C2E3 /* experimental.html in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DE7299682EF0032C2E3 /* experimental.html */; };
944A6DEB299682EF0032C2E3 /* experimental.html in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DE7299682EF0032C2E3 /* experimental.html */; };
944A6DEC299682EF0032C2E3 /* experimental.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DE8299682EF0032C2E3 /* experimental.js */; };
944A6DED299682EF0032C2E3 /* experimental.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DE8299682EF0032C2E3 /* experimental.js */; };
944A6DEE299682EF0032C2E3 /* experimental.build.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DE9299682EF0032C2E3 /* experimental.build.js */; };
944A6DEF299682EF0032C2E3 /* experimental.build.js in Resources */ = {isa = PBXBuildFile; fileRef = 944A6DE9299682EF0032C2E3 /* experimental.build.js */; };
948C69D9297F887600FB3574 /* options.html in Resources */ = {isa = PBXBuildFile; fileRef = 948C69D8297F887600FB3574 /* options.html */; };
948C69DA297F887600FB3574 /* options.html in Resources */ = {isa = PBXBuildFile; fileRef = 948C69D8297F887600FB3574 /* options.html */; };
948C69DD297F88A200FB3574 /* options.css in Resources */ = {isa = PBXBuildFile; fileRef = 948C69DB297F88A200FB3574 /* options.css */; };
@@ -180,6 +192,12 @@
944A6DD22988BA200032C2E3 /* permission.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = permission.html; sourceTree = "<group>"; };
944A6DD52988BD230032C2E3 /* permission.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = permission.js; sourceTree = "<group>"; };
944A6DD82988D7900032C2E3 /* permission.build.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = permission.build.js; sourceTree = "<group>"; };
944A6DDC2991DFC60032C2E3 /* delegation_wizard.build.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = delegation_wizard.build.js; sourceTree = "<group>"; };
944A6DDD2991DFC60032C2E3 /* delegation_wizard.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = delegation_wizard.js; sourceTree = "<group>"; };
944A6DE42991E4550032C2E3 /* delegation_wizard.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = delegation_wizard.html; sourceTree = "<group>"; };
944A6DE7299682EF0032C2E3 /* experimental.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = experimental.html; sourceTree = "<group>"; };
944A6DE8299682EF0032C2E3 /* experimental.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = experimental.js; sourceTree = "<group>"; };
944A6DE9299682EF0032C2E3 /* experimental.build.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = experimental.build.js; sourceTree = "<group>"; };
948C69D8297F887600FB3574 /* options.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = options.html; sourceTree = "<group>"; };
948C69DB297F88A200FB3574 /* options.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = options.css; sourceTree = "<group>"; };
948C69DC297F88A200FB3574 /* options.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = options.js; sourceTree = "<group>"; };
@@ -267,6 +285,12 @@
941B03A2296FA90400CA291E /* Resources */ = {
isa = PBXGroup;
children = (
944A6DE9299682EF0032C2E3 /* experimental.build.js */,
944A6DE7299682EF0032C2E3 /* experimental.html */,
944A6DE8299682EF0032C2E3 /* experimental.js */,
944A6DE42991E4550032C2E3 /* delegation_wizard.html */,
944A6DDC2991DFC60032C2E3 /* delegation_wizard.build.js */,
944A6DDD2991DFC60032C2E3 /* delegation_wizard.js */,
944A6DD82988D7900032C2E3 /* permission.build.js */,
944A6DD52988BD230032C2E3 /* permission.js */,
944A6DD22988BA200032C2E3 /* permission.html */,
@@ -510,8 +534,12 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
944A6DEE299682EF0032C2E3 /* experimental.build.js in Resources */,
941B0413297110F100CA291E /* background.build.js in Resources */,
944A6DE22991DFC60032C2E3 /* delegation_wizard.js in Resources */,
944A6DE52991E4550032C2E3 /* delegation_wizard.html in Resources */,
948C69E82982DFE900FB3574 /* background.html in Resources */,
944A6DEA299682EF0032C2E3 /* experimental.html in Resources */,
948C69DF297F88A200FB3574 /* options.js in Resources */,
944A6DD32988BA200032C2E3 /* permission.html in Resources */,
948C69DD297F88A200FB3574 /* options.css in Resources */,
@@ -524,12 +552,14 @@
941B042C2978CD8E00CA291E /* iOS-Icon-1024.png in Resources */,
941B03F6296FA90400CA291E /* popup.html in Resources */,
941B040D296FAD6900CA291E /* nostr.js in Resources */,
944A6DE02991DFC60032C2E3 /* delegation_wizard.build.js in Resources */,
941B03EE296FA90400CA291E /* images in Resources */,
941B03F0296FA90400CA291E /* manifest.json in Resources */,
944A6DD62988BD230032C2E3 /* permission.js in Resources */,
941B04312978CDF900CA291E /* Icon-32.png in Resources */,
941B041A2971139000CA291E /* content.build.js in Resources */,
941B041C2971139000CA291E /* popup.build.js in Resources */,
944A6DEC299682EF0032C2E3 /* experimental.js in Resources */,
941B03EC296FA90400CA291E /* _locales in Resources */,
941B04222977A25700CA291E /* Icon-512.png in Resources */,
948C69E5297F8BA600FB3574 /* options.build.css in Resources */,
@@ -547,8 +577,12 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
944A6DEF299682EF0032C2E3 /* experimental.build.js in Resources */,
941B0414297110F100CA291E /* background.build.js in Resources */,
944A6DE32991DFC60032C2E3 /* delegation_wizard.js in Resources */,
944A6DE62991E4550032C2E3 /* delegation_wizard.html in Resources */,
948C69E92982DFE900FB3574 /* background.html in Resources */,
944A6DEB299682EF0032C2E3 /* experimental.html in Resources */,
948C69E0297F88A200FB3574 /* options.js in Resources */,
944A6DD42988BA200032C2E3 /* permission.html in Resources */,
948C69DE297F88A200FB3574 /* options.css in Resources */,
@@ -561,12 +595,14 @@
941B042D2978CD8E00CA291E /* iOS-Icon-1024.png in Resources */,
941B03F7296FA90400CA291E /* popup.html in Resources */,
941B040E296FAD6900CA291E /* nostr.js in Resources */,
944A6DE12991DFC60032C2E3 /* delegation_wizard.build.js in Resources */,
941B03EF296FA90400CA291E /* images in Resources */,
941B03F1296FA90400CA291E /* manifest.json in Resources */,
944A6DD72988BD230032C2E3 /* permission.js in Resources */,
941B04322978CDF900CA291E /* Icon-32.png in Resources */,
941B041B2971139000CA291E /* content.build.js in Resources */,
941B041D2971139000CA291E /* popup.build.js in Resources */,
944A6DED299682EF0032C2E3 /* experimental.js in Resources */,
941B03ED296FA90400CA291E /* _locales in Resources */,
941B04232977A25700CA291E /* Icon-512.png in Resources */,
948C69E6297F8BA600FB3574 /* options.build.css in Resources */,

View File

@@ -7,12 +7,12 @@
<key>Nostore (iOS).xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
<integer>1</integer>
</dict>
<key>Nostore (macOS).xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
<integer>0</integer>
</dict>
<key>nostore (iOS).xcscheme_^#shared#^_</key>
<dict>

View File

@@ -4,6 +4,8 @@ import {
signEvent,
nip04,
nip19,
nip26,
getEventHash,
} from 'nostr-tools';
import { Mutex } from 'async-mutex';
import {
@@ -12,6 +14,7 @@ import {
getProfile,
getPermission,
setPermission,
feature,
} from './utils';
const storage = browser.storage.local;
@@ -19,20 +22,6 @@ const log = msg => console.log('Background: ', msg);
const validations = {};
let prompt = { mutex: new Mutex(), release: null, tabId: null };
browser.runtime.onInstalled.addListener(async ({ reason }) => {
// I would like to be able to skip this for development purposes
// let ignoreHook = (await storage.get({ ignoreInstallHook: false }))
// .ignoreInstallHook;
// if (ignoreHook === true) {
// return;
// }
// if (['install'].includes(reason)) {
// browser.tabs.create({
// url: 'https://ursus.camp/nostore',
// });
// }
});
browser.runtime.onMessage.addListener((message, _sender, sendResponse) => {
log(message);
let uuid = crypto.randomUUID();
@@ -57,6 +46,10 @@ browser.runtime.onMessage.addListener((message, _sender, sendResponse) => {
return getNpub(message.payload);
case 'getNsec':
return getNsec(message.payload);
case 'createDelegation':
return createDelegation(message.payload);
case 'calcPubKey':
return Promise.resolve(getPublicKey(message.payload));
// window.nostr
case 'getPubKey':
@@ -64,11 +57,9 @@ browser.runtime.onMessage.addListener((message, _sender, sendResponse) => {
case 'nip04.encrypt':
case 'nip04.decrypt':
case 'getRelays':
console.log('asking');
validations[uuid] = sendResponse;
ask(uuid, message);
setDelegation(message).then(() => ask(uuid, message));
setTimeout(() => {
console.log('timeout release');
prompt.release?.();
}, 10_000);
return true;
@@ -98,9 +89,7 @@ async function ask(uuid, { kind, host, payload }) {
let mKind = kind === 'signEvent' ? `signEvent:${payload.kind}` : kind;
let permission = await getPermission(host, mKind);
console.log('existing permission: ', permission);
if (permission === 'allow') {
console.log('already allowed');
complete({
payload: uuid,
origKind: kind,
@@ -113,13 +102,11 @@ async function ask(uuid, { kind, host, payload }) {
}
if (permission === 'deny') {
console.log('already denied');
deny({ payload: uuid, origKind: kind, host });
prompt.release();
return;
}
console.log('creating asking popup');
let qs = new URLSearchParams({
uuid,
kind,
@@ -136,22 +123,18 @@ async function ask(uuid, { kind, host, payload }) {
}
function complete({ payload, origKind, event, remember, host }) {
console.log('complete');
sendResponse = validations[payload];
if (remember) {
console.log('saving permission');
let mKind =
origKind === 'signEvent' ? `signEvent:${event.kind}` : origKind;
setPermission(host, mKind, 'allow');
}
if (sendResponse) {
console.log('sendResponse found');
switch (origKind) {
case 'getPubKey':
getPubKey().then(pk => {
console.log(pk);
sendResponse(pk);
});
break;
@@ -172,11 +155,9 @@ function complete({ payload, origKind, event, remember, host }) {
}
function deny({ origKind, host, payload, remember, event }) {
console.log('denied');
sendResponse = validations[payload];
if (remember) {
console.log('saving permission');
let mKind =
origKind === 'signEvent' ? `signEvent:${event.kind}` : origKind;
setPermission(host, mKind, 'deny');
@@ -186,17 +167,6 @@ function deny({ origKind, host, payload, remember, event }) {
return false;
}
function keyDeleter(key) {
return new Promise(resolver => {
setTimeout(() => {
console.log('Validations: ', validations);
console.log('Deleting key validations: ', key);
resolver();
delete validations[key];
}, 1000);
});
}
// Options
async function savePrivateKey([index, privKey]) {
if (privKey.startsWith('nsec')) {
@@ -227,6 +197,11 @@ async function getPrivKey() {
}
async function getPubKey() {
let pi = await getProfileIndex();
let profile = await getProfile(pi);
if (profile.delegate) {
return profile.delegator;
}
let privKey = await getPrivKey();
let pubKey = getPublicKey(privKey);
return pubKey;
@@ -239,9 +214,13 @@ async function currentProfile() {
}
async function signEvent_(event) {
event = { ...event };
event = JSON.parse(JSON.stringify(event));
let privKey = await getPrivKey();
let pubKey = getPublicKey(privKey);
event.pubkey = pubKey;
event.id = getEventHash(event);
event.sig = signEvent(event, privKey);
console.log(JSON.stringify(event));
return event;
}
@@ -266,3 +245,42 @@ async function getRelays() {
});
return relayObj;
}
function createDelegation({
kind,
delegatorPrivKey,
delegateePubKey,
until,
since,
}) {
delegatorPrivKey = nip19.decode(delegatorPrivKey).data;
let delegation = nip26.createDelegation(delegatorPrivKey, {
pubkey: delegateePubKey,
until,
// kind,
// since,
});
return Promise.resolve(delegation);
}
async function setDelegation({ payload }) {
if (!payload) return;
let active = await feature('delegation');
if (!active) return;
let { delegate, delegation } = await currentProfile();
// Nothing to do if this is not a delegate
if (!delegate) {
return;
}
payload.tags = payload.tags || [];
payload.tags.push([
'delegation',
delegation.from,
delegation.cond,
delegation.sig,
]);
}

View File

@@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script defer src="delegation_wizard.build.js"></script>
<link rel="stylesheet" href="options.build.css">
<title>Delegation Wizard</title>
</head>
<body x-data="delegated" class="text-fuchsia-900 p-3.5 lg:p-32">
<h1 class="text-3xl lg:text-6xl font-bold md:text-center">New Delegated Profile</h1>
<p class="mt-6">
A delegated user, as laid out in the
<a href="https://github.com/nostr-protocol/nips/blob/master/26.md" @click.prevent="openNip($event)">NIP-26
specification</a>,
is a user that is authorized to sign events for a different user. Additional limits can be
put on the delegated account, such as time limits.
</p>
<p class="mt-3">
This may be useful if you wish to keep your main key stored safely offline, but still post as that user
with a throwaway key.
</p>
<div class="section">
<h2 class="section-header">Delegator</h2>
<p class="text-small italic">The secure account to be delegated.</p>
<div class="mt-3">
<label for="delegator">Delegator Private Key</label>
<br>
<input type="text" class="input" x-model="privKey" :class="validKeyClass" autocapitalize="off" autocomplete="off"
spellcheck="off">
<p class="text-xs italic">This is not stored, but only used to sign a token which is stored instead.</p>
</div>
<div class="mt-3">
<label for="duration">Delegated Duration</label>
<br>
<select x-model.number="duration" class="input">
<option value="1">1 day</option>
<option value="7">7 days</option>
<option value="30">30 days</option>
</select>
</div>
</div>
<div class="mt-6">
<button class="button" @click.prevent="goBack">Back</button>
<button class="button" @click.prevent="save" :disabled="!isKeyValid">Save</button>
</div>
</body>
</html>

View File

@@ -0,0 +1,82 @@
import Alpine from 'alpinejs';
import { generateProfile, getProfiles, validateKey } from './utils';
import { getPublicKey, nip26, nip19 } from 'nostr-tools';
const storage = browser.storage.local;
Alpine.data('delegated', () => ({
privKey: '',
duration: 7,
profile: {},
async init() {
this.profile = await generateProfile('New Delegate');
this.profile.delegate = true;
},
openNip(event) {
browser.tabs.create({ url: event.target.href, active: true });
},
goBack() {
window.location = browser.runtime.getURL('options.html');
},
async save() {
let profiles = await getProfiles();
// We need to jankify this Alpine proxy object so it's in the right format
// when we save it to storage
let profile = JSON.parse(JSON.stringify(this.profile));
profile.delegator = getPublicKey(this.decodedPrivKey);
profile.delegation = this.getDelegation();
profiles.push(profile);
let profileIndex = profiles.length - 1;
await storage.set({ profiles, profileIndex });
window.location = `${browser.runtime.getURL(
'options.html'
)}?index=${profileIndex}`;
},
getDelegation() {
let pubkey = getPublicKey(this.profile.privKey);
let delegation = nip26.createDelegation(this.decodedPrivKey, {
pubkey,
until: this.until,
since: Math.round(Date.now() / 1000) - 1,
});
console.log(delegation);
return delegation;
},
// Properties
get isKeyValid() {
return validateKey(this.privKey);
},
get validKeyClass() {
return this.isKeyValid
? ''
: 'ring-2 ring-rose-500 focus:ring-2 focus:ring-rose-500 border-transparent focus:border-transparent';
},
get until() {
return Math.round(Date.now() / 1000) + 60 * 60 * 24 * this.duration;
},
get decodedPrivKey() {
if (!this.isKeyValid) {
return null;
}
if (this.privKey.startsWith('nsec')) {
return nip19.decode(this.privKey).data;
}
return this.privKey;
},
}));
Alpine.start();

View File

@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="options.build.css">
<script src="experimental.build.js" defer></script>
<title>Experimental Features</title>
</head>
<body class="text-fuchsia-900 p-3.5 lg:p-32" x-data="experimental">
<p>
<a href="options.html" class="border-none hover:underline">← Back</a>
</p>
<h1 class="text-3xl lg:text-6xl font-bold md:text-center">Experimental Features</h1>
<p class="mt-3 text-center font-bold italic">
These things may only work partially, or not
work at all. Caveat
emptor!
</p>
<template x-for="feature in features" :key="feature[0]">
<div class="mt-4">
<input class="checkbox" type="checkbox" :id="feature[0]" x-model="feature[1]"
@change="change(feature[0], feature[1])">
<label :for="feature[0]" x-text="feature[2]" class="font-bold"></label>
<p x-text="feature[3]" class="italic"></p>
</div>
</template>
</body>
</html>

View File

@@ -0,0 +1,40 @@
import Alpine from 'alpinejs';
const FEATURES = [
[
'delegation',
'NIP-26 Delegation Profiles',
'Allow user to create delegated profiles that obey the NIP-26 standard. Requires client support.',
],
];
Alpine.data('experimental', () => ({
features: [],
async init() {
await this.reloadFeatures();
console.log(this.features);
},
async reloadFeatures() {
this.features = await Promise.all(
FEATURES.map(async ([name, shortDesc, longDesc]) => {
name = `feature:${name}`;
let active = await browser.storage.local.get({
[name]: false,
});
active = active[name];
return [name, active, shortDesc, longDesc];
})
);
},
async change(feature, active) {
console.log(feature, active);
await browser.storage.local.set({ [feature]: active });
await this.reloadFeatures();
},
}));
Alpine.start();

View File

@@ -49,7 +49,10 @@
"popup.build.js",
"options.build.js",
"options.build.css",
"options.html"
"options.html",
"delegation_wizard.html",
"delegation_wizard.build.js",
"delegation_wizard.build.css"
],
"matches": [
"<all_urls>"

View File

@@ -9,7 +9,7 @@
</head>
<body x-data="options" class="text-fuchsia-900 p-3.5 lg:p-32">
<body x-data="options" class="text-fuchsia-900 p-3.5 lg:p-32">
<h1 class="text-3xl lg:text-6xl font-bold md:text-center">Settings</h1>
<!-- PROFILES -->
@@ -22,7 +22,8 @@
</template>
</select>
<div class="block md:inline p-3 pl-0 md:p-0">
<button class="button" @click.prevent="await newProfile()">New</button>
<button class="button" @click.prevent="newProfile">New</button>
<button class="button" @click.prevent="newDelegated" x-show="delegationActive">New Delegate</button>
<button class="button" @click.prevent="deleteProfile">Delete</button>
</div>
</div>
@@ -155,9 +156,13 @@
</div>
<div class="mt-6">
<button class="button" @click.prevent="window.close()">Close</button>
<button class="button" @click.prevent="closeOptions">Close</button>
<button class="button" @click.prevent="clearData">Clear Data</button>
</div>
<div class="mt-6">
<a href="experimental.html" class="border-none hover:underline">Experimental features →</a>
</div>
</body>
</html>

View File

@@ -16,6 +16,7 @@ import {
KINDS,
humanPermission,
validateKey,
feature,
} from './utils';
const log = console.log;
@@ -36,6 +37,7 @@ Alpine.data('options', () => ({
host: '',
permHosts: [],
hostPerms: [],
delegationActive: false,
setPermission,
async init(watch = true) {
@@ -63,7 +65,10 @@ Alpine.data('options', () => ({
// on init to set the correct profile.
await this.getProfileNames();
await this.getProfileIndex();
this.setProfileIndexFromSearch();
await this.refreshProfile();
this.delegationActive = await feature('delegation');
},
async refreshProfile() {
@@ -77,6 +82,15 @@ Alpine.data('options', () => ({
// Profile functions
setProfileIndexFromSearch() {
let p = new URLSearchParams(window.location.search);
let index = p.get('index');
if (!index) {
return;
}
this.profileIndex = parseInt(index);
},
async getProfileNames() {
this.profileNames = await getProfileNames();
},
@@ -98,6 +112,10 @@ Alpine.data('options', () => ({
this.profileIndex = newIndex;
},
newDelegated() {
window.location = browser.runtime.getURL('delegation_wizard.html');
},
async deleteProfile() {
if (
confirm(
@@ -236,6 +254,11 @@ Alpine.data('options', () => ({
}
},
async closeOptions() {
const tab = await browser.tabs.getCurrent();
await browser.tabs.remove(tab.id);
},
// Properties
get recommendedRelays() {

View File

@@ -27,7 +27,7 @@
relays now?
</span>
<br>
<button @click="await addRelays()">Add Relays</button>
<button class="button" @click="await addRelays()">Add Relays</button>
</div>
<div class="help">

View File

@@ -1,4 +1,4 @@
const DB_VERSION = 1;
const DB_VERSION = 2;
const storage = browser.storage.local;
export const RECOMMENDED_RELAYS = [
new URL('wss://relay.damus.io'),
@@ -41,6 +41,14 @@ async function migrate(version, goal) {
await storage.set({ profiles });
return version + 1;
}
if (version === 1) {
console.log('migrating to version 2.');
let profiles = await getProfiles();
profiles.forEach(profile => (profile.delegate = false));
await storage.set({ profiles });
return version + 1;
}
}
export async function getProfiles() {
@@ -98,6 +106,7 @@ export async function generateProfile(name = 'Default') {
privKey: await generatePrivateKey(),
hosts: {},
relays: [],
delegate: false,
};
}
@@ -213,3 +222,9 @@ export function validateKey(key) {
return hexMatch || b32Match;
}
export async function feature(name) {
let fname = `feature:${name}`;
let f = await browser.storage.local.get({ [fname]: false });
return f[fname];
}

View File

@@ -19,6 +19,10 @@ require('esbuild')
'popup.build': './Shared (Extension)/Resources/popup.js',
'options.build': './Shared (Extension)/Resources/options.js',
'permission.build': './Shared (Extension)/Resources/permission.js',
'experimental.build':
'./Shared (Extension)/Resources/experimental.js',
'delegation_wizard.build':
'./Shared (Extension)/Resources/delegation_wizard.js',
},
outdir: './Shared (Extension)/Resources',
sourcemap: 'inline',

8
package-lock.json generated
View File

@@ -12,7 +12,7 @@
"alpinejs": "^3.10.5",
"async-mutex": "^0.4.0",
"json-format-highlight": "^1.0.4",
"nostr-tools": "^1.2.1"
"nostr-tools": "^1.3.0"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.3",
@@ -965,9 +965,9 @@
}
},
"node_modules/nostr-tools": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.2.1.tgz",
"integrity": "sha512-SL0sst29mrQ7oUPGQn+NMH4yuTe69a8S4bliNpYB2IG0fDl3Cx+xSLnuCTb4nZiNalatYsA5l+751wQiDGA3+A==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.3.0.tgz",
"integrity": "sha512-AJosFolIhMFfs34sAX1V9kXV2/kDDGzfIVVEFK3u92Q96zTn9dekED6PybZXmKGNniUKfuy1h76oBPeK1MRVQw==",
"dependencies": {
"@noble/hashes": "^0.5.7",
"@noble/secp256k1": "^1.7.0",

View File

@@ -20,7 +20,7 @@
"alpinejs": "^3.10.5",
"async-mutex": "^0.4.0",
"json-format-highlight": "^1.0.4",
"nostr-tools": "^1.2.1"
"nostr-tools": "^1.3.0"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.3",