|
1 | | -import { authSession, authn, store } from 'solid-logic' |
2 | | -import { widgets, utils } from 'solid-ui' |
3 | | -import 'solid-ui/components/header' |
4 | | -import { Header } from 'solid-ui/components/header' |
5 | | -import type { HeaderMenuItem, HeaderAccountMenuItem, HeaderAuthState } from 'solid-ui/components/header' |
6 | | -import { OutlineManager } from '../outline/manager' |
7 | | -import { LiveStore } from 'rdflib' |
8 | | -import helpIconSvg from '../icons/help.svg?raw' |
9 | | -import loginIconSvg from '../icons/person.svg?raw' |
10 | | -import signOutIconSvg from '../icons/signOut.svg?raw' |
11 | | -import defaultAvatarIconSvg from '../icons/personInCircle.svg?raw' |
12 | | -import downArrowIconSvg from '../icons/downArrow.svg?raw' |
13 | | -import signupIconSvg from '../icons/signUp.svg?raw' |
14 | | -import { createUiIcon } from '../icons/iconHelper' |
15 | | -import { setActiveMenuPane } from './menu' |
16 | | -/** |
17 | | - * menu icons |
18 | | -*/ |
19 | | -const HELP_MENU_ICON = createUiIcon(helpIconSvg, 'Help Icon', '#ffffff') |
20 | | -const LOGIN_ICON = createUiIcon(loginIconSvg, 'LogIn Icon', '#ffffff') |
21 | | -const SIGNUP_ICON = createUiIcon(signupIconSvg, 'SignUp Icon', '#ffffff') |
22 | | -const LOGOUT_ICON = createUiIcon(signOutIconSvg, 'LogOut Icon', '#000000') |
23 | | -const DEFAULT_AVATAR_ICON = createUiIcon(defaultAvatarIconSvg, 'Default Avatar Icon', '#6A7282') |
24 | | -const DOWN_ARROW_ICON = createUiIcon(downArrowIconSvg, 'Down Arrow Icon', '#ffffff') |
25 | | -const SOLID_ICON_URL = 'https://solidproject.org/assets/img/solid-emblem.svg' |
26 | | -/** |
27 | | - * menu elements |
28 | | -*/ |
29 | | -const SIGN_IN_MENU_ITEM = 'Log In' |
30 | | -const SIGN_OUT_MENU_ITEM = 'Log Out' |
31 | | -const SIGN_UP_MENU_ITEM = 'Sign Up' |
32 | | -const SIGN_UP__MENU_LINK = 'https://solidproject.org/get_a_pod' |
33 | | - |
34 | | -// data structure extracted for solid-ui-header binding |
35 | | -export const HELP_MENU_LIST = [ |
36 | | - { label: 'User guide', url: 'https://solidos.github.io/userguide/', target: '_blank' }, |
37 | | - { label: 'Report a problem', url: 'https://github.com/solidos/solidos/issues', target: '_blank' } |
38 | | -] |
| 1 | +import { authn } from 'solid-logic' |
| 2 | +import { html, render } from 'lit-html' |
| 3 | +import type { AccountMenuItem } from 'solid-ui/components/account' |
39 | 4 |
|
40 | | -const HEADER_MOBILE_STYLE_ID = 'solid-ui-header-mobile-style' |
| 5 | +import { setActiveMenuPane } from './menu' |
| 6 | +import type { OutlineManager } from '../outline/manager' |
41 | 7 |
|
42 | | -type ManagedHeader = Header & { |
43 | | - __solidPanesListenersAttached?: boolean |
44 | | - __solidPanesOutliner?: OutlineManager |
45 | | -} |
| 8 | +import '~icons/lucide/user' |
| 9 | +import '../components/header' |
46 | 10 |
|
47 | | -function ensureMobileHeaderStyles () { |
48 | | - if (document.getElementById(HEADER_MOBILE_STYLE_ID)) return |
49 | | - const style = document.createElement('style') |
50 | | - style.id = HEADER_MOBILE_STYLE_ID |
51 | | - style.textContent = ` |
52 | | - solid-ui-header[layout="mobile"]::part(logo) { |
53 | | - display: none !important; |
54 | | - } |
55 | | - ` |
56 | | - document.head.appendChild(style) |
57 | | -} |
| 11 | +export async function createHeader (outliner: OutlineManager) { |
| 12 | + const existingHeader = document.querySelector('solid-panes-header') |
58 | 13 |
|
59 | | -export async function createHeader (store: LiveStore, outliner: OutlineManager) { |
60 | | - ensureMobileHeaderStyles() |
61 | | - |
62 | | - const header = (document.querySelector('solid-ui-header') || document.createElement('solid-ui-header')) as ManagedHeader |
63 | | - const isNewHeader = !header.isConnected |
64 | | - if (!header.id) { |
65 | | - header.id = 'mainSolidUiHeader' |
| 14 | + if (existingHeader) { |
| 15 | + return existingHeader |
66 | 16 | } |
67 | | - header.__solidPanesOutliner = outliner |
68 | 17 |
|
69 | | - // ensure it is in DOM (before MainContent for consistency) |
70 | 18 | const main = document.getElementById('MainContent') |
71 | | - if (!header.isConnected) { |
72 | | - if (main && main.parentNode) { |
73 | | - main.parentNode.insertBefore(header, main) |
74 | | - } else { |
75 | | - document.body.prepend(header) |
76 | | - } |
77 | | - } |
78 | | - |
79 | | - await refreshHeader(outliner, header) |
80 | | - |
81 | | - if (isNewHeader) { |
82 | | - header.__solidPanesListenersAttached = false |
83 | | - } |
84 | | - |
85 | | - attachHeaderListeners(header) |
86 | | - |
87 | | - return header |
88 | | -} |
89 | | - |
90 | | -function attachHeaderListeners (header: ManagedHeader) { |
91 | | - if (header.__solidPanesListenersAttached) return |
92 | | - |
93 | | - const refreshCurrentHeader = async () => { |
94 | | - const outliner = header.__solidPanesOutliner |
95 | | - if (!outliner) return |
96 | | - await refreshHeader(outliner, header) |
97 | | - } |
98 | | - |
99 | | - authSession.events.on('login', refreshCurrentHeader) |
100 | | - authSession.events.on('logout', refreshCurrentHeader) |
101 | | - authSession.events.on('sessionRestore', refreshCurrentHeader) |
102 | | - |
103 | | - header.addEventListener('auth-action-select', async (e: Event) => { |
104 | | - const outliner = header.__solidPanesOutliner |
105 | | - if (!outliner) return |
106 | | - |
107 | | - const detail = (e as CustomEvent).detail |
108 | | - if (detail?.role === 'login') { |
109 | | - await refreshCurrentHeader() |
110 | | - // Do not auto-open the profile pane after login. |
111 | | - // outliner.showDashboard({ pane: 'profile' }) |
112 | | - } |
113 | | - }) |
114 | | - |
115 | | - header.addEventListener('signup-success', async () => { |
116 | | - // do nothing |
117 | | - }) |
118 | | - |
119 | | - header.addEventListener('account-menu-select', async (e: Event) => { |
120 | | - const outliner = header.__solidPanesOutliner |
121 | | - if (!outliner) return |
122 | | - |
123 | | - const detail = (e as CustomEvent).detail |
124 | | - if (detail?.action === 'logout') { |
125 | | - await refreshCurrentHeader() |
126 | | - // Do not navigate to the profile after logout. |
127 | | - } else if (detail?.action === 'show-profile') { |
128 | | - // TODO see if this can be consolidated |
129 | | - const currentUser = authn.currentUser() |
130 | | - if (currentUser) { |
131 | | - outliner.showDashboard(currentUser, { pane: 'profile' }) |
132 | | - setActiveMenuPane('profile') |
| 19 | + const tmpContainer = document.createElement('div') |
| 20 | + const menuItems: AccountMenuItem[] = [ |
| 21 | + { |
| 22 | + label: html`<icon-lucide-user slot="left-icon"></icon-lucide-user> Profile`, |
| 23 | + onSelected () { |
| 24 | + const currentUser = authn.currentUser() |
| 25 | + |
| 26 | + if (currentUser) { |
| 27 | + outliner.showDashboard(currentUser, { pane: 'profile' }) |
| 28 | + setActiveMenuPane('profile') |
| 29 | + } |
133 | 30 | } |
134 | 31 | } |
135 | | - }) |
136 | | - |
137 | | - header.__solidPanesListenersAttached = true |
138 | | -} |
139 | | - |
140 | | -export async function refreshHeader (outliner: OutlineManager, headerElement?: Header) { |
141 | | - ensureMobileHeaderStyles() |
142 | | - const headerOptions = setHeaderOptions(outliner) |
143 | | - const header = headerElement || document.querySelector('solid-ui-header') as Header | null |
144 | | - if (!header) return null |
145 | | - |
146 | | - header.theme = headerOptions.theme |
147 | | - header.layout = headerOptions.layout |
148 | | - header.brandLink = headerOptions.brandLink |
149 | | - header.logo = headerOptions.layout === 'desktop' ? headerOptions.logo : '' |
150 | | - header.helpIcon = headerOptions.helpIcon |
151 | | - header.helpMenuList = headerOptions.helpMenuList |
152 | | - header.authState = headerOptions.authState |
153 | | - header.loginAction = headerOptions.loginAction |
154 | | - header.signUpAction = headerOptions.signUpAction |
155 | | - header.accountMenu = await setUserMenu() |
156 | | - header.logoutLabel = headerOptions.logoutLabel |
157 | | - header.accountIcon = headerOptions.accountIcon |
158 | | - header.accountAvatar = headerOptions.accountAvatar |
159 | | - header.accountAvatarFallback = headerOptions.accountAvatarFallback |
160 | | - header.loginIcon = headerOptions.loginIcon |
161 | | - header.signUpIcon = headerOptions.signUpIcon |
162 | | - header.logoutIcon = headerOptions.logoutIcon |
163 | | - |
164 | | - return header |
165 | | -} |
166 | | - |
167 | | -function setHeaderOptions (outliner: OutlineManager) { |
168 | | - const currentUser = authn.currentUser() |
169 | | - const isAuthenticated = !!currentUser |
170 | | - const authState: HeaderAuthState = isAuthenticated ? 'logged-in' : 'logged-out' |
171 | | - const layout: Header['layout'] = outliner.context?.environment?.layout === 'mobile' ? 'mobile' : 'desktop' |
172 | | - const theme: Header['theme'] = outliner.context?.environment?.theme === 'dark' ? 'dark' : 'light' |
| 32 | + ] |
173 | 33 |
|
174 | | - const headerOptions = { |
175 | | - logo: SOLID_ICON_URL, |
176 | | - helpIcon: HELP_MENU_ICON, |
177 | | - helpMenuList: HELP_MENU_LIST, |
178 | | - layout, |
179 | | - theme, |
180 | | - brandLink: '/', |
181 | | - authState, |
182 | | - loginAction: { |
183 | | - label: SIGN_IN_MENU_ITEM |
184 | | - } as HeaderMenuItem, |
185 | | - signUpAction: { |
186 | | - label: SIGN_UP_MENU_ITEM, |
187 | | - url: SIGN_UP__MENU_LINK, |
188 | | - } as HeaderMenuItem, |
189 | | - logoutLabel: SIGN_OUT_MENU_ITEM, |
190 | | - accountIcon: isAuthenticated ? DOWN_ARROW_ICON : '', |
191 | | - accountAvatar: isAuthenticated ? widgets.findImage(currentUser) : undefined, |
192 | | - accountAvatarFallback: DEFAULT_AVATAR_ICON, |
193 | | - loginIcon: LOGIN_ICON, |
194 | | - signUpIcon: SIGNUP_ICON, |
195 | | - logoutIcon: LOGOUT_ICON |
196 | | - } |
| 34 | + render( |
| 35 | + html`<solid-panes-header .menuItems=${menuItems}></solid-panes-header>`, |
| 36 | + tmpContainer |
| 37 | + ) |
197 | 38 |
|
198 | | - return headerOptions |
199 | | -} |
| 39 | + const header = tmpContainer.firstElementChild |
200 | 40 |
|
201 | | -async function setUserMenu () { |
202 | | - const me = authn.currentUser() |
203 | | - if (!me) { |
204 | | - return [] |
| 41 | + if (!header) { |
| 42 | + throw new Error('Failed to create header') |
205 | 43 | } |
206 | 44 |
|
207 | | - try { |
208 | | - await store.fetcher.load(me.doc()) |
209 | | - } catch (err) { |
210 | | - console.error('Unable to load user profile', err) |
| 45 | + // ensure it is in DOM (before MainContent for consistency) |
| 46 | + if (main && main.parentNode) { |
| 47 | + main.parentNode.insertBefore(header, main) |
| 48 | + } else { |
| 49 | + document.body.prepend(header) |
211 | 50 | } |
212 | 51 |
|
213 | | - const accountMenu: HeaderAccountMenuItem[] = [ |
214 | | - { |
215 | | - label: utils.label(me), |
216 | | - avatar: widgets.findImage(me), |
217 | | - webid: me.value, |
218 | | - action: 'show-profile' |
219 | | - }, |
220 | | - // TODO add all my available accounts |
221 | | - ] |
222 | | - |
223 | | - return accountMenu |
| 52 | + return header |
224 | 53 | } |
0 commit comments