more work on offline capabilities
This commit is contained in:
parent
42fd153364
commit
6cf4a11283
|
@ -3282,6 +3282,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"idb-keyval": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-2.3.0.tgz",
|
||||||
|
"integrity": "sha1-TURLgMP4b8vNUTIbTcvJJHxZSMA="
|
||||||
|
},
|
||||||
"ieee754": {
|
"ieee754": {
|
||||||
"version": "1.1.8",
|
"version": "1.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
"fg-loadcss": "^2.0.1",
|
"fg-loadcss": "^2.0.1",
|
||||||
"font-awesome-svg-png": "^1.2.2",
|
"font-awesome-svg-png": "^1.2.2",
|
||||||
"glob": "^7.1.2",
|
"glob": "^7.1.2",
|
||||||
|
"idb-keyval": "^2.3.0",
|
||||||
"indexeddb-getall-shim": "^1.3.1",
|
"indexeddb-getall-shim": "^1.3.1",
|
||||||
"intersection-observer": "^0.5.0",
|
"intersection-observer": "^0.5.0",
|
||||||
"intl-relativeformat": "^2.1.0",
|
"intl-relativeformat": "^2.1.0",
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{then constructor}}
|
{{then constructor}}
|
||||||
<:Component {constructor} :target />
|
<:Component {constructor} :timeline />
|
||||||
{{catch error}}
|
{{catch error}}
|
||||||
<div>Component failed to load. Please try refreshing! {{error}}</div>
|
<div>Component failed to load. Please try refreshing! {{error}}</div>
|
||||||
{{/await}}
|
{{/await}}
|
||||||
|
|
|
@ -11,16 +11,18 @@
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
import { store } from '../_utils/store'
|
import { store } from '../_utils/store'
|
||||||
import { getHomeTimeline } from '../_utils/mastodon/timelines'
|
import { getTimeline } from '../_utils/mastodon/timelines'
|
||||||
import StatusListItem from './StatusListItem.html'
|
import StatusListItem from './StatusListItem.html'
|
||||||
import VirtualList from './VirtualList.html'
|
import VirtualList from './VirtualList.html'
|
||||||
import { splice, push } from 'svelte-extras'
|
import { splice, push } from 'svelte-extras'
|
||||||
import {
|
import {
|
||||||
insertStatuses as insertStatusesIntoDatabase,
|
insertStatuses as insertStatusesIntoDatabase,
|
||||||
getTimelineAfter as getTimelineFromDatabaseAfter
|
getTimeline as getTimelineFromDatabase
|
||||||
} from '../_utils/database/statuses'
|
} from '../_utils/database/statuses'
|
||||||
import { mergeStatuses } from '../_utils/statuses'
|
import { mergeStatuses } from '../_utils/statuses'
|
||||||
import { mark, stop } from '../_utils/marks'
|
import { mark, stop } from '../_utils/marks'
|
||||||
|
import { timelines } from '../_static/timelines'
|
||||||
|
|
||||||
|
|
||||||
const FETCH_LIMIT = 20
|
const FETCH_LIMIT = 20
|
||||||
|
|
||||||
|
@ -30,15 +32,14 @@
|
||||||
let instanceData = this.store.get('currentInstanceData')
|
let instanceData = this.store.get('currentInstanceData')
|
||||||
let online = this.get('online')
|
let online = this.get('online')
|
||||||
let statuses = online ?
|
let statuses = online ?
|
||||||
await getHomeTimeline(instanceName, instanceData.access_token, null, FETCH_LIMIT) :
|
await getTimeline(instanceName, instanceData.access_token, this.get('timeline'), null, FETCH_LIMIT) :
|
||||||
await getTimelineFromDatabaseAfter(null, FETCH_LIMIT)
|
await getTimelineFromDatabase(instanceName, this.get('timeline'), null, FETCH_LIMIT)
|
||||||
if (online) {
|
if (online) {
|
||||||
insertStatusesIntoDatabase(statuses)
|
insertStatusesIntoDatabase(instanceName, this.get('timeline'), statuses)
|
||||||
}
|
}
|
||||||
this.addStatuses(statuses)
|
this.addStatuses(statuses)
|
||||||
},
|
},
|
||||||
data: () => ({
|
data: () => ({
|
||||||
target: 'home',
|
|
||||||
StatusListItem: StatusListItem,
|
StatusListItem: StatusListItem,
|
||||||
statuses: [],
|
statuses: [],
|
||||||
runningUpdate: false
|
runningUpdate: false
|
||||||
|
@ -49,7 +50,7 @@
|
||||||
key: status.id
|
key: status.id
|
||||||
})),
|
})),
|
||||||
lastStatusId: (statuses) => statuses.length && statuses[statuses.length - 1].id,
|
lastStatusId: (statuses) => statuses.length && statuses[statuses.length - 1].id,
|
||||||
label: (target, $currentInstance) => `${target} timeline for ${$currentInstance}`
|
label: (timeline, $currentInstance) => `${timelines[timeline].label} timeline for ${$currentInstance}`
|
||||||
},
|
},
|
||||||
store: () => store,
|
store: () => store,
|
||||||
components: {
|
components: {
|
||||||
|
@ -69,10 +70,10 @@
|
||||||
let instanceData = this.store.get('currentInstanceData')
|
let instanceData = this.store.get('currentInstanceData')
|
||||||
let online = this.get('online')
|
let online = this.get('online')
|
||||||
let newStatuses = online ?
|
let newStatuses = online ?
|
||||||
await getHomeTimeline(instanceName, instanceData.access_token, lastStatusId, FETCH_LIMIT) :
|
await getTimeline(instanceName, instanceData.access_token, this.get('timeline'), lastStatusId, FETCH_LIMIT) :
|
||||||
await getTimelineFromDatabaseAfter(lastStatusId, FETCH_LIMIT)
|
await getTimelineFromDatabase(instanceName, this.get('timeline'), lastStatusId, FETCH_LIMIT)
|
||||||
if (online) {
|
if (online) {
|
||||||
insertStatusesIntoDatabase(newStatuses)
|
insertStatusesIntoDatabase(instanceName, this.get('timeline'), newStatuses)
|
||||||
}
|
}
|
||||||
let statuses = this.get('statuses')
|
let statuses = this.get('statuses')
|
||||||
if (statuses) {
|
if (statuses) {
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
const timelines = {
|
||||||
|
home: { name: 'home', label: 'Home' },
|
||||||
|
local: { name: 'local', label: 'Local' },
|
||||||
|
federated: { name: 'federated', label: 'Federated' }
|
||||||
|
}
|
||||||
|
|
||||||
|
export { timelines }
|
|
@ -25,15 +25,10 @@ const importIndexedDBGetAllShim = () => import(
|
||||||
/* webpackChunkName: 'indexeddb-getall-shim' */ 'indexeddb-getall-shim'
|
/* webpackChunkName: 'indexeddb-getall-shim' */ 'indexeddb-getall-shim'
|
||||||
)
|
)
|
||||||
|
|
||||||
const importOfflineNotification = () => import(
|
|
||||||
/* webpackHunkName: 'offlineNotification' */ '../_utils/offlineNotification'
|
|
||||||
)
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
importURLSearchParams,
|
importURLSearchParams,
|
||||||
importTimeline,
|
importTimeline,
|
||||||
importIntersectionObserver,
|
importIntersectionObserver,
|
||||||
importRequestIdleCallback,
|
importRequestIdleCallback,
|
||||||
importIndexedDBGetAllShim,
|
importIndexedDBGetAllShim
|
||||||
importOfflineNotification
|
|
||||||
}
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
import keyval from 'idb-keyval'
|
||||||
|
export { keyval }
|
|
@ -1,7 +1,9 @@
|
||||||
|
import { keyval } from './keyval'
|
||||||
import cloneDeep from 'lodash/cloneDeep'
|
import cloneDeep from 'lodash/cloneDeep'
|
||||||
import padStart from 'lodash/padStart'
|
import padStart from 'lodash/padStart'
|
||||||
|
|
||||||
const STORE = 'statuses'
|
const STORE = 'statuses'
|
||||||
|
const databaseCache = {}
|
||||||
|
|
||||||
function toPaddedBigInt(id) {
|
function toPaddedBigInt(id) {
|
||||||
return padStart(id, 30, '0')
|
return padStart(id, 30, '0')
|
||||||
|
@ -24,26 +26,40 @@ function transformStatusForStorage(status) {
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
const dbPromise = new Promise((resolve, reject) => {
|
function getDatabase(instanceName, timeline) {
|
||||||
let req = indexedDB.open(STORE, 1)
|
const key = `${instanceName}_${timeline}`
|
||||||
req.onerror = reject
|
if (databaseCache[key]) {
|
||||||
req.onblocked = () => {
|
return Promise.resolve(databaseCache[key])
|
||||||
console.log('idb blocked')
|
|
||||||
}
|
}
|
||||||
req.onupgradeneeded = () => {
|
|
||||||
let db = req.result;
|
|
||||||
let oStore = db.createObjectStore(STORE, {
|
|
||||||
keyPath: 'id'
|
|
||||||
})
|
|
||||||
oStore.createIndex('created_at', 'created_at')
|
|
||||||
oStore.createIndex('pinafore_id_as_negative_big_int', 'pinafore_id_as_negative_big_int')
|
|
||||||
oStore.createIndex('pinafore_id_as_big_int', 'pinafore_id_as_big_int')
|
|
||||||
}
|
|
||||||
req.onsuccess = () => resolve(req.result)
|
|
||||||
})
|
|
||||||
|
|
||||||
export async function getTimelineAfter(max_id = null, limit = 20) {
|
let objectStoreName = `${STORE}_${key}`
|
||||||
const db = await dbPromise
|
|
||||||
|
keyval.get('known_dbs').then(knownDbs => {
|
||||||
|
knownDbs = knownDbs || {}
|
||||||
|
knownDbs[objectStoreName] = true
|
||||||
|
keyval.set('known_dbs', knownDbs)
|
||||||
|
})
|
||||||
|
|
||||||
|
databaseCache[key] = new Promise((resolve, reject) => {
|
||||||
|
let req = indexedDB.open(objectStoreName, 1)
|
||||||
|
req.onerror = reject
|
||||||
|
req.onblocked = () => {
|
||||||
|
console.log('idb blocked')
|
||||||
|
}
|
||||||
|
req.onupgradeneeded = () => {
|
||||||
|
let db = req.result;
|
||||||
|
let oStore = db.createObjectStore(STORE, {
|
||||||
|
keyPath: 'id'
|
||||||
|
})
|
||||||
|
oStore.createIndex('pinafore_id_as_negative_big_int', 'pinafore_id_as_negative_big_int')
|
||||||
|
}
|
||||||
|
req.onsuccess = () => resolve(req.result)
|
||||||
|
})
|
||||||
|
return databaseCache[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getTimeline(instanceName, timeline, max_id = null, limit = 20) {
|
||||||
|
const db = await getDatabase(instanceName, timeline)
|
||||||
return await new Promise((resolve, reject) => {
|
return await new Promise((resolve, reject) => {
|
||||||
const tx = db.transaction(STORE, 'readonly')
|
const tx = db.transaction(STORE, 'readonly')
|
||||||
const store = tx.objectStore(STORE)
|
const store = tx.objectStore(STORE)
|
||||||
|
@ -61,8 +77,8 @@ export async function getTimelineAfter(max_id = null, limit = 20) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function insertStatuses(statuses) {
|
export async function insertStatuses(instanceName, timeline, statuses) {
|
||||||
const db = await dbPromise
|
const db = await getDatabase(instanceName, timeline)
|
||||||
return await new Promise((resolve, reject) => {
|
return await new Promise((resolve, reject) => {
|
||||||
const tx = db.transaction(STORE, 'readwrite')
|
const tx = db.transaction(STORE, 'readwrite')
|
||||||
const store = tx.objectStore(STORE)
|
const store = tx.objectStore(STORE)
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { get, paramsString } from '../ajax'
|
import { get, paramsString } from '../ajax'
|
||||||
import { basename } from './utils'
|
import { basename } from './utils'
|
||||||
|
|
||||||
export function getHomeTimeline(instanceName, accessToken, maxId, since) {
|
export function getTimeline(instanceName, accessToken, timeline, maxId, since) {
|
||||||
let url = `${basename(instanceName)}/api/v1/timelines/home`
|
let timelineUrlName = timeline === 'local' || timeline === 'federated' ? 'public' : timeline
|
||||||
|
let url = `${basename(instanceName)}/api/v1/timelines/${timelineUrlName}`
|
||||||
|
|
||||||
let params = {}
|
let params = {}
|
||||||
if (since) {
|
if (since) {
|
||||||
|
@ -13,6 +14,10 @@ export function getHomeTimeline(instanceName, accessToken, maxId, since) {
|
||||||
params.max_id = maxId
|
params.max_id = maxId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (timeline === 'local') {
|
||||||
|
params.local = true
|
||||||
|
}
|
||||||
|
|
||||||
url += '?' + paramsString(params)
|
url += '?' + paramsString(params)
|
||||||
|
|
||||||
return get(url, {
|
return get(url, {
|
||||||
|
|
|
@ -17,5 +17,9 @@ const observe = online => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!navigator.onLine) {
|
||||||
|
observe(false)
|
||||||
|
}
|
||||||
|
|
||||||
window.addEventListener('offline', () => observe(false));
|
window.addEventListener('offline', () => observe(false));
|
||||||
window.addEventListener('online', () => observe(true));
|
window.addEventListener('online', () => observe(true));
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { toast } from './toast'
|
||||||
|
import { keyval } from './database/keyval'
|
||||||
|
|
||||||
|
function onUpdateFound(registration) {
|
||||||
|
const newWorker = registration.installing
|
||||||
|
|
||||||
|
newWorker.addEventListener('statechange', async () => {
|
||||||
|
if (!(await keyval.get('serviceworker_installed'))) {
|
||||||
|
await keyval.set('serviceworker_installed', true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (newWorker.state === 'activated' && navigator.serviceWorker.controller) {
|
||||||
|
toast.say('App update available. Reload to update.')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!location.origin.match('localhost') && 'serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker.register('/service-worker.js').then(registration => {
|
||||||
|
registration.addEventListener('updatefound', () => onUpdateFound(registration))
|
||||||
|
})
|
||||||
|
}
|
|
@ -3,16 +3,29 @@
|
||||||
</:Head>
|
</:Head>
|
||||||
|
|
||||||
<Layout page='federated'>
|
<Layout page='federated'>
|
||||||
<h1>Federated Timeline</h1>
|
{{#if $isUserLoggedIn}}
|
||||||
|
<LazyTimeline timeline='federated' />
|
||||||
|
{{else}}
|
||||||
|
<FreeTextLayout>
|
||||||
|
<h1>Federated</h1>
|
||||||
|
|
||||||
|
<p>Your federated timeline will appear here when logged in.</p>
|
||||||
|
</FreeTextLayout>
|
||||||
|
{{/if}}
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Layout from './_components/Layout.html';
|
import Layout from './_components/Layout.html'
|
||||||
|
import LazyTimeline from './_components/LazyTimeline.html'
|
||||||
|
import FreeTextLayout from './_components/FreeTextLayout.html'
|
||||||
|
import { store } from './_utils/store.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
store: () => store,
|
||||||
components: {
|
components: {
|
||||||
Layout
|
Layout,
|
||||||
|
LazyTimeline,
|
||||||
|
FreeTextLayout
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
<Layout page='home'>
|
<Layout page='home'>
|
||||||
{{#if $isUserLoggedIn}}
|
{{#if $isUserLoggedIn}}
|
||||||
<LazyTimeline target='home' />
|
<LazyTimeline timeline='home' />
|
||||||
{{else}}
|
{{else}}
|
||||||
<NotLoggedInHome/>
|
<NotLoggedInHome/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -3,16 +3,29 @@
|
||||||
</:Head>
|
</:Head>
|
||||||
|
|
||||||
<Layout page='local'>
|
<Layout page='local'>
|
||||||
<h1>Local</h1>
|
{{#if $isUserLoggedIn}}
|
||||||
|
<LazyTimeline timeline='local' />
|
||||||
|
{{else}}
|
||||||
|
<FreeTextLayout>
|
||||||
|
<h1>Local</h1>
|
||||||
|
|
||||||
|
<p>Your local timeline will appear here when logged in.</p>
|
||||||
|
</FreeTextLayout>
|
||||||
|
{{/if}}
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Layout from './_components/Layout.html';
|
import Layout from './_components/Layout.html'
|
||||||
|
import LazyTimeline from './_components/LazyTimeline.html'
|
||||||
|
import FreeTextLayout from './_components/FreeTextLayout.html'
|
||||||
|
import { store } from './_utils/store.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
store: () => store,
|
||||||
components: {
|
components: {
|
||||||
Layout
|
Layout,
|
||||||
|
LazyTimeline,
|
||||||
|
FreeTextLayout
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
|
@ -3,16 +3,21 @@
|
||||||
</:Head>
|
</:Head>
|
||||||
|
|
||||||
<Layout page='notifications'>
|
<Layout page='notifications'>
|
||||||
<h1>Notifications</h1>
|
<FreeTextLayout>
|
||||||
|
<h1>Notifications</h1>
|
||||||
|
|
||||||
|
<p>Your notifications will appear here when logged in.</p>
|
||||||
|
</FreeTextLayout>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Layout from './_components/Layout.html';
|
import Layout from './_components/Layout.html';
|
||||||
|
import FreeTextLayout from './_components/FreeTextLayout.html'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Layout
|
Layout,
|
||||||
|
FreeTextLayout
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
|
@ -16,7 +16,7 @@
|
||||||
<span class="acct-display-name">{{instanceUserAccount.display_name}}</span>
|
<span class="acct-display-name">{{instanceUserAccount.display_name}}</span>
|
||||||
</div>
|
</div>
|
||||||
<h2>Theme:</h2>
|
<h2>Theme:</h2>
|
||||||
<form class="theme-chooser">
|
<form class="theme-chooser" aria-label="Choose a theme">
|
||||||
{{#each themes as theme}}
|
{{#each themes as theme}}
|
||||||
<div class="theme-group">
|
<div class="theme-group">
|
||||||
<input type="radio" id="choice-theme-{{theme.name}}"
|
<input type="radio" id="choice-theme-{{theme.name}}"
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form class="instance-actions">
|
<form class="instance-actions" aria-label="Switch to or log out of this instance">
|
||||||
{{#if $loggedInInstancesInOrder.length > 1}}
|
{{#if $loggedInInstancesInOrder.length > 1}}
|
||||||
<button class="primary" disabled="{{$currentInstance === params.instanceName}}"
|
<button class="primary" disabled="{{$currentInstance === params.instanceName}}"
|
||||||
on:click="onSwitchToThisInstance()">
|
on:click="onSwitchToThisInstance()">
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
<Layout page='settings'>
|
<Layout page='settings'>
|
||||||
<SettingsLayout page='settings/instances/add' label="Add an Instance">
|
<SettingsLayout page='settings/instances/add' label="Add an Instance">
|
||||||
<h1>Add an Instance</h1>
|
<h1 id="add-an-instance-h1">Add an Instance</h1>
|
||||||
|
|
||||||
<LoadingMask show="{{loading}}"/>
|
<LoadingMask show="{{loading}}"/>
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
<p>Log in to an instance to start using Pinafore.</p>
|
<p>Log in to an instance to start using Pinafore.</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<form class="add-new-instance" on:submit='onSubmit(event)'>
|
<form class="add-new-instance" on:submit='onSubmit(event)' aria-labelledby="add-an-instance-h1">
|
||||||
<label for="instanceInput">Instance:</label>
|
<label for="instanceInput">Instance:</label>
|
||||||
<input type="text" id="instanceInput" bind:value='$instanceNameInSearch' placeholder=''>
|
<input type="text" id="instanceInput" bind:value='$instanceNameInSearch' placeholder=''>
|
||||||
<button class="primary" type="submit" id="submitButton" disabled="{{!$instanceNameInSearch}}">Add instance</button>
|
<button class="primary" type="submit" id="submitButton" disabled="{{!$instanceNameInSearch}}">Add instance</button>
|
||||||
|
|
|
@ -8,12 +8,6 @@
|
||||||
<link rel='manifest' href='/manifest.json'>
|
<link rel='manifest' href='/manifest.json'>
|
||||||
<link rel='icon' type='image/png' href='/favicon.png'>
|
<link rel='icon' type='image/png' href='/favicon.png'>
|
||||||
|
|
||||||
<script>
|
|
||||||
if (!location.origin.match('localhost') && 'serviceWorker' in navigator) {
|
|
||||||
navigator.serviceWorker.register('/service-worker.js');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* auto-generated w/ build-sass.js */
|
/* auto-generated w/ build-sass.js */
|
||||||
body{--button-primary-bg:#6081e6;--button-primary-text:#fff;--button-primary-border:#132c76;--button-primary-bg-active:#456ce2;--button-primary-bg-hover:#6988e7;--button-bg:#e6e6e6;--button-text:#333;--button-border:#a7a7a7;--button-bg-active:#bfbfbf;--button-bg-hover:#f2f2f2;--input-border:#dadada;--anchor-text:#4169e1;--main-bg:#fff;--body-bg:#e8edfb;--body-text-color:#333;--main-border:#dadada;--svg-fill:#4169e1;--form-bg:#f7f7f7;--form-border:#c1c1c1;--nav-bg:#4169e1;--nav-border:#214cce;--nav-a-border:#4169e1;--nav-a-selected-border:#fff;--nav-a-selected-bg:#6d8ce8;--nav-svg-fill:#fff;--nav-text-color:#fff;--nav-a-selected-border-hover:#fff;--nav-a-selected-bg-hover:#839deb;--nav-a-bg-hover:#577ae4;--nav-a-border-hover:#4169e1;--nav-svg-fill-hover:#fff;--nav-text-color-hover:#fff;--action-button-fill-color:#839deb;--action-button-fill-color-hover:#99afef;--action-button-fill-color-active:#577ae4;--settings-list-item-bg:#fff;--settings-list-item-text:#4169e1;--settings-list-item-text-hover:#4169e1;--settings-list-item-border:#dadada;--settings-list-item-bg-active:#e6e6e6;--settings-list-item-bg-hover:#fafafa;--toast-bg:#333;--toast-border:#fafafa;--toast-text:#fff;--mask-bg:#333;--mask-svg-fill:#fff;--deemphasized-text-color:#666}
|
body{--button-primary-bg:#6081e6;--button-primary-text:#fff;--button-primary-border:#132c76;--button-primary-bg-active:#456ce2;--button-primary-bg-hover:#6988e7;--button-bg:#e6e6e6;--button-text:#333;--button-border:#a7a7a7;--button-bg-active:#bfbfbf;--button-bg-hover:#f2f2f2;--input-border:#dadada;--anchor-text:#4169e1;--main-bg:#fff;--body-bg:#e8edfb;--body-text-color:#333;--main-border:#dadada;--svg-fill:#4169e1;--form-bg:#f7f7f7;--form-border:#c1c1c1;--nav-bg:#4169e1;--nav-border:#214cce;--nav-a-border:#4169e1;--nav-a-selected-border:#fff;--nav-a-selected-bg:#6d8ce8;--nav-svg-fill:#fff;--nav-text-color:#fff;--nav-a-selected-border-hover:#fff;--nav-a-selected-bg-hover:#839deb;--nav-a-bg-hover:#577ae4;--nav-a-border-hover:#4169e1;--nav-svg-fill-hover:#fff;--nav-text-color-hover:#fff;--action-button-fill-color:#839deb;--action-button-fill-color-hover:#99afef;--action-button-fill-color-active:#577ae4;--settings-list-item-bg:#fff;--settings-list-item-text:#4169e1;--settings-list-item-text-hover:#4169e1;--settings-list-item-border:#dadada;--settings-list-item-bg-active:#e6e6e6;--settings-list-item-bg-hover:#fafafa;--toast-bg:#333;--toast-border:#fafafa;--toast-text:#fff;--mask-bg:#333;--mask-svg-fill:#fff;--deemphasized-text-color:#666}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { init } from 'sapper/runtime.js'
|
import { init } from 'sapper/runtime.js'
|
||||||
import { toast } from '../routes/_utils/toast'
|
import { offlineNotifiction } from '../routes/_utils/offlineNotification'
|
||||||
|
import { serviceWorkerClient } from '../routes/_utils/serviceWorkerClient'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
importURLSearchParams,
|
importURLSearchParams,
|
||||||
importIntersectionObserver,
|
importIntersectionObserver,
|
||||||
importRequestIdleCallback,
|
importRequestIdleCallback,
|
||||||
importIndexedDBGetAllShim,
|
importIndexedDBGetAllShim
|
||||||
importOfflineNotification
|
|
||||||
} from '../routes/_utils/asyncModules'
|
} from '../routes/_utils/asyncModules'
|
||||||
|
|
||||||
// polyfills
|
// polyfills
|
||||||
|
@ -18,14 +18,4 @@ Promise.all([
|
||||||
]).then(() => {
|
]).then(() => {
|
||||||
// `routes` is an array of route objects injected by Sapper
|
// `routes` is an array of route objects injected by Sapper
|
||||||
init(document.querySelector('#sapper'), __routes__)
|
init(document.querySelector('#sapper'), __routes__)
|
||||||
|
})
|
||||||
if (navigator.serviceWorker && navigator.serviceWorker.controller) {
|
|
||||||
navigator.serviceWorker.controller.onstatechange = (e) => {
|
|
||||||
if (e.target.state === 'redundant') {
|
|
||||||
toast.say('App update available. Reload to update.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
importOfflineNotification()
|
|
Loading…
Reference in New Issue