forked from
grain.social/grain-pwa
WIP PWA for Grain
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);