forked from cory/tildefriends
		
	Compare commits
	
		
			10 Commits
		
	
	
		
			5e72c9caf4
			...
			user_setti
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 58dbf42a3a | |||
| a1f221879b | |||
| 2a928dcafc | |||
| 5474c5a101 | |||
| 4b7261fa20 | |||
| 4992ff3a2d | |||
| 88ee0aa6f0 | |||
| 392206c19e | |||
| 
						
						
							
						
						f9e95e5733
	
				 | 
					
					
						|||
| 
						
						
							
						
						1444c945de
	
				 | 
					
					
						
@@ -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
									
								
								apps/user_settings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								apps/user_settings.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					{"type": "tildefriends-app", "emoji": "⚙️"}
 | 
				
			||||||
							
								
								
									
										60
									
								
								apps/user_settings/app.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								apps/user_settings/app.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					import * as tfrpc from '/tfrpc.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tfrpc.register(async function getIdentities() {
 | 
				
			||||||
 | 
						return ssb.getIdentities();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					tfrpc.register(async function createID(id) {
 | 
				
			||||||
 | 
						return await ssb.createIdentity();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					tfrpc.register(async function 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() {
 | 
				
			||||||
 | 
						// TODO
 | 
				
			||||||
 | 
						return ['solarized', 'gruvbox', 'light'];
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					tfrpc.register(async function getTheme() {
 | 
				
			||||||
 | 
						// TODO
 | 
				
			||||||
 | 
						return 'gruvbox';
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					tfrpc.register(async function setTheme() {
 | 
				
			||||||
 | 
						// TODO
 | 
				
			||||||
 | 
						console.warn('setTheme called - not implemented');
 | 
				
			||||||
 | 
						return null;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					tfrpc.register(async function reload() {
 | 
				
			||||||
 | 
						await main();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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-v1.css"/>
 | 
				
			||||||
 | 
								<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>Manage your identities</h3>
 | 
				
			||||||
 | 
						<tf-identity-manager></tf-identity-manager>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<h3>Change my password</h3>
 | 
				
			||||||
 | 
						<tf-password-form></tf-password-form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<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
											
										
									
								
							
							
								
								
									
										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-v1.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);
 | 
				
			||||||
							
								
								
									
										118
									
								
								apps/user_settings/tf-identity-manager.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								apps/user_settings/tf-identity-manager.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
				
			|||||||
 | 
					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 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) {
 | 
				
			||||||
 | 
							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() {
 | 
				
			||||||
 | 
							return html` <link rel="stylesheet" href="/static/tildefriends-v1.css" />
 | 
				
			||||||
 | 
								<style>
 | 
				
			||||||
 | 
									.id-span {
 | 
				
			||||||
 | 
										font-family: monospace;
 | 
				
			||||||
 | 
										margin-left: 8px;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<h4>Create a new identity</h4>
 | 
				
			||||||
 | 
								<button id="create-id" class="green" @click=${this.createIdentity}>
 | 
				
			||||||
 | 
									Create Identity
 | 
				
			||||||
 | 
								</button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<h4>Import an SSB Identity from 12 BIP39 English Words</h4>
 | 
				
			||||||
 | 
								<textarea id="import-id-textarea" style="width: 100%" rows="4"></textarea>
 | 
				
			||||||
 | 
								<button class="green" @click=${this.importIdentity}>
 | 
				
			||||||
 | 
									Import Identity
 | 
				
			||||||
 | 
								</button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<h4>Warning !</h4>
 | 
				
			||||||
 | 
								<strong
 | 
				
			||||||
 | 
									>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 !
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<ul>
 | 
				
			||||||
 | 
									${this.ids.map(
 | 
				
			||||||
 | 
										(id) =>
 | 
				
			||||||
 | 
											html` <li>
 | 
				
			||||||
 | 
												<button class="blue" @click=${() => this.exportIdentity(id)}>
 | 
				
			||||||
 | 
													Export Identity
 | 
				
			||||||
 | 
												</button>
 | 
				
			||||||
 | 
												<button class="red" @click=${() => this.deleteIdentity(id)}>
 | 
				
			||||||
 | 
													Delete Identity
 | 
				
			||||||
 | 
												</button>
 | 
				
			||||||
 | 
												<span class="id-span">${id}</span>
 | 
				
			||||||
 | 
											</li>`
 | 
				
			||||||
 | 
									)}
 | 
				
			||||||
 | 
								</ul>`;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					customElements.define('tf-identity-manager', TfIdentityManagerElement);
 | 
				
			||||||
							
								
								
									
										82
									
								
								apps/user_settings/tf-password-form.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								apps/user_settings/tf-password-form.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					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-v1.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);
 | 
				
			||||||
							
								
								
									
										51
									
								
								apps/user_settings/tf-theme-picker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								apps/user_settings/tf-theme-picker.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					import {LitElement, html, nothing} 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();
 | 
				
			||||||
 | 
							this.selected = await tfrpc.rpc.getTheme();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let select = this.renderRoot?.querySelector('#theme-select');
 | 
				
			||||||
 | 
							select.value = this.selected;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						changed(event) {
 | 
				
			||||||
 | 
							this.selected = event.srcElement.value;
 | 
				
			||||||
 | 
							console.log('selected theme', this.selected);
 | 
				
			||||||
 | 
							// TODO
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						render() {
 | 
				
			||||||
 | 
							return html`
 | 
				
			||||||
 | 
								<link rel="stylesheet" href="/static/tildefriends-v1.css" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<label for="theme">[Not implemented] Choose your theme:</label>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<select
 | 
				
			||||||
 | 
									name="theme"
 | 
				
			||||||
 | 
									id="theme-select"
 | 
				
			||||||
 | 
									?hidden=${!this.themes?.length}
 | 
				
			||||||
 | 
									@change=${this.changed}
 | 
				
			||||||
 | 
								>
 | 
				
			||||||
 | 
									${(this.themes ?? []).map(
 | 
				
			||||||
 | 
										(name) => html`<option value=${name}>${name}</option>`
 | 
				
			||||||
 | 
									)}
 | 
				
			||||||
 | 
								</select>
 | 
				
			||||||
 | 
							`;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					customElements.define('tf-theme-picker', TfThemePickerElement);
 | 
				
			||||||
							
								
								
									
										114
									
								
								core/tildefriends-v1.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								core/tildefriends-v1.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 / 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