forked from cory/tildefriends
Compare commits
5 Commits
tasiaiso-s
...
4b7261fa20
Author | SHA1 | Date | |
---|---|---|---|
4b7261fa20 | |||
88ee0aa6f0 | |||
392206c19e | |||
f9e95e5733
|
|||
1444c945de
|
4
apps/user_settings.json
Normal file
4
apps/user_settings.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"type": "tildefriends-app",
|
||||||
|
"emoji": "⚙️"
|
||||||
|
}
|
46
apps/user_settings/app.js
Normal file
46
apps/user_settings/app.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import * as tfrpc from '/tfrpc.js';
|
||||||
|
|
||||||
|
tfrpc.register(async function getIdentities() {
|
||||||
|
return ssb.getIdentities();
|
||||||
|
});
|
||||||
|
tfrpc.register(async function getPrivateKey(id) {
|
||||||
|
return bip39Words(await ssb.getPrivateKey(id));
|
||||||
|
});
|
||||||
|
tfrpc.register(async function getThemes() {
|
||||||
|
// TODO
|
||||||
|
return ['solarized', 'gruvbox', 'light'];
|
||||||
|
});
|
||||||
|
tfrpc.register(async function setTheme() {
|
||||||
|
// TODO
|
||||||
|
console.warn("setTheme called - not implemented")
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
// Get body.html
|
||||||
|
const body = utf8Decode(await getFile('body.html'));
|
||||||
|
|
||||||
|
// Build the document
|
||||||
|
const document = `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="/static/tildefriends-latest.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-password-form.js" type="module"></script>
|
||||||
|
<script src="tf-delete-account-btn.js" type="module"></script>
|
||||||
|
<script src="tf-identity-manager.js" type="module"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="flex-column">
|
||||||
|
${body}
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
|
||||||
|
// Send it to the browser
|
||||||
|
app.setDocument(document);
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
20
apps/user_settings/body.html
Normal file
20
apps/user_settings/body.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<h1>Your settings</h1>
|
||||||
|
|
||||||
|
<div class="box flex-column">
|
||||||
|
<h2>Appearance</h2>
|
||||||
|
|
||||||
|
<tf-theme-picker></tf-theme-picker>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box flex-column">
|
||||||
|
<h2>Danger Zone</h2>
|
||||||
|
|
||||||
|
<h3>Change my password</h3>
|
||||||
|
<tf-password-form></tf-password-form>
|
||||||
|
|
||||||
|
<h3>Manage your identities</h3>
|
||||||
|
<tf-identity-manager></tf-identity-manager>
|
||||||
|
|
||||||
|
<h3>Delete your account</h3>
|
||||||
|
<tf-delete-account-btn></tf-delete-account-btn>
|
||||||
|
</div>
|
120
apps/user_settings/lit-all.min.js
vendored
Normal file
120
apps/user_settings/lit-all.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
apps/user_settings/lit-all.min.js.map
Normal file
1
apps/user_settings/lit-all.min.js.map
Normal file
File diff suppressed because one or more lines are too long
1
apps/user_settings/script.js
Normal file
1
apps/user_settings/script.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
/* */
|
1
apps/user_settings/style.css
Normal file
1
apps/user_settings/style.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
/* */
|
36
apps/user_settings/tf-delete-account-btn.js
Normal file
36
apps/user_settings/tf-delete-account-btn.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import {LitElement, html} from './lit-all.min.js';
|
||||||
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
|
|
||||||
|
class TfDeleteAccountButtonElement extends LitElement {
|
||||||
|
static get properties() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAccount() {
|
||||||
|
const res = confirm(
|
||||||
|
'Are you really sure you want to delete your account ?'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res) return;
|
||||||
|
|
||||||
|
console.warn('TODO');
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<link rel="stylesheet" href="/static/tildefriends-latest.css"/>
|
||||||
|
|
||||||
|
<span>This action is irreversible !</span>
|
||||||
|
|
||||||
|
<button class="red" @click=${this.deleteAccount}>
|
||||||
|
[Not implemented] Delete my Tilde Friends account
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('tf-delete-account-btn', TfDeleteAccountButtonElement);
|
61
apps/user_settings/tf-identity-manager.js
Normal file
61
apps/user_settings/tf-identity-manager.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import {LitElement, html} from './lit-all.min.js';
|
||||||
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
|
|
||||||
|
class TfIdentityManagerElement extends LitElement {
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
ids: {type: Array},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.ids = [];
|
||||||
|
this.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
this.ids = await tfrpc.rpc.getIdentities();
|
||||||
|
}
|
||||||
|
|
||||||
|
async exportIdentity(id) {
|
||||||
|
alert('Your private key is:\n' + (await tfrpc.rpc.getPrivateKey(id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<link rel="stylesheet" href="/static/tildefriends-latest.css"/>
|
||||||
|
<style>
|
||||||
|
.id-span {
|
||||||
|
font-family: monospace;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<h4>Create a new identity</h4>
|
||||||
|
<button id="create-id" class="btn-green">[Not implemented] Create Identity</button>
|
||||||
|
|
||||||
|
<h4>Import an SSB Identity from 12 BIP39 English Words</h4>
|
||||||
|
<textarea id="add-id" style="width: 100%" rows="4"></textarea>
|
||||||
|
<button class="green">[Not implemented] Import Identity</button>
|
||||||
|
|
||||||
|
<h4>Warning !</h4>
|
||||||
|
<strong>Anybody that has access to your private key can gain total access over your account.</strong>
|
||||||
|
<br><br>
|
||||||
|
Tilde Friends' contributors will never ask you for your private key !
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
${this.ids.map(
|
||||||
|
(id) =>
|
||||||
|
html`
|
||||||
|
<li>
|
||||||
|
<button class="blue" @click=${() => this.exportIdentity(id)}>Export Identity</button>
|
||||||
|
<button class="red">[Not implemented] Delete Identity</button>
|
||||||
|
<span class="id-span">${id}</span>
|
||||||
|
</li>`
|
||||||
|
)}
|
||||||
|
</ul>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('tf-identity-manager', TfIdentityManagerElement);
|
68
apps/user_settings/tf-password-form.js
Normal file
68
apps/user_settings/tf-password-form.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import {LitElement, html} from './lit-all.min.js';
|
||||||
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
|
|
||||||
|
class TfPasswordFormElement extends LitElement {
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
//selected: {type: String},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks a password against different requirements
|
||||||
|
* @param {string} password the password to validate
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
validatePassword(password) {
|
||||||
|
// TODO(tasiaiso)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
submitPassword() {
|
||||||
|
const currentPwd = this.shadowRoot.getElementById('current').value;
|
||||||
|
const newPwd = this.shadowRoot.getElementById('new').value;
|
||||||
|
const repeatPwd = this.shadowRoot.getElementById('Repeat').value;
|
||||||
|
|
||||||
|
if (!(newPwd === repeatPwd)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// tfrpc.changePassword()
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<link rel="stylesheet" href="/static/tildefriends-latest.css"/>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="grid">
|
||||||
|
<label for="current">Current password:</label>
|
||||||
|
<input type="password" id="current" name="current" autocomplete="current-password" />
|
||||||
|
|
||||||
|
<label for="new">Enter new password:</label>
|
||||||
|
<input type="password" id="new" name="new" autocomplete="new-password" />
|
||||||
|
|
||||||
|
<label for="repeat">Repeat new password:</label>
|
||||||
|
<input type="password" id="repeat" name="repeat" autocomplete="new-password" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button @click=${this.submitPassword} class="red">
|
||||||
|
[Not implemented] Change my password
|
||||||
|
</button>
|
||||||
|
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('tf-password-form', TfPasswordFormElement);
|
40
apps/user_settings/tf-theme-picker.js
Normal file
40
apps/user_settings/tf-theme-picker.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import {LitElement, html} from './lit-all.min.js';
|
||||||
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
|
|
||||||
|
class TfThemePickerElement extends LitElement {
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
selected: {type: String},
|
||||||
|
themes: {type: Array},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
this.themes = await tfrpc.rpc.getThemes();
|
||||||
|
}
|
||||||
|
|
||||||
|
changed(event) {
|
||||||
|
this.selected = event.srcElement.value;
|
||||||
|
console.log('selected theme', this.selected);
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<link rel="stylesheet" href="/static/tildefriends-latest.css"/>
|
||||||
|
|
||||||
|
<label for="theme">[Not implemented] Choose your theme:</label>
|
||||||
|
|
||||||
|
<select name="theme" ?hidden=${!this.themes?.length} @change=${this.changed}>
|
||||||
|
${(this.themes ?? []).map((id) => html`<option value=${id}>${id}</option>`)}
|
||||||
|
</select>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('tf-theme-picker', TfThemePickerElement);
|
113
core/tildefriends-v1.css
Normal file
113
core/tildefriends-v1.css
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Tilde Friends core stylesheet
|
||||||
|
*
|
||||||
|
* This Software is an external library that is part of
|
||||||
|
* Tilde Friends and is shared under the MIT license.
|
||||||
|
*
|
||||||
|
* Inject this file in your app at tildefriends.css
|
||||||
|
* and use this tag to import it:
|
||||||
|
* <link rel="stylesheet" href="/static/tildefriends-v1.css"/>
|
||||||
|
*
|
||||||
|
* v1.0.0 / 2024 M03 21
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
.button,
|
||||||
|
input[type=button],
|
||||||
|
input[type=submit],
|
||||||
|
select {
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 4px;
|
||||||
|
|
||||||
|
&.red {
|
||||||
|
background-color: #bd1e24;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.green {
|
||||||
|
background-color: #18922d;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.blue {
|
||||||
|
background-color: #0067a7;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.yellow {
|
||||||
|
background-color: #ee9600;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(0.75);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a:link {
|
||||||
|
color: #268bd2;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited {
|
||||||
|
color: #6c71c4;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:active {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td, th {
|
||||||
|
border: 1px solid #ffffff40;
|
||||||
|
text-align: left;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background-color: #ffffff20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-column {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-flex-row {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
background-color: #00000020;
|
||||||
|
border: 1px solid grey;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
margin: 4px;
|
||||||
|
}
|
@ -644,6 +644,7 @@ static void _httpd_endpoint_static(tf_http_request_t* request)
|
|||||||
"style.css",
|
"style.css",
|
||||||
"tfrpc.js",
|
"tfrpc.js",
|
||||||
"w3.css",
|
"w3.css",
|
||||||
|
"tildefriends-v1.css"
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* k_map[][2] = {
|
const char* k_map[][2] = {
|
||||||
|
Reference in New Issue
Block a user