Self-hosted, federated location sharing app and server that prioritizes user privacy and security
end-to-end-encryption location-sharing privacy self-hosted federated

D Adds a settings Page and some mods to apis

Changed files
+9 -171
app
src
home-page
settings-page
signup-page
+3 -1
app/src/home-page/home.html
··· 14 14 <div> 15 15 <!-- somehow the "+" emoji does not display in the code for me, but it's temporary anyways --> 16 16 <button class="icon-btn" @click="addFriend()">➕</button> 17 - <button class="icon-btn" @click="openSettings()">⚙️</button> 17 + <button class="icon-btn" @click="goto('settings')"> 18 + ⚙️ 19 + </button> 18 20 </div> 19 21 </header> 20 22
+5
app/src/home-page/home.ts
··· 39 39 isAdmin() { 40 40 return Store.get("is_admin"); 41 41 }, 42 + 43 + goto(newLocation: string) { 44 + window.location.href = 45 + "/src/" + newLocation + "-page/" + newLocation + ".html"; 46 + }, 42 47 })); 43 48 44 49 Alpine.start();
-116
app/src/settings-page/signup.css
··· 1 - body { 2 - font-family: system-ui, sans-serif; 3 - background: #f9fafb; 4 - display: flex; 5 - align-items: center; 6 - justify-content: center; 7 - height: 100vh; 8 - margin: 0; 9 - } 10 - 11 - .card { 12 - max-width: 90%; 13 - background: white; 14 - border: 1px solid #d1d5db; 15 - border-radius: 8px; 16 - padding: 1.5rem; 17 - box-sizing: border-box; 18 - } 19 - 20 - .header { 21 - text-align: center; 22 - margin-bottom: 1.5rem; 23 - } 24 - 25 - .icon-circle { 26 - width: 64px; 27 - height: 64px; 28 - background: #dbeafe; 29 - border-radius: 50%; 30 - display: flex; 31 - align-items: center; 32 - justify-content: center; 33 - margin: 0 auto 1rem; 34 - } 35 - 36 - .icon-circle img { 37 - width: 32px; 38 - height: 32px; 39 - } 40 - 41 - h1 { 42 - font-size: 1.5rem; 43 - } 44 - 45 - p { 46 - font-size: 0.9rem; 47 - color: #6b7280; 48 - } 49 - 50 - .actions { 51 - display: flex; 52 - flex-direction: column; 53 - gap: 1rem; 54 - } 55 - 56 - label { 57 - display: block; 58 - font-size: 0.85rem; 59 - font-weight: 600; 60 - margin-bottom: 0.25rem; 61 - } 62 - 63 - input { 64 - width: 100%; 65 - padding: 0.5rem 0.75rem; 66 - border: 1px solid #d1d5db; 67 - border-radius: 4px; 68 - font-size: 0.95rem; 69 - box-sizing: border-box; 70 - } 71 - 72 - input:focus { 73 - outline: none; 74 - border-color: #2563eb; 75 - } 76 - 77 - button { 78 - width: 100%; 79 - padding: 0.6rem; 80 - font-size: 0.95rem; 81 - border-radius: 4px; 82 - cursor: pointer; 83 - /*transition: background 0.2s ease;*/ 84 - display: flex; 85 - align-items: center; 86 - justify-content: center; 87 - } 88 - 89 - .btn-primary { 90 - background: #2563eb; 91 - color: white; 92 - border: none; 93 - } 94 - 95 - .btn-primary:hover { 96 - background: #1d4ed8; 97 - } 98 - 99 - .btn-qr { 100 - background: white; 101 - gap: 0.5rem; 102 - border: 1px solid #d1d5db; 103 - } 104 - 105 - .btn-qr:hover { 106 - background: #f3f4f6; 107 - } 108 - 109 - .btn-qr img { 110 - width: 16px; 111 - height: 16px; 112 - } 113 - 114 - .hint { 115 - font-size: 0.75rem; 116 - }
-42
app/src/settings-page/signup.html
··· 1 - <!doctype html> 2 - <html lang="en"> 3 - <head> 4 - <meta charset="UTF-8" /> 5 - <script type="module" src="./signup.ts"></script> 6 - <link rel="stylesheet" href="./signup.css" /> 7 - </head> 8 - 9 - <body> 10 - <div class="card"> 11 - <div class="header"> 12 - <div class="icon-circle"> 13 - <img src="/src/assets/pin.svg" alt="Pin Icon" /> 14 - </div> 15 - <h1>PrivacyPin</h1> 16 - <p>Connect with a server to start sharing</p> 17 - </div> 18 - 19 - <!-- x-data connects this element to the signupPageState Alpine component, enabling its data (serverAddress and signupKey) and functions (signup and scanQR) to work within it :) --> 20 - <!-- TODO: make this a form instead? --> 21 - <div class="actions" x-data="signupPageState"> 22 - <div> 23 - <label for="server">Server Address</label> 24 - <input id="server" type="url" placeholder="https://your-server.com" x-model="serverAddress" required /> 25 - </div> 26 - 27 - <div> 28 - <label for="key">Signup Key</label> 29 - <input id="key" type="password" placeholder="Enter your signup key" x-model="signupKey" required /> 30 - </div> 31 - 32 - <p class="hint">Scan a QR code to automatically fill both server address and signup key</p> 33 - <button type="button" x-bind:disabled="isDoingStuff" class="btn-qr" @click="await scanQR()"> 34 - <img src="/src/assets/qr.svg" alt="QR Icon" /> 35 - Scan QR Code 36 - </button> 37 - 38 - <button class="btn-primary" x-bind:disabled="isDoingStuff" @click="await signup()"><span x-show="isDoingStuff">Connecting...</span> <span x-show="!isDoingStuff">Connect</span></button> 39 - </div> 40 - </div> 41 - </body> 42 - </html>
-11
app/src/settings-page/signup.ts
··· 1 - import Alpine from "alpinejs"; 2 - import { createAccount } from "../utils/api.ts"; 3 - import { Store } from "../utils/store.ts"; 4 - 5 - Alpine.data("signupPageState", () => ({ 6 - resetStore() { 7 - Store.reset(); 8 - }, 9 - })); 10 - 11 - Alpine.start();
+1 -1
app/src/signup-page/signup.ts
··· 23 23 async scanQR() { 24 24 this.isDoingStuff = true; 25 25 await new Promise((resolve) => setTimeout(resolve, 1000)); 26 - this.serverAddress = "dummy server address"; 26 + this.serverAddress = "http://127.0.0.1:3000"; 27 27 this.signupKey = "dummy signup key"; 28 28 this.isDoingStuff = false; 29 29 },