WIP PWA for Grain
at main 185 lines 4.6 kB view raw
1import { LitElement, html, css } from 'lit'; 2import { router } from '../../router.js'; 3import { auth } from '../../services/auth.js'; 4import { pwa } from '../../services/pwa.js'; 5import '../atoms/grain-icon.js'; 6 7export class GrainSettings extends LitElement { 8 static properties = { 9 _canInstall: { state: true }, 10 _showIOSInstructions: { state: true } 11 }; 12 static styles = css` 13 :host { 14 display: block; 15 width: 100%; 16 max-width: var(--feed-max-width); 17 min-height: 100%; 18 padding-bottom: 80px; 19 background: var(--color-bg-primary); 20 align-self: center; 21 } 22 .header { 23 display: flex; 24 align-items: center; 25 gap: var(--space-sm); 26 padding: var(--space-md) var(--space-sm); 27 } 28 @media (min-width: 600px) { 29 .header { 30 padding-left: 0; 31 padding-right: 0; 32 } 33 } 34 .back-button { 35 background: none; 36 border: none; 37 padding: 8px; 38 cursor: pointer; 39 color: var(--color-text-primary); 40 margin-left: -8px; 41 } 42 h1 { 43 font-size: var(--font-size-md); 44 font-weight: var(--font-weight-semibold); 45 color: var(--color-text-primary); 46 margin: 0; 47 } 48 .settings-list { 49 border-top: 1px solid var(--color-border); 50 } 51 .settings-row { 52 display: flex; 53 align-items: center; 54 gap: var(--space-sm); 55 padding: var(--space-md) var(--space-sm); 56 background: none; 57 border: none; 58 border-bottom: 1px solid var(--color-border); 59 width: 100%; 60 cursor: pointer; 61 color: var(--color-text-primary); 62 font-size: var(--font-size-sm); 63 text-align: left; 64 } 65 @media (min-width: 600px) { 66 .settings-row { 67 padding-left: 0; 68 padding-right: 0; 69 } 70 } 71 .settings-row:active { 72 background: var(--color-bg-elevated); 73 } 74 .ios-instructions { 75 display: flex; 76 align-items: flex-start; 77 gap: var(--space-sm); 78 padding: var(--space-md) var(--space-sm); 79 border-bottom: 1px solid var(--color-border); 80 color: var(--color-text-secondary); 81 font-size: var(--font-size-sm); 82 line-height: 1.4; 83 } 84 .ios-instructions grain-icon { 85 flex-shrink: 0; 86 margin-top: 2px; 87 } 88 `; 89 90 #unsubscribe = null; 91 92 connectedCallback() { 93 super.connectedCallback(); 94 95 // Redirect to timeline if not authenticated 96 if (!auth.isAuthenticated) { 97 router.replace('/'); 98 return; 99 } 100 101 this._canInstall = pwa.canInstall; 102 this._showIOSInstructions = pwa.showIOSInstructions; 103 this.#unsubscribe = pwa.subscribe((canInstall) => { 104 this._canInstall = canInstall; 105 }); 106 } 107 108 disconnectedCallback() { 109 super.disconnectedCallback(); 110 this.#unsubscribe?.(); 111 } 112 113 #goBack() { 114 history.back(); 115 } 116 117 #goToEditProfile() { 118 router.push('/settings/profile'); 119 } 120 121 #installApp() { 122 pwa.install(); 123 } 124 125 #signOut() { 126 router.push('/'); 127 auth.logout(); 128 } 129 130 #goToTerms() { 131 router.push('/legal/terms'); 132 } 133 134 #goToPrivacy() { 135 router.push('/legal/privacy'); 136 } 137 138 #goToCopyright() { 139 router.push('/legal/copyright'); 140 } 141 142 render() { 143 return html` 144 <div class="header"> 145 <button class="back-button" @click=${this.#goBack}> 146 <grain-icon name="back" size="20"></grain-icon> 147 </button> 148 <h1>Settings</h1> 149 </div> 150 <div class="settings-list"> 151 <button class="settings-row" @click=${this.#goToEditProfile}> 152 <grain-icon name="user" size="18"></grain-icon> 153 Edit Profile 154 </button> 155 ${this._canInstall ? html` 156 <button class="settings-row" @click=${this.#installApp}> 157 <grain-icon name="download" size="18"></grain-icon> 158 Install App 159 </button> 160 ` : ''} 161 ${this._showIOSInstructions ? html` 162 <div class="ios-instructions"> 163 <grain-icon name="share" size="18"></grain-icon> 164 <span>To install, tap the share button in Safari and select "Add to Home Screen"</span> 165 </div> 166 ` : ''} 167 <button class="settings-row" @click=${this.#signOut}> 168 <grain-icon name="logout" size="18"></grain-icon> 169 Sign Out 170 </button> 171 <button class="settings-row" @click=${this.#goToTerms}> 172 Terms 173 </button> 174 <button class="settings-row" @click=${this.#goToPrivacy}> 175 Privacy 176 </button> 177 <button class="settings-row" @click=${this.#goToCopyright}> 178 Copyright 179 </button> 180 </div> 181 `; 182 } 183} 184 185customElements.define('grain-settings', GrainSettings);