forked from
grain.social/grain-pwa
WIP PWA for Grain
1import { LitElement, html, css } from 'lit';
2import { router } from '../../router.js';
3import '../organisms/grain-action-dialog.js';
4import '../organisms/grain-report-dialog.js';
5import '../atoms/grain-toast.js';
6
7// Import pages
8import './grain-timeline.js';
9import './grain-profile.js';
10import './grain-profile-followers.js';
11import './grain-profile-following.js';
12import './grain-gallery-detail.js';
13import './grain-settings.js';
14import './grain-edit-profile.js';
15import './grain-create-gallery.js';
16import './grain-image-descriptions.js';
17import './grain-explore.js';
18import './grain-notifications.js';
19import './grain-terms.js';
20import './grain-privacy.js';
21import './grain-copyright.js';
22import './grain-oauth-callback.js';
23import './grain-onboarding.js';
24import '../organisms/grain-header.js';
25import '../organisms/grain-bottom-nav.js';
26
27export class GrainApp extends LitElement {
28 static properties = {
29 _dialogType: { state: true },
30 _dialogProps: { state: true }
31 };
32
33 static styles = css`
34 :host {
35 display: block;
36 font-family: var(--font-family);
37 background: var(--color-bg-primary);
38 color: var(--color-text-primary);
39 height: 100vh;
40 height: 100dvh;
41 overflow: hidden;
42 }
43 #outlet {
44 display: flex;
45 flex-direction: column;
46 position: fixed;
47 top: 48px;
48 left: 0;
49 right: 0;
50 bottom: calc(57px + env(safe-area-inset-bottom, 0px));
51 overflow-y: auto;
52 -webkit-overflow-scrolling: touch;
53 }
54 #outlet > * {
55 flex: 0 0 auto;
56 min-height: 100%;
57 }
58 `;
59
60 constructor() {
61 super();
62 this._dialogType = null;
63 this._dialogProps = {};
64 }
65
66 connectedCallback() {
67 super.connectedCallback();
68 this.addEventListener('open-dialog', this.#handleOpenDialog);
69 this.addEventListener('close-dialog', this.#closeDialog);
70 }
71
72 disconnectedCallback() {
73 this.removeEventListener('open-dialog', this.#handleOpenDialog);
74 this.removeEventListener('close-dialog', this.#closeDialog);
75 super.disconnectedCallback();
76 }
77
78 #handleOpenDialog = (e) => {
79 this._dialogType = e.detail.type;
80 this._dialogProps = e.detail.props || {};
81 };
82
83 #closeDialog = () => {
84 this._dialogType = null;
85 this._dialogProps = {};
86 };
87
88 #handleReportSubmitted = () => {
89 this.#closeDialog();
90 this.shadowRoot.querySelector('grain-toast')?.show('Report submitted');
91 };
92
93 #handleDialogAction = (e) => {
94 this.dispatchEvent(new CustomEvent('dialog-action', {
95 bubbles: true,
96 composed: true,
97 detail: e.detail
98 }));
99 };
100
101 #renderDialog() {
102 switch (this._dialogType) {
103 case 'report':
104 return html`
105 <grain-report-dialog
106 open
107 galleryUri=${this._dialogProps.galleryUri || ''}
108 @close=${this.#closeDialog}
109 @submitted=${this.#handleReportSubmitted}
110 ></grain-report-dialog>
111 `;
112 case 'action':
113 return html`
114 <grain-action-dialog
115 open
116 .actions=${this._dialogProps.actions || []}
117 ?loading=${this._dialogProps.loading}
118 loadingText=${this._dialogProps.loadingText || ''}
119 @close=${this.#closeDialog}
120 @action=${this.#handleDialogAction}
121 ></grain-action-dialog>
122 `;
123 default:
124 return '';
125 }
126 }
127
128 firstUpdated() {
129 const outlet = this.shadowRoot.getElementById('outlet');
130
131 router
132 .register('/', 'grain-timeline')
133 .register('/profile/:handle/followers', 'grain-profile-followers')
134 .register('/profile/:handle/following', 'grain-profile-following')
135 .register('/profile/:handle/gallery/:rkey', 'grain-gallery-detail')
136 .register('/profile/:handle', 'grain-profile')
137 .register('/settings', 'grain-settings')
138 .register('/settings/profile', 'grain-edit-profile')
139 .register('/legal/terms', 'grain-terms')
140 .register('/legal/privacy', 'grain-privacy')
141 .register('/legal/copyright', 'grain-copyright')
142 .register('/create', 'grain-create-gallery')
143 .register('/create/descriptions', 'grain-image-descriptions')
144 .register('/explore', 'grain-explore')
145 .register('/notifications', 'grain-notifications')
146 .register('/onboarding', 'grain-onboarding')
147 .register('/oauth/callback', 'grain-oauth-callback')
148 .register('*', 'grain-timeline')
149 .connect(outlet);
150 }
151
152 render() {
153 return html`
154 <grain-header></grain-header>
155 <div id="outlet"></div>
156 <grain-bottom-nav></grain-bottom-nav>
157 ${this.#renderDialog()}
158 <grain-toast></grain-toast>
159 `;
160 }
161}
162
163customElements.define('grain-app', GrainApp);