Compare commits
	
		
			10 Commits
		
	
	
		
			v0.0.17
			...
			user_setti
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 58dbf42a3a | |||
| a1f221879b | |||
| 2a928dcafc | |||
| 5474c5a101 | |||
| 4b7261fa20 | |||
| 4992ff3a2d | |||
| 88ee0aa6f0 | |||
| 392206c19e | |||
| f9e95e5733 | |||
| 1444c945de | 
							
								
								
									
										44
									
								
								GNUmakefile
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								GNUmakefile
									
									
									
									
									
								
							| @@ -4,7 +4,7 @@ MAKEFLAGS += --warn-undefined-variables | ||||
| MAKEFLAGS += --no-builtin-rules | ||||
|  | ||||
| VERSION_CODE := 17 | ||||
| VERSION_NUMBER := 0.0.17 | ||||
| VERSION_NUMBER := 0.0.17-wip | ||||
| VERSION_NAME := Please enjoy responsibly. | ||||
|  | ||||
| SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3450200.zip | ||||
| @@ -17,6 +17,18 @@ UNAME_M := $(shell uname -m) | ||||
|  | ||||
| ANDROID_SDK ?= ~/Android/Sdk | ||||
|  | ||||
| ifeq ($(UNAME_M),x86_64) | ||||
| ifneq ($(UNAME_S),Haiku) | ||||
| debug: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-common | ||||
| debug: LDFLAGS += -fsanitize=address -fsanitize=undefined | ||||
| endif | ||||
| endif | ||||
|  | ||||
| ifeq ($(UNAME_M),aarch64) | ||||
| debug: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-common | ||||
| debug: LDFLAGS += -fsanitize=address -fsanitize=undefined | ||||
| endif | ||||
|  | ||||
| ifeq ($(UNAME_S),Darwin) | ||||
| BUILD_TYPES := macosdebug macosrelease iosdebug iosrelease iossimdebug iossimrelease | ||||
| else ifeq ($(UNAME_S),Linux) | ||||
| @@ -210,18 +222,6 @@ $(IOS_TARGETS): LDFLAGS += -Ldeps/openssl/ios/ios64-xcrun/usr/local/lib | ||||
| $(IOSSIM_TARGETS): CFLAGS += -Ideps/openssl/ios/iossimulator-xcrun/usr/local/include | ||||
| $(IOSSIM_TARGETS): LDFLAGS += -Ldeps/openssl/ios/iossimulator-xcrun/usr/local/lib | ||||
|  | ||||
| ifeq ($(UNAME_M),x86_64) | ||||
| ifneq ($(UNAME_S),Haiku) | ||||
| out/debug/tildefriends: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-common | ||||
| out/debug/tildefriends: LDFLAGS += -fsanitize=address -fsanitize=undefined | ||||
| endif | ||||
| endif | ||||
|  | ||||
| ifeq ($(UNAME_M),aarch64) | ||||
| out/debug/tildefriends: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-common | ||||
| out/debug/tildefriends: LDFLAGS += -fsanitize=address -fsanitize=undefined | ||||
| endif | ||||
|  | ||||
| get_objs = \ | ||||
| 	$(foreach build_type,$(BUILD_TYPES),$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)))))) \ | ||||
| 	$(foreach build_type,debug release,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_unix))))) \ | ||||
| @@ -578,7 +578,7 @@ $(MINIUNZIP_OBJS): CFLAGS += \ | ||||
| LDFLAGS += \ | ||||
| 	-pthread \ | ||||
| 	-lm | ||||
| $(LINUX_TARGETS) $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \ | ||||
| debug release $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \ | ||||
| 	-lssl \ | ||||
| 	-lcrypto | ||||
| ifneq ($(UNAME_S),Haiku) | ||||
| @@ -728,7 +728,7 @@ out/apk/TildeFriends-arm-%.unsigned.apk: | ||||
| 	@cp out/apk/res.apk $@.zip | ||||
| 	@cp out/apk/classes.dex out/apk-arm-$(BUILD_TYPE)/ | ||||
| 	@cd out/apk-arm-$(BUILD_TYPE) && zip -u ../../$@.zip -q -9 -r . && cd ../../ | ||||
| 	@zip -u $@.zip -q -9 $(RAW_FILES) | ||||
| 	@zip -u $@.zip -q $(RAW_FILES) | ||||
| 	@$(ANDROID_BUILD_TOOLS)/zipalign -f 4 $@.zip $@ | ||||
|  | ||||
| out/apk/TildeFriends-x86-%.unsigned.apk: | ||||
| @@ -741,7 +741,7 @@ out/apk/TildeFriends-x86-%.unsigned.apk: | ||||
| 	@cp out/apk/res.apk $@.zip | ||||
| 	@cp out/apk/classes.dex out/apk-x86-$(BUILD_TYPE)/ | ||||
| 	@cd out/apk-x86-$(BUILD_TYPE) && zip -u ../../$@.zip -q -9 -r . && cd ../../ | ||||
| 	@zip -u $@.zip -q -9 $(RAW_FILES) | ||||
| 	@zip -u $@.zip -q $(RAW_FILES) | ||||
| 	@$(ANDROID_BUILD_TOOLS)/zipalign -f 4 $@.zip $@ | ||||
|  | ||||
| out/%.apk: out/apk/%.unsigned.apk | ||||
| @@ -769,10 +769,10 @@ out/%.app/tildefriends.png: src/ios/tildefriends.png | ||||
| 	@mkdir -p $(dir $@) | ||||
| 	@cp -v $< $@ | ||||
|  | ||||
| out/data.zip: $(RAW_FILES) | ||||
| out/%/data.zip: $(RAW_FILES) | ||||
| 	@zip -u $@ -q -9 $(RAW_FILES) | ||||
|  | ||||
| out/tildefriends-%.app/tildefriends: out/%/tildefriends out/tildefriends-%.app/Info.plist out/tildefriends-%.app/tildefriends.png out/data.zip | ||||
| out/tildefriends-%.app/tildefriends: out/%/tildefriends out/tildefriends-%.app/Info.plist out/tildefriends-%.app/tildefriends.png out/tildefriends-%.app/data.zip | ||||
| 	@mkdir -p $(dir $@) | ||||
| 	@cp -v $< $@ | ||||
| ifeq ($(HAVE_LINUX_IOS),1) | ||||
| @@ -787,12 +787,6 @@ out/tildefriends-%.ipa: out/tildefriends-ios%.app/tildefriends | ||||
| 	@cd $@.tmp/ && zip -u ../../$@ -q -9 -r ./ | ||||
| 	@rm -rf $@.tmp/ | ||||
|  | ||||
|  | ||||
| out/%/tildefriends.standalone: out/%/tildefriends out/data.zip | ||||
| 	@echo "[standalone] $@" | ||||
| 	@cat $< out/data.zip > $@ | ||||
| 	@chmod +x $@ | ||||
|  | ||||
| iossimdebug-app: out/tildefriends-iossimdebug.app/tildefriends | ||||
| iossimrelease-app: out/tildefriends-iossimrelease.app/tildefriends | ||||
| iosdebug-app: out/tildefriends-iosdebug.app/tildefriends | ||||
| @@ -857,7 +851,7 @@ dist: release-apk iosrelease-ipa | ||||
| 	@echo [archive] dist/tildefriends-$(VERSION_NUMBER).tar.xz | ||||
| 	@rm -rf out/tildefriends-$(VERSION_NUMBER) | ||||
| 	@mkdir -p dist/ out/tildefriends-$(VERSION_NUMBER) | ||||
| 	@git archive HEAD | tar -x -C out/tildefriends-$(VERSION_NUMBER) | ||||
| 	@git archive main | tar -x -C out/tildefriends-$(VERSION_NUMBER) | ||||
| 	@tar \ | ||||
| 		--exclude=apps/welcome* \ | ||||
| 		--exclude=deps/libbacktrace/Isaac.Newton-Opticks.txt \ | ||||
|   | ||||
| @@ -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); | ||||
							
								
								
									
										
											BIN
										
									
								
								bleh.tar.xz
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								bleh.tar.xz
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										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; | ||||
| } | ||||
| @@ -2,7 +2,7 @@ | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
| 	package="com.unprompted.tildefriends" | ||||
| 	android:versionCode="17" | ||||
| 	android:versionName="0.0.17"> | ||||
| 	android:versionName="0.0.17-wip"> | ||||
| 	<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="34"/> | ||||
| 	<uses-permission android:name="android.permission.INTERNET"/> | ||||
| 	<application | ||||
|   | ||||
| @@ -316,9 +316,7 @@ static void _file_read_file_zip_after_work(uv_work_t* work, int status) | ||||
| 	tf_trace_begin(trace, "file_read_zip_after_work"); | ||||
| 	if (data->result >= 0) | ||||
| 	{ | ||||
| 		JSValue array = tf_util_new_uint8_array(data->context, data->buffer, data->result); | ||||
| 		tf_task_resolve_promise(data->task, data->promise, array); | ||||
| 		JS_FreeValue(data->context, array); | ||||
| 		tf_task_resolve_promise(data->task, data->promise, tf_util_new_uint8_array(data->context, data->buffer, data->result)); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
|   | ||||
							
								
								
									
										34
									
								
								src/http.c
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								src/http.c
									
									
									
									
									
								
							| @@ -161,11 +161,10 @@ static void _http_request_destroy(tf_http_request_t* request) | ||||
| 	tf_http_close_callback* on_close = request->on_close; | ||||
| 	if (on_close) | ||||
| 	{ | ||||
| 		tf_trace_t* trace = request->http->trace; | ||||
| 		request->on_close = NULL; | ||||
| 		tf_trace_begin(trace, request->connection && request->connection->trace_name ? request->connection->trace_name : "websocket"); | ||||
| 		tf_trace_begin(request->http->trace, request->connection && request->connection->trace_name ? request->connection->trace_name : "websocket"); | ||||
| 		on_close(request); | ||||
| 		tf_trace_end(trace); | ||||
| 		tf_trace_end(request->http->trace); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -175,9 +174,12 @@ static void _http_connection_destroy(tf_http_connection_t* connection, const cha | ||||
|  | ||||
| 	if (connection->request) | ||||
| 	{ | ||||
| 		tf_http_request_t* request = connection->request; | ||||
| 		_http_request_destroy(connection->request); | ||||
| 		if (connection->request && connection->request->ref_count == 0) | ||||
| 		{ | ||||
| 			tf_free(connection->request); | ||||
| 		} | ||||
| 		connection->request = NULL; | ||||
| 		_http_request_destroy(request); | ||||
| 	} | ||||
|  | ||||
| 	if (connection->tcp.data && !uv_is_closing((uv_handle_t*)&connection->tcp)) | ||||
| @@ -381,19 +383,11 @@ static void _http_add_body_bytes(tf_http_connection_t* connection, const void* d | ||||
| 			}; | ||||
| 			connection->request = request; | ||||
|  | ||||
| 			if (!connection->http->is_shutting_down) | ||||
| 			{ | ||||
| 				tf_http_request_ref(request); | ||||
| 				tf_trace_begin(connection->http->trace, connection->trace_name ? connection->trace_name : "http"); | ||||
| 				connection->callback(request); | ||||
| 				tf_trace_end(connection->http->trace); | ||||
| 				tf_http_request_unref(request); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				const char* k_payload = tf_http_status_text(503); | ||||
| 				tf_http_respond(request, 503, NULL, 0, k_payload, strlen(k_payload)); | ||||
| 			} | ||||
| 			tf_http_request_ref(request); | ||||
| 			tf_trace_begin(connection->http->trace, connection->trace_name ? connection->trace_name : "http"); | ||||
| 			connection->callback(request); | ||||
| 			tf_trace_end(connection->http->trace); | ||||
| 			tf_http_request_unref(request); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -792,8 +786,6 @@ const char* tf_http_status_text(int status) | ||||
| 		return "File not found"; | ||||
| 	case 500: | ||||
| 		return "Internal server error"; | ||||
| 	case 503: | ||||
| 		return "Service Unavailable"; | ||||
| 	default: | ||||
| 		return "Unknown"; | ||||
| 	} | ||||
| @@ -973,11 +965,11 @@ void tf_http_request_unref(tf_http_request_t* request) | ||||
| 	tf_http_connection_t* connection = request->connection; | ||||
| 	if (--request->ref_count == 0) | ||||
| 	{ | ||||
| 		_http_request_destroy(request); | ||||
| 		if (connection) | ||||
| 		{ | ||||
| 			connection->request = NULL; | ||||
| 		} | ||||
| 		_http_request_destroy(request); | ||||
| 		tf_free(request); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -644,6 +644,7 @@ static void _httpd_endpoint_static(tf_http_request_t* request) | ||||
| 		"style.css", | ||||
| 		"tfrpc.js", | ||||
| 		"w3.css", | ||||
| 		"tildefriends-v1.css" | ||||
| 	}; | ||||
|  | ||||
| 	const char* k_map[][2] = { | ||||
|   | ||||
| @@ -11,7 +11,6 @@ | ||||
|  | ||||
| #include "backtrace.h" | ||||
| #include "sqlite3.h" | ||||
| #include "unzip.h" | ||||
|  | ||||
| #include <getopt.h> | ||||
| #include <stdlib.h> | ||||
| @@ -417,14 +416,6 @@ static int _tf_command_run(const char* file, int argc, char* argv[]) | ||||
| 	}; | ||||
| 	bool show_usage = false; | ||||
|  | ||||
| 	/* Check if the executable has data attached. */ | ||||
| 	unzFile zip = unzOpen(file); | ||||
| 	if (zip) | ||||
| 	{ | ||||
| 		args.zip = file; | ||||
| 		unzClose(zip); | ||||
| 	} | ||||
|  | ||||
| 	while (!show_usage) | ||||
| 	{ | ||||
| 		static const struct option k_options[] = { | ||||
|   | ||||
| @@ -1939,11 +1939,6 @@ void tf_task_destroy(tf_task_t* task) | ||||
| 	tf_free(task->_promise_stacks); | ||||
| 	tf_free((void*)task->_path); | ||||
| 	bool was_trusted = task->_trusted; | ||||
| 	if (task->_zip) | ||||
| 	{ | ||||
| 		unzClose(task->_zip); | ||||
| 		task->_zip = NULL; | ||||
| 	} | ||||
| 	tf_free(task); | ||||
| 	if (was_trusted) | ||||
| 	{ | ||||
|   | ||||
| @@ -1,2 +1,2 @@ | ||||
| #define VERSION_NUMBER "0.0.17" | ||||
| #define VERSION_NUMBER "0.0.17-wip" | ||||
| #define VERSION_NAME "Please enjoy responsibly." | ||||
|   | ||||
		Reference in New Issue
	
	Block a user