WIP PWA for Grain
at main 163 lines 4.7 kB view raw
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);