-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: create config page for sw settings #24
Merged
Merged
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
b29048f
feat: create config page for sw settings
SgtPooki 53001c6
Update src/lib/channel.ts
SgtPooki 7c6410f
Update src/sw.ts
SgtPooki eca1114
Update src/lib/channel.ts
SgtPooki 0d5f91f
Update src/index.tsx
SgtPooki 764ebbc
chore: fix build
SgtPooki 527cc4b
Merge branch 'main' into feat/config-page
SgtPooki f80b2ed
chore: change gear color
SgtPooki 7c988ad
feat: service worker config is shared to subdomains
SgtPooki 3e73d05
fix: test running
SgtPooki 2c0cd88
fix: service worker registration
SgtPooki 658c41d
chore: remove calls to removed to commsChannel methods
SgtPooki 535fc7e
chore: use LOCAL_STORAGE_KEYS
SgtPooki c588d2a
feat: config page auto reload works
SgtPooki d493df7
chore: import react functions directly
SgtPooki f50182d
chore: use latest verified-fetch
SgtPooki 732f9a4
chore: remove console.logs and cleanup
SgtPooki ff7bafe
chore: consolidate app logic
SgtPooki b85f323
feat: users can control debugging output
SgtPooki 8068f14
chore: todo determinism
SgtPooki ffe08c1
fix: gateway & routers default value
SgtPooki d078e2d
fix: bug parsing ipfs namespaced subdomains
SgtPooki 9304b9f
chore: comment
SgtPooki 78ebfc6
fix: use configured gateways & routers prior to defaults
SgtPooki 56485a5
Merge branch 'main' into feat/config-page
SgtPooki d4aa1d0
Merge branch 'main' into feat/config-page
SgtPooki 78ec8a3
Merge branch 'main' into feat/config-page
SgtPooki 21c2e7b
feat: config collapsed, reload button, sw-ready-btn
SgtPooki 4fcc269
chore: remove unused in config.tsx
SgtPooki File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,3 +41,7 @@ form { | |
flex: 1; | ||
word-break: break-word; | ||
} | ||
|
||
.cursor-disabled { | ||
cursor: not-allowed; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,21 @@ | ||
import React from 'react' | ||
import React, { useContext } from 'react' | ||
import { ConfigContext } from '../context/config-context.tsx' | ||
import gearIcon from '../gear-icon.svg' | ||
import ipfsLogo from '../ipfs-logo.svg' | ||
|
||
export default function Header (): JSX.Element { | ||
const { isConfigExpanded, setConfigExpanded } = useContext(ConfigContext) | ||
|
||
return ( | ||
<header className='flex items-center pa3 bg-navy bb bw3 b--aqua'> | ||
<a href='https://ipfs.io' title='home'> | ||
<img alt='IPFS logo' src={ipfsLogo} style={{ height: 50 }} className='v-top' /> | ||
</a> | ||
|
||
<button onClick={() => { setConfigExpanded(!isConfigExpanded) }} style={{ border: 'none', position: 'absolute', top: '0.5rem', right: '0.5rem', background: 'none', cursor: 'pointer' }}> | ||
{/* https://isotropic.co/tool/hex-color-to-css-filter/ to #ffffff */} | ||
<img alt='Config gear icon' src={gearIcon} style={{ height: 50, filter: 'invert(100%) sepia(100%) saturate(0%) hue-rotate(275deg) brightness(103%) contrast(103%)' }} className='v-top' /> | ||
</button> | ||
</header> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import React, { useState } from 'react' | ||
|
||
export interface CollapsibleProps { | ||
children: React.ReactNode | ||
collapsedLabel: string | ||
expandedLabel: string | ||
collapsed: boolean | ||
} | ||
|
||
export function Collapsible ({ children, collapsedLabel, expandedLabel, collapsed }: CollapsibleProps): JSX.Element { | ||
const [cId] = useState(Math.random().toString(36).substring(7)) | ||
const [isCollapsed, setCollapsed] = useState(collapsed) | ||
|
||
return ( | ||
<React.Fragment> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. vscode was being weird in this file yelling about all types of syntax errors that didn't exist after closing it and re-opening. ¯\_(ツ)_/¯ |
||
<input type="checkbox" className="dn" name="collapsible" id={`collapsible-${cId}`} onClick={() => { setCollapsed(!isCollapsed) }} /> | ||
<label htmlFor={`collapsible-${cId}`} className="collapsible__item-label db pv3 link black hover-blue pointer blue">{isCollapsed ? collapsedLabel : expandedLabel}</label> | ||
<div className={`bb b--black-20 ${isCollapsed ? 'dn' : ''}`}> | ||
{children} | ||
</div> | ||
</React.Fragment> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import React, { useCallback, useContext, useEffect, useState } from 'react' | ||
import { ConfigContext } from '../context/config-context.tsx' | ||
import { HeliaServiceWorkerCommsChannel } from '../lib/channel.ts' | ||
import { getConfig, loadConfigFromLocalStorage } from '../lib/config-db.ts' | ||
import { LOCAL_STORAGE_KEYS } from '../lib/local-storage.ts' | ||
import { Collapsible } from './collapsible' | ||
import LocalStorageInput from './local-storage-input.tsx' | ||
import { LocalStorageToggle } from './local-storage-toggle' | ||
import { ServiceWorkerReadyButton } from './sw-ready-button.tsx' | ||
|
||
const channel = new HeliaServiceWorkerCommsChannel('WINDOW') | ||
|
||
const urlValidationFn = (value: string): Error | null => { | ||
try { | ||
const urls = JSON.parse(value) satisfies string[] | ||
let i = 0 | ||
try { | ||
urls.map((url, index) => { | ||
i = index | ||
return new URL(url) | ||
}) | ||
} catch (e) { | ||
throw new Error(`URL "${urls[i]}" at index ${i} is not valid`) | ||
} | ||
return null | ||
} catch (err) { | ||
return err as Error | ||
} | ||
} | ||
|
||
const stringValidationFn = (value: string): Error | null => { | ||
// we accept any string | ||
return null | ||
} | ||
|
||
export default (): JSX.Element | null => { | ||
const { isConfigExpanded, setConfigExpanded } = useContext(ConfigContext) | ||
const [error, setError] = useState<Error | null>(null) | ||
|
||
const isLoadedInIframe = window.self !== window.top | ||
|
||
const postFromIframeToParentSw = useCallback(async () => { | ||
if (!isLoadedInIframe) { | ||
return | ||
} | ||
// we get the iframe origin from a query parameter called 'origin', if this is loaded in an iframe | ||
const targetOrigin = decodeURIComponent(window.location.search.split('origin=')[1]) | ||
const config = await getConfig() | ||
|
||
/** | ||
* The reload page in the parent window is listening for this message, and then it passes a RELOAD_CONFIG message to the service worker | ||
*/ | ||
window.parent?.postMessage({ source: 'helia-sw-config-iframe', target: 'PARENT', action: 'RELOAD_CONFIG', config }, { | ||
targetOrigin | ||
}) | ||
}, []) | ||
|
||
useEffect(() => { | ||
/** | ||
* On initial load, we want to send the config to the parent window, so that the reload page can auto-reload if enabled, and the subdomain registered service worker gets the latest config without user interaction. | ||
*/ | ||
void postFromIframeToParentSw() | ||
}, []) | ||
|
||
const saveConfig = useCallback(async () => { | ||
try { | ||
await loadConfigFromLocalStorage() | ||
// update the BASE_URL service worker | ||
// TODO: use channel.messageAndWaitForResponse to ensure that the config is loaded before proceeding. | ||
channel.postMessage({ target: 'SW', action: 'RELOAD_CONFIG' }) | ||
// update the <subdomain>.<namespace>.BASE_URL service worker | ||
await postFromIframeToParentSw() | ||
setConfigExpanded(false) | ||
} catch (err) { | ||
setError(err as Error) | ||
} | ||
}, []) | ||
|
||
if (!isConfigExpanded) { | ||
return null | ||
} | ||
|
||
return ( | ||
<main className='pa4-l bg-snow mw7 center pa4'> | ||
<Collapsible collapsedLabel="View config" expandedLabel='Hide config' collapsed={true}> | ||
<LocalStorageInput localStorageKey={LOCAL_STORAGE_KEYS.config.gateways} label='Gateways' validationFn={urlValidationFn} defaultValue='[]' /> | ||
<LocalStorageInput localStorageKey={LOCAL_STORAGE_KEYS.config.routers} label='Routers' validationFn={urlValidationFn} defaultValue='[]'/> | ||
<LocalStorageToggle localStorageKey={LOCAL_STORAGE_KEYS.config.autoReload} onLabel='Auto Reload' offLabel='Show Config' /> | ||
<LocalStorageInput localStorageKey={LOCAL_STORAGE_KEYS.config.debug} label='Debug logging' validationFn={stringValidationFn} defaultValue=''/> | ||
<ServiceWorkerReadyButton id="save-config" label='Save Config' waitingLabel='Waiting for service worker registration...' onClick={() => { void saveConfig() }} /> | ||
|
||
{error != null && <span style={{ color: 'red' }}>{error.message}</span>} | ||
</Collapsible> | ||
</main> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import React, { useEffect, useState } from 'react' | ||
|
||
export interface LocalStorageInputProps { | ||
localStorageKey: string | ||
label: string | ||
placeholder?: string | ||
defaultValue: string | ||
validationFn?(value: string): Error | null | ||
} | ||
|
||
const defaultValidationFunction = (value: string): Error | null => { | ||
try { | ||
JSON.parse(value) | ||
return null | ||
} catch (err) { | ||
return err as Error | ||
} | ||
} | ||
export default ({ localStorageKey, label, placeholder, validationFn, defaultValue }: LocalStorageInputProps): JSX.Element => { | ||
const [value, setValue] = useState(localStorage.getItem(localStorageKey) ?? defaultValue) | ||
const [error, setError] = useState<null | Error>(null) | ||
|
||
if (validationFn == null) { | ||
validationFn = defaultValidationFunction | ||
} | ||
|
||
useEffect(() => { | ||
try { | ||
const err = validationFn?.(value) | ||
if (err != null) { | ||
throw err | ||
} | ||
localStorage.setItem(localStorageKey, value) | ||
setError(null) | ||
} catch (err) { | ||
setError(err as Error) | ||
} | ||
}, [value]) | ||
|
||
return ( | ||
<> | ||
<label htmlFor={localStorageKey} className='f5 ma0 pb2 aqua fw4 db'>{label}:</label> | ||
<input | ||
className='input-reset bn black-80 bg-white pa3 w-100 mb3' | ||
id={localStorageKey} | ||
name={localStorageKey} | ||
type='text' | ||
placeholder={placeholder} | ||
value={value} | ||
onChange={(e) => { setValue(e.target.value) }} | ||
/> | ||
{error != null && <span style={{ color: 'red' }}>{error.message}</span>} | ||
</> | ||
) | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eventually, this landing page should only be the config page....?