diff --git a/package-lock.json b/package-lock.json index 098342a..edf879f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "www-epivis", - "version": "2.1.5", + "version": "2.1.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "www-epivis", - "version": "2.1.5", + "version": "2.1.6", "license": "MIT", "dependencies": { "uikit": "^3.15.5" diff --git a/package.json b/package.json index fff84c4..f8609bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "www-epivis", - "version": "2.1.5", + "version": "2.1.6", "private": true, "license": "MIT", "description": "", diff --git a/src/api/EpiData.ts b/src/api/EpiData.ts index 142d2c5..8612320 100644 --- a/src/api/EpiData.ts +++ b/src/api/EpiData.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/restrict-template-expressions */ import UIkit from 'uikit'; import { appendIssueToTitle } from '../components/dialogs/utils'; import { @@ -43,11 +44,23 @@ const ENDPOINT = process.env.EPIDATA_ENDPOINT_URL; export const fetchOptions: RequestInit = process.env.NODE_ENV === 'development' ? { cache: 'force-cache' } : {}; +function processResponse(response: Response): Promise { + if (response.ok) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return response.json(); + } + return response.text().then((text) => { + throw new Error(`[${response.status}] ${text}`); + }); +} + export function fetchImpl(url: URL): Promise { const urlGetS = url.toString(); if (urlGetS.length < 4096) { // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return fetch(url.toString(), fetchOptions).then((d) => d.json()); + return fetch(url.toString(), fetchOptions).then((d) => { + return processResponse(d); + }); } const params = new URLSearchParams(url.searchParams); url.searchParams.forEach((d) => url.searchParams.delete(d)); @@ -56,7 +69,9 @@ export function fetchImpl(url: URL): Promise { ...fetchOptions, method: 'POST', body: params, - }).then((d) => d.json()); + }).then((d) => { + return processResponse(d); + }); } // generic epidata loader @@ -147,18 +162,42 @@ export function loadDataSet( url.searchParams.set('format', 'json'); return fetchImpl[]>(url) .then((res) => { - const data = loadEpidata(title, res, columns, columnRenamings, { _endpoint: endpoint, ...params }); - if (data.datasets.length == 0) { + try { + const data = loadEpidata(title, res, columns, columnRenamings, { _endpoint: endpoint, ...params }); + if (data.datasets.length == 0) { + return UIkit.modal + .alert( + ` +
+ API Link returned no data, which suggests that the API has no available information for the selected location. +
`, + ) + .then(() => null); + } + return data; + } catch (error) { + console.warn('failed loading data', error); + // EpiData API error - JSON with "message" property + if ('message' in res) { + return UIkit.modal + .alert( + ` +
+ [f01] Failed to fetch API data from API Link:
${res['message']} +
`, + ) + .then(() => null); + } + // Fallback for generic error return UIkit.modal .alert( `
- API Link returned no data. + [f02] Failed to fetch API data from API Link:
${error}
`, ) .then(() => null); } - return data; }) .catch((error) => { console.warn('failed fetching data', error); @@ -166,7 +205,7 @@ export function loadDataSet( .alert( `
- Failed to fetch API data from API Link. + [f03] Failed to fetch API data from API Link:
${error}
`, ) .then(() => null); diff --git a/src/components/dialogs/ImportAPIDialog.svelte b/src/components/dialogs/ImportAPIDialog.svelte index 2d48895..08fe4b8 100644 --- a/src/components/dialogs/ImportAPIDialog.svelte +++ b/src/components/dialogs/ImportAPIDialog.svelte @@ -19,29 +19,14 @@ import NowCast from './dataSources/Nowcast.svelte'; import CovidHosp from './dataSources/COVIDHosp.svelte'; import CoviDcast from './dataSources/COVIDcast.svelte'; - import { navMode } from '../../store'; + import { navMode, storeApiKeys } from '../../store'; import { NavMode } from '../chartUtils'; + import { formSelections } from '../../store'; const dispatch = createEventDispatcher(); const id = randomId(); - let dataSource: - | 'fluview' - | 'flusurv' - | 'gft' - | 'ght' - | 'twitter' - | 'wiki' - | 'cdc' - | 'quidel' - | 'nidss_flu' - | 'nidss_dengue' - | 'sensors' - | 'nowcast' - | 'covidcast' - | 'covid_hosp' = 'fluview'; - let loading = false; let handler: unknown = null; @@ -71,47 +56,89 @@
Data Source
+ FluSurv (source: + cdc.gov) + Google Flu Trends (source: google.com) + Google Health Trends (source: private Google API) + CDC Page Hits (source: private CDC dataset) Quidel Data (source: private Quidel dataset) delphi.cmu.edu) + Delphi Sensors (source: delphi.cmu.edu) + Delphi Nowcast (source: delphi.cmu.edu) + Delphi COVIDcast (source: delphi.cmu.edu)
- {#if dataSource === 'fluview'} + {#if $formSelections.dataSource === 'fluview'} - {:else if dataSource === 'flusurv'} + {:else if $formSelections.dataSource === 'flusurv'} - {:else if dataSource === 'gft'} + {:else if $formSelections.dataSource === 'gft'} - {:else if dataSource === 'ght'} + {:else if $formSelections.dataSource === 'ght'} - {:else if dataSource === 'twitter'} + {:else if $formSelections.dataSource === 'twitter'} - {:else if dataSource === 'wiki'} + {:else if $formSelections.dataSource === 'wiki'} - {:else if dataSource === 'quidel'} + {:else if $formSelections.dataSource === 'quidel'} - {:else if dataSource === 'nidss_dengue'} + {:else if $formSelections.dataSource === 'nidss_dengue'} - {:else if dataSource === 'nidss_flu'} + {:else if $formSelections.dataSource === 'nidss_flu'} - {:else if dataSource === 'cdc'} + {:else if $formSelections.dataSource === 'cdc'} - {:else if dataSource === 'sensors'} + {:else if $formSelections.dataSource === 'sensors'} - {:else if dataSource === 'nowcast'} + {:else if $formSelections.dataSource === 'nowcast'} - {:else if dataSource === 'covid_hosp'} + {:else if $formSelections.dataSource === 'covid_hosp'} - {:else if dataSource === 'covidcast'} + {:else if $formSelections.dataSource === 'covidcast'} {/if} - +
+
+ + +
+
+ + diff --git a/src/components/dialogs/dataSources/CDC.svelte b/src/components/dialogs/dataSources/CDC.svelte index 3740151..89ac684 100644 --- a/src/components/dialogs/dataSources/CDC.svelte +++ b/src/components/dialogs/dataSources/CDC.svelte @@ -3,16 +3,20 @@ import { cdcLocations as regions } from '../../../data/data'; import SelectField from '../inputs/SelectField.svelte'; import TextField from '../inputs/TextField.svelte'; + import { apiKey, formSelections } from '../../../store'; export let id: string; - let locations = regions[0].value; - let auth = ''; - export function importDataSet() { - return importCDC({ locations, auth }); + return importCDC({ locations: $formSelections.cdc.locations, auth: $apiKey }); } - - + + diff --git a/src/components/dialogs/dataSources/COVIDHosp.svelte b/src/components/dialogs/dataSources/COVIDHosp.svelte index b3c5fac..0393274 100644 --- a/src/components/dialogs/dataSources/COVIDHosp.svelte +++ b/src/components/dialogs/dataSources/COVIDHosp.svelte @@ -3,17 +3,14 @@ import { covidHospLocations as regions } from '../../../data/data'; import SelectField from '../inputs/SelectField.svelte'; import SelectIssue from '../inputs/SelectIssue.svelte'; - import { DEFAULT_ISSUE } from '../utils'; + import { formSelections } from '../../../store'; export let id: string; - let states = regions[0].value; - let issue = DEFAULT_ISSUE; - export function importDataSet() { - return importCOVIDHosp({ states, ...issue }); + return importCOVIDHosp({ states: $formSelections.covidHosp.states, ...$formSelections.covidHosp.issue }); } - - + + diff --git a/src/components/dialogs/dataSources/COVIDcast.svelte b/src/components/dialogs/dataSources/COVIDcast.svelte index 074c881..f7b2ecb 100644 --- a/src/components/dialogs/dataSources/COVIDcast.svelte +++ b/src/components/dialogs/dataSources/COVIDcast.svelte @@ -4,25 +4,21 @@ import type { LabelValue } from '../../../data/data'; import SelectField from '../inputs/SelectField.svelte'; import TextField from '../inputs/TextField.svelte'; + import { apiKey, formSelections } from '../../../store'; export let id: string; - - let api_key = ''; - let data_source = ''; - let signal = ''; - let geo_type = ''; - let geo_value = ''; - let form_key = ''; let valid_key = true; let dataSources: (LabelValue & { signals: string[] })[] = []; let geoTypes: string[] = []; - $: dataSignals = (dataSources.find((d) => d.value === data_source) || { signals: [] }).signals; + $: dataSignals = (dataSources.find((d) => d.value === $formSelections.covidcast.dataSource) || { signals: [] }) + .signals; $: { - if (data_source) { - signal = ''; + if ($formSelections.covidcast.dataSource) { + dataSignals = (dataSources.find((d) => d.value === $formSelections.covidcast.dataSource) || { signals: [] }) + .signals; } } @@ -36,12 +32,11 @@ }; function fetchMetadata() { - fetchCOVIDcastMeta(form_key).then((res) => { + fetchCOVIDcastMeta($apiKey).then((res) => { if (res.length == 0) { valid_key = false; } else { valid_key = true; - api_key = form_key; // API key is valid -> use it to fetch data later on geoTypes = [...new Set(res.map((d) => d.geo_type))]; const byDataSource = new Map(); for (const row of res) { @@ -69,10 +64,20 @@ }); export function importDataSet() { - return fetchCOVIDcastMeta(api_key).then((res) => { - const meta = res.filter((row) => row.data_source === data_source && row.signal === signal); + return fetchCOVIDcastMeta($apiKey).then((res) => { + const meta = res.filter( + (row) => + row.data_source === $formSelections.covidcast.dataSource && row.signal === $formSelections.covidcast.signal, + ); const time_type = meta[0].time_type; - return importCOVIDcast({ data_source, signal, geo_type, geo_value, time_type, api_key }); + return importCOVIDcast({ + data_source: $formSelections.covidcast.dataSource, + geo_type: $formSelections.covidcast.geoType, + geo_value: $formSelections.covidcast.geoValue, + signal: $formSelections.covidcast.signal, + time_type, + api_key: $apiKey, + }); }); } @@ -89,7 +94,7 @@ class:uk-form-danger={!valid_key} name="api_key" required={false} - bind:value={form_key} + bind:value={$apiKey} on:input={debounce(() => fetchMetadata(), 500)} /> {#if !valid_key} @@ -97,13 +102,31 @@ {/if} - - - + + + diff --git a/src/components/dialogs/dataSources/FluSurv.svelte b/src/components/dialogs/dataSources/FluSurv.svelte index cf6c27b..d3d03ed 100644 --- a/src/components/dialogs/dataSources/FluSurv.svelte +++ b/src/components/dialogs/dataSources/FluSurv.svelte @@ -4,17 +4,14 @@ import { fluSurvRegions as regions } from '../../../data/data'; import SelectField from '../inputs/SelectField.svelte'; import SelectIssue from '../inputs/SelectIssue.svelte'; - import { DEFAULT_ISSUE } from '../utils'; + import { formSelections } from '../../../store'; export let id: string; - let locations = regions[0].value; - let issue = DEFAULT_ISSUE; - export function importDataSet() { - return importFluSurv({ locations, ...issue }); + return importFluSurv({ locations: $formSelections.fluSurv.locations, ...$formSelections.fluSurv.issue }); } - - + + diff --git a/src/components/dialogs/dataSources/FluView.svelte b/src/components/dialogs/dataSources/FluView.svelte index 00ce0ec..caf90d5 100644 --- a/src/components/dialogs/dataSources/FluView.svelte +++ b/src/components/dialogs/dataSources/FluView.svelte @@ -4,26 +4,26 @@ import SelectField from '../inputs/SelectField.svelte'; import SelectIssue from '../inputs/SelectIssue.svelte'; import TextField from '../inputs/TextField.svelte'; - import { DEFAULT_ISSUE } from '../utils'; + import { apiKey, formSelections } from '../../../store'; export let id: string; - let regions = fluViewRegions[0].value; - let issue = DEFAULT_ISSUE; - let auth: string = ''; - export function importDataSet() { - return importFluView({ regions, ...issue, auth }); + return importFluView({ + regions: $formSelections.fluView.locations, + ...$formSelections.fluView.issue, + auth: $apiKey, + }); } - - + + diff --git a/src/components/dialogs/dataSources/GFT.svelte b/src/components/dialogs/dataSources/GFT.svelte index 1cf6724..0e802d0 100644 --- a/src/components/dialogs/dataSources/GFT.svelte +++ b/src/components/dialogs/dataSources/GFT.svelte @@ -2,14 +2,13 @@ import { importGFT } from '../../../api/EpiData'; import { gftLocations as regions } from '../../../data/data'; import SelectField from '../inputs/SelectField.svelte'; + import { formSelections } from '../../../store'; export let id: string; - let locations = regions[0].value; - export function importDataSet() { - return importGFT({ locations }); + return importGFT({ locations: $formSelections.gft.locations }); } - + diff --git a/src/components/dialogs/dataSources/GHT.svelte b/src/components/dialogs/dataSources/GHT.svelte index 0c2102c..d599dae 100644 --- a/src/components/dialogs/dataSources/GHT.svelte +++ b/src/components/dialogs/dataSources/GHT.svelte @@ -3,18 +3,21 @@ import { ghtLocations as regions } from '../../../data/data'; import SelectField from '../inputs/SelectField.svelte'; import TextField from '../inputs/TextField.svelte'; + import { apiKey, formSelections } from '../../../store'; export let id: string; - let locations = regions[0].value; - let auth = ''; - let query = ''; - export function importDataSet() { - return importGHT({ auth, locations, query }); + return importGHT({ auth: $apiKey, locations: $formSelections.ght.locations, query: $formSelections.ght.query }); } - - - + + + diff --git a/src/components/dialogs/dataSources/NIDSSDengue.svelte b/src/components/dialogs/dataSources/NIDSSDengue.svelte index 4797946..46f4235 100644 --- a/src/components/dialogs/dataSources/NIDSSDengue.svelte +++ b/src/components/dialogs/dataSources/NIDSSDengue.svelte @@ -2,14 +2,13 @@ import { importNIDSSDengue } from '../../../api/EpiData'; import { nidssDengueLocations as regions } from '../../../data/data'; import SelectField from '../inputs/SelectField.svelte'; + import { formSelections } from '../../../store'; export let id: string; - let locations = regions[0].value; - export function importDataSet() { - return importNIDSSDengue({ locations }); + return importNIDSSDengue({ locations: $formSelections.nidssDengue.locations }); } - + diff --git a/src/components/dialogs/dataSources/NIDSSFlu.svelte b/src/components/dialogs/dataSources/NIDSSFlu.svelte index 72b9069..69052c5 100644 --- a/src/components/dialogs/dataSources/NIDSSFlu.svelte +++ b/src/components/dialogs/dataSources/NIDSSFlu.svelte @@ -3,17 +3,14 @@ import { nidssFluLocations as regions } from '../../../data/data'; import SelectField from '../inputs/SelectField.svelte'; import SelectIssue from '../inputs/SelectIssue.svelte'; - import { DEFAULT_ISSUE } from '../utils'; + import { formSelections } from '../../../store'; export let id: string; - let locations = regions[0].value; - let issue = DEFAULT_ISSUE; - export function importDataSet() { - return importNIDSSFlu({ regions: locations, ...issue }); + return importNIDSSFlu({ regions: $formSelections.nidssFlu.locations, ...$formSelections.nidssFlu.issue }); } - - + + diff --git a/src/components/dialogs/dataSources/Nowcast.svelte b/src/components/dialogs/dataSources/Nowcast.svelte index 0dfa662..a904fe5 100644 --- a/src/components/dialogs/dataSources/Nowcast.svelte +++ b/src/components/dialogs/dataSources/Nowcast.svelte @@ -2,14 +2,13 @@ import { importNowcast } from '../../../api/EpiData'; import { nowcastLocations as regions } from '../../../data/data'; import SelectField from '../inputs/SelectField.svelte'; + import { formSelections } from '../../../store'; export let id: string; - let locations = regions[0].value; - export function importDataSet() { - return importNowcast({ locations }); + return importNowcast({ locations: $formSelections.nowcast.locations }); } - + diff --git a/src/components/dialogs/dataSources/Quidel.svelte b/src/components/dialogs/dataSources/Quidel.svelte index dd79cca..c63b6d8 100644 --- a/src/components/dialogs/dataSources/Quidel.svelte +++ b/src/components/dialogs/dataSources/Quidel.svelte @@ -3,16 +3,20 @@ import { quidelLocations as regions } from '../../../data/data'; import SelectField from '../inputs/SelectField.svelte'; import TextField from '../inputs/TextField.svelte'; + import { apiKey, formSelections } from '../../../store'; export let id: string; - let locations = regions[0].value; - let auth = ''; - export function importDataSet() { - return importQuidel({ auth, locations }); + return importQuidel({ auth: $apiKey, locations: $formSelections.quidel.locations }); } - - + + diff --git a/src/components/dialogs/dataSources/Sensors.svelte b/src/components/dialogs/dataSources/Sensors.svelte index d47c9d1..13729cd 100644 --- a/src/components/dialogs/dataSources/Sensors.svelte +++ b/src/components/dialogs/dataSources/Sensors.svelte @@ -3,18 +3,25 @@ import { sensorLocations as regions, sensorNames } from '../../../data/data'; import SelectField from '../inputs/SelectField.svelte'; import TextField from '../inputs/TextField.svelte'; + import { apiKey, formSelections } from '../../../store'; export let id: string; - let locations = regions[0].value; - let auth = ''; - let names = sensorNames[0].value; - export function importDataSet() { - return importSensors({ auth, names, locations }); + return importSensors({ + auth: $apiKey, + names: $formSelections.sensors.names, + locations: $formSelections.sensors.locations, + }); } - - - + + + diff --git a/src/components/dialogs/dataSources/Twitter.svelte b/src/components/dialogs/dataSources/Twitter.svelte index d3800d5..ce9ab48 100644 --- a/src/components/dialogs/dataSources/Twitter.svelte +++ b/src/components/dialogs/dataSources/Twitter.svelte @@ -3,26 +3,47 @@ import { twitterLocations as regions } from '../../../data/data'; import SelectField from '../inputs/SelectField.svelte'; import TextField from '../inputs/TextField.svelte'; + import { apiKey, formSelections } from '../../../store'; export let id: string; - let locations = regions[0].value; - let auth = ''; - let resolution: 'daily' | 'weekly' = 'daily'; - export function importDataSet() { - return importTwitter({ auth, locations, resolution }); + return importTwitter({ + auth: $apiKey, + locations: $formSelections.twitter.locations, + resolution: $formSelections.twitter.resolution, + }); } - - + +
Temporal Resolution
- Daily +
diff --git a/src/components/dialogs/dataSources/Wiki.svelte b/src/components/dialogs/dataSources/Wiki.svelte index c00dc4c..64da12a 100644 --- a/src/components/dialogs/dataSources/Wiki.svelte +++ b/src/components/dialogs/dataSources/Wiki.svelte @@ -2,15 +2,10 @@ import { importWiki } from '../../../api/EpiData'; import { wikiArticles } from '../../../data/data'; import SelectField from '../inputs/SelectField.svelte'; + import { formSelections } from '../../../store'; export let id: string; - let articles = wikiArticles[0].value; - let resolution: 'daily' | 'weekly' = 'daily'; - let hour = 0; - let useHour = false; - let language = 'en'; - const languages = [ { value: 'en', @@ -19,29 +14,63 @@ ]; export function importDataSet() { - return importWiki({ articles, resolution, hour: useHour ? hour : null, language }); + return importWiki({ + articles: $formSelections.wiki.articles, + resolution: $formSelections.wiki.resolution, + hour: $formSelections.wiki.useHour ? $formSelections.wiki.hour : null, + language: $formSelections.wiki.language, + }); } - +
Temporal Resolution
- Daily +
Specific Hour (filter by + hour else return sum: 0-23, timezone is UTC)
- +
- + diff --git a/src/components/dialogs/formSelections.ts b/src/components/dialogs/formSelections.ts new file mode 100644 index 0000000..c030af9 --- /dev/null +++ b/src/components/dialogs/formSelections.ts @@ -0,0 +1,123 @@ +import { + cdcLocations, + covidHospLocations, + fluSurvRegions, + fluViewRegions, + gftLocations, + ghtLocations, + nidssDengueLocations, + nidssFluLocations, + nowcastLocations, + quidelLocations, + sensorLocations, + sensorNames, + twitterLocations, + wikiArticles, +} from '../../data/data'; +import { DEFAULT_ISSUE } from './utils'; + +// The following classes define default values for the various "data source" forms, and will store modifications to those values as the user interacts with the forms. + +export class CdcSelections { + locations = cdcLocations[0].value; +} + +export class CovidcastSelections { + dataSource = ''; + signal = ''; + geoType = ''; + geoValue = ''; +} + +export class CovidHospSelections { + states = covidHospLocations[0].value; + issue = DEFAULT_ISSUE; +} + +export class FluSurvSelections { + locations = fluSurvRegions[0].value; + issue = DEFAULT_ISSUE; +} + +export class FluViewSelections { + locations = fluViewRegions[0].value; + issue = DEFAULT_ISSUE; +} + +export class GftSelections { + locations = gftLocations[0].value; +} + +export class GhtSelections { + locations = ghtLocations[0].value; + query = ''; +} + +export class NidssDengueSelections { + locations = nidssDengueLocations[0].value; +} + +export class NidssFluSelections { + locations = nidssFluLocations[0].value; + issue = DEFAULT_ISSUE; +} + +export class NowcastSelections { + locations = nowcastLocations[0].value; +} + +export class QuidelSelections { + locations = quidelLocations[0].value; +} + +export class SensorSelections { + locations = sensorLocations[0].value; + names = sensorNames[0].value; +} + +export class TwitterSelections { + locations = twitterLocations[0].value; + resolution: 'daily' | 'weekly' = 'daily'; +} + +export class WikiSelections { + articles = wikiArticles[0].value; + resolution: 'daily' | 'weekly' = 'daily'; + hour = 0; + useHour = false; + language = 'en'; +} + +// The FormSelections class defines the default form choice, stores changes to that choice, and holds references to the values used in each "data source" form. + +export default class FormSelections { + dataSource: + | 'fluview' + | 'flusurv' + | 'gft' + | 'ght' + | 'twitter' + | 'wiki' + | 'cdc' + | 'quidel' + | 'nidss_flu' + | 'nidss_dengue' + | 'sensors' + | 'nowcast' + | 'covidcast' + | 'covid_hosp' = 'fluview'; + cdc = new CdcSelections(); + covidcast = new CovidcastSelections(); + covidHosp = new CovidHospSelections(); + fluSurv = new FluSurvSelections(); + fluView = new FluViewSelections(); + gft = new GftSelections(); + ght = new GhtSelections(); + nidssDengue = new NidssDengueSelections(); + nidssFlu = new NidssFluSelections(); + nowcast = new NowcastSelections(); + quidel = new QuidelSelections(); + sensors = new SensorSelections(); + twitter = new TwitterSelections(); + wiki = new WikiSelections(); +} diff --git a/src/store.ts b/src/store.ts index 7bcc73b..397ffb1 100644 --- a/src/store.ts +++ b/src/store.ts @@ -2,6 +2,7 @@ import { get, writable } from 'svelte/store'; import { NavMode } from './components/chartUtils'; import DataSet, { DataGroup } from './data/DataSet'; import deriveLinkDefaults, { getDirectLinkImpl } from './deriveLinkDefaults'; +import FormSelections from './components/dialogs/formSelections'; declare const __VERSION__: string; @@ -17,6 +18,45 @@ export const isShowingPoints = writable(defaults.showPoints); export const initialViewport = writable(defaults.viewport); export const navMode = writable(NavMode.autofit); +export function getFormSelections() { + try { + if (sessionStorage.getItem('form')) { + return JSON.parse(sessionStorage.getItem('form')!) as FormSelections; + } + } catch { + // we are probably here because parsing failed, so remove bad JSON from sessionStorage + sessionStorage.removeItem('form'); + } + return new FormSelections(); +} + +export const formSelections = writable(getFormSelections()); +formSelections.subscribe((val) => { + sessionStorage.setItem('form', JSON.stringify(val)); +}); + +export const storeApiKeys = writable(localStorage.getItem('store-api-key') === 'true'); +storeApiKeys.subscribe((val) => { + localStorage.setItem('store-api-key', val.toString()); + if (val) { + // persist key from session to local storage + localStorage.setItem('api-key', sessionStorage.getItem('api-key') || ''); + } else { + // remove key from local storage + localStorage.removeItem('api-key'); + } +}); + +export const apiKey = writable(localStorage.getItem('api-key')! || ''); +apiKey.subscribe((val) => { + // always keep key around in session storage (resets on page refresh) + sessionStorage.setItem('api-key', val); + if (localStorage.getItem('store-api-key') === 'true') { + // store it in local storage (persistent) + localStorage.setItem('api-key', val); + } +}); + export function addDataSet(dataset: DataSet | DataGroup): void { const root = get(datasetTree); root.datasets.push(dataset);