forked from cory/tildefriends
Compare commits
5 Commits
4b7261fa20
...
user_setti
Author | SHA1 | Date | |
---|---|---|---|
58dbf42a3a | |||
a1f221879b | |||
2a928dcafc | |||
5474c5a101 | |||
4992ff3a2d |
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "tildefriends-app",
|
|
||||||
"emoji": "🪪",
|
|
||||||
"previous": "&kgukkyDk1RxgfzgMH6H/0QeDPIuwPZypLuAFax21ljk=.sha256"
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
import * as tfrpc from '/tfrpc.js';
|
|
||||||
|
|
||||||
tfrpc.register(async function get_private_key(id) {
|
|
||||||
return bip39Words(await ssb.getPrivateKey(id));
|
|
||||||
});
|
|
||||||
tfrpc.register(async function create_id(id) {
|
|
||||||
return await ssb.createIdentity();
|
|
||||||
});
|
|
||||||
tfrpc.register(async function add_id(id) {
|
|
||||||
return await ssb.addIdentity(bip39Bytes(id));
|
|
||||||
});
|
|
||||||
tfrpc.register(async function delete_id(id) {
|
|
||||||
return await ssb.deleteIdentity(id);
|
|
||||||
});
|
|
||||||
tfrpc.register(async function reload() {
|
|
||||||
await main();
|
|
||||||
});
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
let ids = await ssb.getIdentities();
|
|
||||||
await app.setDocument(
|
|
||||||
`<body style="color: #fff">
|
|
||||||
<script>const handler = {};</script>
|
|
||||||
<script type="module">
|
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
|
||||||
handler.export_id = async function export_id(event) {
|
|
||||||
let id = event.srcElement.dataset.id;
|
|
||||||
let element = document.createElement('textarea');
|
|
||||||
element.value = await tfrpc.rpc.get_private_key(id);
|
|
||||||
element.style = 'width: 100%; read-only: true';
|
|
||||||
element.readOnly = true;
|
|
||||||
event.srcElement.parentElement.appendChild(element);
|
|
||||||
event.srcElement.onclick = event => handler.hide_id(event, element);
|
|
||||||
}
|
|
||||||
handler.add_id = async function add_id(event) {
|
|
||||||
let id = document.getElementById('add_id').value;
|
|
||||||
try {
|
|
||||||
let new_id = await tfrpc.rpc.add_id(id);
|
|
||||||
alert('Successfully imported: ' + new_id);
|
|
||||||
await tfrpc.rpc.reload();
|
|
||||||
} catch (e) {
|
|
||||||
alert('Error importing identity: ' + e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handler.create_id = async function create_id(event) {
|
|
||||||
try {
|
|
||||||
let id = await tfrpc.rpc.create_id();
|
|
||||||
alert('Successfully created: ' + id);
|
|
||||||
await tfrpc.rpc.reload();
|
|
||||||
} catch (e) {
|
|
||||||
alert('Error creating identity: ' + e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handler.hide_id = function hide_id(event, element) {
|
|
||||||
element.parentNode.removeChild(element);
|
|
||||||
event.srcElement.onclick = handler.export_id;
|
|
||||||
}
|
|
||||||
handler.delete_id = async function delete_id(event) {
|
|
||||||
let id = event.srcElement.dataset.id;
|
|
||||||
try {
|
|
||||||
if (prompt('Are you sure you want to delete "' + id + '"? It cannot be recovered without the exported phrase.\\n\\nEnter the word "DELETE" to confirm you wish to delete it.') === 'DELETE') {
|
|
||||||
if (await tfrpc.rpc.delete_id(id)) {
|
|
||||||
alert('Successfully deleted ID: ' + id);
|
|
||||||
}
|
|
||||||
await tfrpc.rpc.reload();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
alert('Error deleting ID: ' + e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<h1>SSB Identity Management</h1>
|
|
||||||
<h2>Create a new identity</h2>
|
|
||||||
<button id="create_id" onclick="handler.create_id()">Create Identity</button>
|
|
||||||
<h2>Import an SSB Identity from 12 BIP39 English Words</h2>
|
|
||||||
<textarea id="add_id" style="width: 100%" rows="4"></textarea><button id="add" onclick="handler.add_id(event)">Import Identity</button>
|
|
||||||
<h2>Identities</h2>
|
|
||||||
<ul>` +
|
|
||||||
ids
|
|
||||||
.map(
|
|
||||||
(id) => `<li>
|
|
||||||
<button onclick="handler.export_id(event)" data-id="${id}">Export Identity</button>
|
|
||||||
<button onclick="handler.delete_id(event)" data-id="${id}">Delete Identity</button>
|
|
||||||
${id}
|
|
||||||
</li>`
|
|
||||||
)
|
|
||||||
.join('\n') +
|
|
||||||
` </ul>
|
|
||||||
</body>`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
@ -1,4 +1 @@
|
|||||||
{
|
{"type": "tildefriends-app", "emoji": "⚙️"}
|
||||||
"type": "tildefriends-app",
|
|
||||||
"emoji": "⚙️"
|
|
||||||
}
|
|
||||||
|
@ -3,18 +3,34 @@ import * as tfrpc from '/tfrpc.js';
|
|||||||
tfrpc.register(async function getIdentities() {
|
tfrpc.register(async function getIdentities() {
|
||||||
return ssb.getIdentities();
|
return ssb.getIdentities();
|
||||||
});
|
});
|
||||||
|
tfrpc.register(async function createID(id) {
|
||||||
|
return await ssb.createIdentity();
|
||||||
|
});
|
||||||
tfrpc.register(async function getPrivateKey(id) {
|
tfrpc.register(async function getPrivateKey(id) {
|
||||||
return bip39Words(await ssb.getPrivateKey(id));
|
return bip39Words(await ssb.getPrivateKey(id));
|
||||||
});
|
});
|
||||||
|
tfrpc.register(async function addID(id) {
|
||||||
|
return await ssb.addIdentity(bip39Bytes(id));
|
||||||
|
});
|
||||||
|
tfrpc.register(async function deleteID(id) {
|
||||||
|
return await ssb.deleteIdentity(id);
|
||||||
|
});
|
||||||
tfrpc.register(async function getThemes() {
|
tfrpc.register(async function getThemes() {
|
||||||
// TODO
|
// TODO
|
||||||
return ['solarized', 'gruvbox', 'light'];
|
return ['solarized', 'gruvbox', 'light'];
|
||||||
});
|
});
|
||||||
|
tfrpc.register(async function getTheme() {
|
||||||
|
// TODO
|
||||||
|
return 'gruvbox';
|
||||||
|
});
|
||||||
tfrpc.register(async function setTheme() {
|
tfrpc.register(async function setTheme() {
|
||||||
// TODO
|
// TODO
|
||||||
console.warn("setTheme called - not implemented")
|
console.warn('setTheme called - not implemented');
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
tfrpc.register(async function reload() {
|
||||||
|
await main();
|
||||||
|
});
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
// Get body.html
|
// Get body.html
|
||||||
@ -25,9 +41,7 @@ async function main() {
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link rel="stylesheet" href="/static/tildefriends-latest.css"/>
|
<link rel="stylesheet" href="/static/tildefriends-v1.css"/>
|
||||||
<link rel="stylesheet" href="style.css"/>
|
|
||||||
<script src="script.js" type="module"></script>
|
|
||||||
<script src="tf-theme-picker.js" type="module"></script>
|
<script src="tf-theme-picker.js" type="module"></script>
|
||||||
<script src="tf-password-form.js" type="module"></script>
|
<script src="tf-password-form.js" type="module"></script>
|
||||||
<script src="tf-delete-account-btn.js" type="module"></script>
|
<script src="tf-delete-account-btn.js" type="module"></script>
|
||||||
|
@ -9,12 +9,12 @@
|
|||||||
<div class="box flex-column">
|
<div class="box flex-column">
|
||||||
<h2>Danger Zone</h2>
|
<h2>Danger Zone</h2>
|
||||||
|
|
||||||
<h3>Change my password</h3>
|
|
||||||
<tf-password-form></tf-password-form>
|
|
||||||
|
|
||||||
<h3>Manage your identities</h3>
|
<h3>Manage your identities</h3>
|
||||||
<tf-identity-manager></tf-identity-manager>
|
<tf-identity-manager></tf-identity-manager>
|
||||||
|
|
||||||
|
<h3>Change my password</h3>
|
||||||
|
<tf-password-form></tf-password-form>
|
||||||
|
|
||||||
<h3>Delete your account</h3>
|
<h3>Delete your account</h3>
|
||||||
<tf-delete-account-btn></tf-delete-account-btn>
|
<tf-delete-account-btn></tf-delete-account-btn>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1 +0,0 @@
|
|||||||
/* */
|
|
@ -1 +0,0 @@
|
|||||||
/* */
|
|
@ -22,7 +22,7 @@ class TfDeleteAccountButtonElement extends LitElement {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<link rel="stylesheet" href="/static/tildefriends-latest.css"/>
|
<link rel="stylesheet" href="/static/tildefriends-v1.css" />
|
||||||
|
|
||||||
<span>This action is irreversible !</span>
|
<span>This action is irreversible !</span>
|
||||||
|
|
||||||
|
@ -18,13 +18,60 @@ class TfIdentityManagerElement extends LitElement {
|
|||||||
this.ids = await tfrpc.rpc.getIdentities();
|
this.ids = await tfrpc.rpc.getIdentities();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async createIdentity() {
|
||||||
|
try {
|
||||||
|
const id = await tfrpc.rpc.createID();
|
||||||
|
alert('Successfully created: ' + id);
|
||||||
|
await tfrpc.rpc.reload();
|
||||||
|
} catch (err) {
|
||||||
|
alert('Error creating identity: ' + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async importIdentity() {
|
||||||
|
const words = this.renderRoot?.querySelector('#import-id-textarea').value;
|
||||||
|
if (!words) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const newID = await tfrpc.rpc.addID(words);
|
||||||
|
|
||||||
|
if (newID) alert('Successfully imported a new identity.');
|
||||||
|
else alert('This identity already exists or is invalid.');
|
||||||
|
await tfrpc.rpc.reload();
|
||||||
|
} catch (err) {
|
||||||
|
alert('Error importing identity: ' + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async exportIdentity(id) {
|
async exportIdentity(id) {
|
||||||
alert('Your private key is:\n' + (await tfrpc.rpc.getPrivateKey(id)));
|
alert(
|
||||||
|
'Your private key is:\n' +
|
||||||
|
(await tfrpc.rpc.getPrivateKey(id)) +
|
||||||
|
'\nDo not share it with anyone!'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteIdentity(id) {
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
prompt(
|
||||||
|
'Are you sure you want to delete "' +
|
||||||
|
id +
|
||||||
|
'"? It cannot be recovered without the exported phrase.\\n\\nEnter the word "DELETE" to confirm you wish to delete it.'
|
||||||
|
) === 'DELETE'
|
||||||
|
) {
|
||||||
|
if (await tfrpc.rpc.deleteID(id)) {
|
||||||
|
alert('Successfully deleted ID: ' + id);
|
||||||
|
}
|
||||||
|
await tfrpc.rpc.reload();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert('Error deleting ID: ' + e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html` <link rel="stylesheet" href="/static/tildefriends-v1.css" />
|
||||||
<link rel="stylesheet" href="/static/tildefriends-latest.css"/>
|
|
||||||
<style>
|
<style>
|
||||||
.id-span {
|
.id-span {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
@ -33,29 +80,39 @@ class TfIdentityManagerElement extends LitElement {
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<h4>Create a new identity</h4>
|
<h4>Create a new identity</h4>
|
||||||
<button id="create-id" class="btn-green">[Not implemented] Create Identity</button>
|
<button id="create-id" class="green" @click=${this.createIdentity}>
|
||||||
|
Create Identity
|
||||||
|
</button>
|
||||||
|
|
||||||
<h4>Import an SSB Identity from 12 BIP39 English Words</h4>
|
<h4>Import an SSB Identity from 12 BIP39 English Words</h4>
|
||||||
<textarea id="add-id" style="width: 100%" rows="4"></textarea>
|
<textarea id="import-id-textarea" style="width: 100%" rows="4"></textarea>
|
||||||
<button class="green">[Not implemented] Import Identity</button>
|
<button class="green" @click=${this.importIdentity}>
|
||||||
|
Import Identity
|
||||||
|
</button>
|
||||||
|
|
||||||
<h4>Warning !</h4>
|
<h4>Warning !</h4>
|
||||||
<strong>Anybody that has access to your private key can gain total access over your account.</strong>
|
<strong
|
||||||
<br><br>
|
>Anybody that knows your private key can gain total access over your
|
||||||
|
account.</strong
|
||||||
|
>
|
||||||
|
<br /><br />
|
||||||
Tilde Friends' contributors will never ask you for your private key !
|
Tilde Friends' contributors will never ask you for your private key !
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
${this.ids.map(
|
${this.ids.map(
|
||||||
(id) =>
|
(id) =>
|
||||||
html`
|
html` <li>
|
||||||
<li>
|
<button class="blue" @click=${() => this.exportIdentity(id)}>
|
||||||
<button class="blue" @click=${() => this.exportIdentity(id)}>Export Identity</button>
|
Export Identity
|
||||||
<button class="red">[Not implemented] Delete Identity</button>
|
</button>
|
||||||
<span class="id-span">${id}</span>
|
<button class="red" @click=${() => this.deleteIdentity(id)}>
|
||||||
</li>`
|
Delete Identity
|
||||||
)}
|
</button>
|
||||||
|
<span class="id-span">${id}</span>
|
||||||
|
</li>`
|
||||||
|
)}
|
||||||
</ul>`;
|
</ul>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('tf-identity-manager', TfIdentityManagerElement);
|
customElements.define('tf-identity-manager', TfIdentityManagerElement);
|
||||||
|
@ -37,7 +37,7 @@ class TfPasswordFormElement extends LitElement {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<link rel="stylesheet" href="/static/tildefriends-latest.css"/>
|
<link rel="stylesheet" href="/static/tildefriends-v1.css" />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.grid {
|
.grid {
|
||||||
@ -48,21 +48,35 @@ class TfPasswordFormElement extends LitElement {
|
|||||||
|
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<label for="current">Current password:</label>
|
<label for="current">Current password:</label>
|
||||||
<input type="password" id="current" name="current" autocomplete="current-password" />
|
<input
|
||||||
|
type="password"
|
||||||
|
id="current"
|
||||||
|
name="current"
|
||||||
|
autocomplete="current-password"
|
||||||
|
/>
|
||||||
|
|
||||||
<label for="new">Enter new password:</label>
|
<label for="new">Enter new password:</label>
|
||||||
<input type="password" id="new" name="new" autocomplete="new-password" />
|
<input
|
||||||
|
type="password"
|
||||||
|
id="new"
|
||||||
|
name="new"
|
||||||
|
autocomplete="new-password"
|
||||||
|
/>
|
||||||
|
|
||||||
<label for="repeat">Repeat new password:</label>
|
<label for="repeat">Repeat new password:</label>
|
||||||
<input type="password" id="repeat" name="repeat" autocomplete="new-password" />
|
<input
|
||||||
|
type="password"
|
||||||
|
id="repeat"
|
||||||
|
name="repeat"
|
||||||
|
autocomplete="new-password"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button @click=${this.submitPassword} class="red">
|
<button @click=${this.submitPassword} class="red">
|
||||||
[Not implemented] Change my password
|
[Not implemented] Change my password
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('tf-password-form', TfPasswordFormElement);
|
customElements.define('tf-password-form', TfPasswordFormElement);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {LitElement, html} from './lit-all.min.js';
|
import {LitElement, html, nothing} from './lit-all.min.js';
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
|
|
||||||
class TfThemePickerElement extends LitElement {
|
class TfThemePickerElement extends LitElement {
|
||||||
@ -16,6 +16,10 @@ class TfThemePickerElement extends LitElement {
|
|||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
this.themes = await tfrpc.rpc.getThemes();
|
this.themes = await tfrpc.rpc.getThemes();
|
||||||
|
this.selected = await tfrpc.rpc.getTheme();
|
||||||
|
|
||||||
|
let select = this.renderRoot?.querySelector('#theme-select');
|
||||||
|
select.value = this.selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
changed(event) {
|
changed(event) {
|
||||||
@ -26,12 +30,19 @@ class TfThemePickerElement extends LitElement {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<link rel="stylesheet" href="/static/tildefriends-latest.css"/>
|
<link rel="stylesheet" href="/static/tildefriends-v1.css" />
|
||||||
|
|
||||||
<label for="theme">[Not implemented] Choose your theme:</label>
|
<label for="theme">[Not implemented] Choose your theme:</label>
|
||||||
|
|
||||||
<select name="theme" ?hidden=${!this.themes?.length} @change=${this.changed}>
|
<select
|
||||||
${(this.themes ?? []).map((id) => html`<option value=${id}>${id}</option>`)}
|
name="theme"
|
||||||
|
id="theme-select"
|
||||||
|
?hidden=${!this.themes?.length}
|
||||||
|
@change=${this.changed}
|
||||||
|
>
|
||||||
|
${(this.themes ?? []).map(
|
||||||
|
(name) => html`<option value=${name}>${name}</option>`
|
||||||
|
)}
|
||||||
</select>
|
</select>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* and use this tag to import it:
|
* and use this tag to import it:
|
||||||
* <link rel="stylesheet" href="/static/tildefriends-v1.css"/>
|
* <link rel="stylesheet" href="/static/tildefriends-v1.css"/>
|
||||||
*
|
*
|
||||||
* v1.0.0 / 2024 M03 21
|
* v1.0 / 2024 M03 21
|
||||||
*/
|
*/
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@ -18,8 +18,8 @@ body {
|
|||||||
|
|
||||||
button,
|
button,
|
||||||
.button,
|
.button,
|
||||||
input[type=button],
|
input[type='button'],
|
||||||
input[type=submit],
|
input[type='submit'],
|
||||||
select {
|
select {
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@ -75,7 +75,8 @@ table {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
td, th {
|
td,
|
||||||
|
th {
|
||||||
border: 1px solid #ffffff40;
|
border: 1px solid #ffffff40;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
Reference in New Issue
Block a user