use standard
This commit is contained in:
parent
537a112adb
commit
2e83bc0ff9
|
@ -21,7 +21,7 @@ const scssDir = path.join(__dirname, '../scss')
|
||||||
const themesScssDir = path.join(__dirname, '../scss/themes')
|
const themesScssDir = path.join(__dirname, '../scss/themes')
|
||||||
const assetsDir = path.join(__dirname, '../assets')
|
const assetsDir = path.join(__dirname, '../assets')
|
||||||
|
|
||||||
function doWatch() {
|
function doWatch () {
|
||||||
var start = now()
|
var start = now()
|
||||||
chokidar.watch(scssDir).on('change', debounce(() => {
|
chokidar.watch(scssDir).on('change', debounce(() => {
|
||||||
console.log('Recompiling SCSS...')
|
console.log('Recompiling SCSS...')
|
||||||
|
@ -35,7 +35,7 @@ function doWatch() {
|
||||||
chokidar.watch()
|
chokidar.watch()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function compileGlobalSass() {
|
async function compileGlobalSass () {
|
||||||
let results = await Promise.all([
|
let results = await Promise.all([
|
||||||
render({file: defaultThemeScss, outputStyle: 'compressed'}),
|
render({file: defaultThemeScss, outputStyle: 'compressed'}),
|
||||||
render({file: globalScss, outputStyle: 'compressed'}),
|
render({file: globalScss, outputStyle: 'compressed'}),
|
||||||
|
@ -51,7 +51,7 @@ async function compileGlobalSass() {
|
||||||
await writeFile(html2xxFile, html, 'utf8')
|
await writeFile(html2xxFile, html, 'utf8')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function compileThemesSass() {
|
async function compileThemesSass () {
|
||||||
let files = (await readdir(themesScssDir)).filter(file => !path.basename(file).startsWith('_'))
|
let files = (await readdir(themesScssDir)).filter(file => !path.basename(file).startsWith('_'))
|
||||||
await Promise.all(files.map(async file => {
|
await Promise.all(files.map(async file => {
|
||||||
let res = await render({file: path.join(themesScssDir, file)})
|
let res = await render({file: path.join(themesScssDir, file)})
|
||||||
|
@ -60,7 +60,7 @@ async function compileThemesSass() {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main () {
|
||||||
await Promise.all([compileGlobalSass(), compileThemesSass()])
|
await Promise.all([compileGlobalSass(), compileThemesSass()])
|
||||||
if (argv.watch) {
|
if (argv.watch) {
|
||||||
doWatch()
|
doWatch()
|
||||||
|
@ -70,4 +70,4 @@ async function main() {
|
||||||
Promise.resolve().then(main).catch(err => {
|
Promise.resolve().then(main).catch(err => {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
})
|
})
|
||||||
|
|
|
@ -11,7 +11,7 @@ const $ = require('cheerio')
|
||||||
const readFile = pify(fs.readFile.bind(fs))
|
const readFile = pify(fs.readFile.bind(fs))
|
||||||
const writeFile = pify(fs.writeFile.bind(fs))
|
const writeFile = pify(fs.writeFile.bind(fs))
|
||||||
|
|
||||||
async function main() {
|
async function main () {
|
||||||
let result = (await Promise.all(svgs.map(async svg => {
|
let result = (await Promise.all(svgs.map(async svg => {
|
||||||
let filepath = path.join(__dirname, '../', svg.src)
|
let filepath = path.join(__dirname, '../', svg.src)
|
||||||
let content = await readFile(filepath, 'utf8')
|
let content = await readFile(filepath, 'utf8')
|
||||||
|
@ -40,4 +40,4 @@ async function main() {
|
||||||
main().catch(err => {
|
main().catch(err => {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
})
|
})
|
||||||
|
|
|
@ -11,10 +11,10 @@ const readFile = pify(fs.readFile.bind(fs))
|
||||||
const glob = pify(require('glob'))
|
const glob = pify(require('glob'))
|
||||||
const rimraf = pify(require('rimraf'))
|
const rimraf = pify(require('rimraf'))
|
||||||
|
|
||||||
const selectorRegex = /\n[ \t]*([0-9\w\- \t\.:#,]+?)[ \t]*\{/g
|
const selectorRegex = /\n[ \t]*([0-9\w\- \t.:#,]+?)[ \t]*{/g
|
||||||
const styleRegex = /<style>[\s\S]+?<\/style>/
|
const styleRegex = /<style>[\s\S]+?<\/style>/
|
||||||
|
|
||||||
async function main() {
|
async function main () {
|
||||||
if (argv.reverse) { // reverse the operation we just did
|
if (argv.reverse) { // reverse the operation we just did
|
||||||
let tmpComponents = await glob('./routes/**/.tmp-*.html')
|
let tmpComponents = await glob('./routes/**/.tmp-*.html')
|
||||||
for (let filename of tmpComponents) {
|
for (let filename of tmpComponents) {
|
||||||
|
@ -29,7 +29,7 @@ async function main() {
|
||||||
let text = await readFile(filename, 'utf8')
|
let text = await readFile(filename, 'utf8')
|
||||||
let newText = text.replace(styleRegex, style => {
|
let newText = text.replace(styleRegex, style => {
|
||||||
return style.replace(selectorRegex, selectorMatch => {
|
return style.replace(selectorRegex, selectorMatch => {
|
||||||
return selectorMatch.replace(/\S[^\{]+/, selector => `:global(${selector})`)
|
return selectorMatch.replace(/\S[^{]+/, selector => `:global(${selector})`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
let newFilename = path.join(path.dirname(filename), '.tmp-' + path.basename(filename))
|
let newFilename = path.join(path.dirname(filename), '.tmp-' + path.basename(filename))
|
||||||
|
@ -43,4 +43,4 @@ async function main() {
|
||||||
Promise.resolve().then(main).catch(err => {
|
Promise.resolve().then(main).catch(err => {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
})
|
})
|
||||||
|
|
48
bin/svgs.js
48
bin/svgs.js
|
@ -1,26 +1,26 @@
|
||||||
module.exports = [
|
module.exports = [
|
||||||
{id: 'pinafore-logo', src: 'original-assets/sailboat.svg', title: 'Home'},
|
{id: 'pinafore-logo', src: 'original-assets/sailboat.svg', title: 'Home'},
|
||||||
{id:'fa-bell', src:'node_modules/font-awesome-svg-png/white/svg/bell.svg', title: 'Notifications'},
|
{id: 'fa-bell', src: 'node_modules/font-awesome-svg-png/white/svg/bell.svg', title: 'Notifications'},
|
||||||
{id:'fa-users', src:'node_modules/font-awesome-svg-png/white/svg/users.svg', title: 'Local'},
|
{id: 'fa-users', src: 'node_modules/font-awesome-svg-png/white/svg/users.svg', title: 'Local'},
|
||||||
{id:'fa-globe', src:'node_modules/font-awesome-svg-png/white/svg/globe.svg', title: 'Federated'},
|
{id: 'fa-globe', src: 'node_modules/font-awesome-svg-png/white/svg/globe.svg', title: 'Federated'},
|
||||||
{id:'fa-gear', src:'node_modules/font-awesome-svg-png/white/svg/gear.svg', title: 'Settings'},
|
{id: 'fa-gear', src: 'node_modules/font-awesome-svg-png/white/svg/gear.svg', title: 'Settings'},
|
||||||
{id:'fa-reply', src:'node_modules/font-awesome-svg-png/white/svg/reply.svg', title: 'Reply'},
|
{id: 'fa-reply', src: 'node_modules/font-awesome-svg-png/white/svg/reply.svg', title: 'Reply'},
|
||||||
{id:'fa-retweet', src:'node_modules/font-awesome-svg-png/white/svg/retweet.svg', title: 'Boost'},
|
{id: 'fa-retweet', src: 'node_modules/font-awesome-svg-png/white/svg/retweet.svg', title: 'Boost'},
|
||||||
{id:'fa-star', src:'node_modules/font-awesome-svg-png/white/svg/star.svg', title: 'Favorite'},
|
{id: 'fa-star', src: 'node_modules/font-awesome-svg-png/white/svg/star.svg', title: 'Favorite'},
|
||||||
{id:'fa-ellipsis-h', src:'node_modules/font-awesome-svg-png/white/svg/ellipsis-h.svg', title: 'More'},
|
{id: 'fa-ellipsis-h', src: 'node_modules/font-awesome-svg-png/white/svg/ellipsis-h.svg', title: 'More'},
|
||||||
{id:'fa-spinner', src:'node_modules/font-awesome-svg-png/white/svg/spinner.svg', title: 'Spinner'},
|
{id: 'fa-spinner', src: 'node_modules/font-awesome-svg-png/white/svg/spinner.svg', title: 'Spinner'},
|
||||||
{id:'fa-user', src:'node_modules/font-awesome-svg-png/white/svg/user.svg', title: 'Empty user profile'},
|
{id: 'fa-user', src: 'node_modules/font-awesome-svg-png/white/svg/user.svg', title: 'Empty user profile'},
|
||||||
{id:'fa-play-circle', src:'node_modules/font-awesome-svg-png/white/svg/play-circle.svg', title: 'Play'},
|
{id: 'fa-play-circle', src: 'node_modules/font-awesome-svg-png/white/svg/play-circle.svg', title: 'Play'},
|
||||||
{id:'fa-eye', src:'node_modules/font-awesome-svg-png/white/svg/eye.svg', title: 'Show Sensitive Content'},
|
{id: 'fa-eye', src: 'node_modules/font-awesome-svg-png/white/svg/eye.svg', title: 'Show Sensitive Content'},
|
||||||
{id:'fa-eye-slash', src:'node_modules/font-awesome-svg-png/white/svg/eye-slash.svg', title: 'Hide Sensitive Content'},
|
{id: 'fa-eye-slash', src: 'node_modules/font-awesome-svg-png/white/svg/eye-slash.svg', title: 'Hide Sensitive Content'},
|
||||||
{id:'fa-lock', src:'node_modules/font-awesome-svg-png/white/svg/lock.svg', title: 'Locked'},
|
{id: 'fa-lock', src: 'node_modules/font-awesome-svg-png/white/svg/lock.svg', title: 'Locked'},
|
||||||
{id:'fa-envelope', src:'node_modules/font-awesome-svg-png/white/svg/envelope.svg', title: 'Sealed Envelope'},
|
{id: 'fa-envelope', src: 'node_modules/font-awesome-svg-png/white/svg/envelope.svg', title: 'Sealed Envelope'},
|
||||||
{id:'fa-user-times', src:'node_modules/font-awesome-svg-png/white/svg/user-times.svg', title: 'Stop Following'},
|
{id: 'fa-user-times', src: 'node_modules/font-awesome-svg-png/white/svg/user-times.svg', title: 'Stop Following'},
|
||||||
{id:'fa-user-plus', src:'node_modules/font-awesome-svg-png/white/svg/user-plus.svg', title: 'Follow'},
|
{id: 'fa-user-plus', src: 'node_modules/font-awesome-svg-png/white/svg/user-plus.svg', title: 'Follow'},
|
||||||
{id:'fa-external-link', src:'node_modules/font-awesome-svg-png/white/svg/external-link.svg', title: 'External Link'},
|
{id: 'fa-external-link', src: 'node_modules/font-awesome-svg-png/white/svg/external-link.svg', title: 'External Link'},
|
||||||
{id:'fa-search', src:'node_modules/font-awesome-svg-png/white/svg/search.svg', title: 'Search'},
|
{id: 'fa-search', src: 'node_modules/font-awesome-svg-png/white/svg/search.svg', title: 'Search'},
|
||||||
{id:'fa-comments', src:'node_modules/font-awesome-svg-png/white/svg/comments.svg', title: 'Conversations'},
|
{id: 'fa-comments', src: 'node_modules/font-awesome-svg-png/white/svg/comments.svg', title: 'Conversations'},
|
||||||
{id:'fa-paperclip', src:'node_modules/font-awesome-svg-png/white/svg/paperclip.svg', title: 'Paperclip'},
|
{id: 'fa-paperclip', src: 'node_modules/font-awesome-svg-png/white/svg/paperclip.svg', title: 'Paperclip'},
|
||||||
{id:'fa-thumbtack', src:'node_modules/font-awesome-svg-png/white/svg/thumb-tack.svg', title: 'Thumbtack'},
|
{id: 'fa-thumbtack', src: 'node_modules/font-awesome-svg-png/white/svg/thumb-tack.svg', title: 'Thumbtack'},
|
||||||
{id:'fa-bars', src:'node_modules/font-awesome-svg-png/white/svg/bars.svg', title: 'List'},
|
{id: 'fa-bars', src: 'node_modules/font-awesome-svg-png/white/svg/bars.svg', title: 'List'}
|
||||||
]
|
]
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
22
package.json
22
package.json
|
@ -3,6 +3,7 @@
|
||||||
"description": "TODO",
|
"description": "TODO",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"lint": "standard",
|
||||||
"dev": "npm run build-svg && concurrently --kill-others \"npm run build-sass-watch\" \"node server.js\"",
|
"dev": "npm run build-svg && concurrently --kill-others \"npm run build-sass-watch\" \"node server.js\"",
|
||||||
"build": "npm run globalize-css && npm run build-sass && npm run build-svg && sapper build && npm run deglobalize-css",
|
"build": "npm run globalize-css && npm run build-sass && npm run build-svg && sapper build && npm run deglobalize-css",
|
||||||
"start": "cross-env NODE_ENV=production node server.js",
|
"start": "cross-env NODE_ENV=production node server.js",
|
||||||
|
@ -47,6 +48,7 @@
|
||||||
"rimraf": "^2.6.2",
|
"rimraf": "^2.6.2",
|
||||||
"sapper": "nolanlawson/sapper#fix-style-loader-built",
|
"sapper": "nolanlawson/sapper#fix-style-loader-built",
|
||||||
"serve-static": "^1.13.1",
|
"serve-static": "^1.13.1",
|
||||||
|
"standard": "^10.0.3",
|
||||||
"style-loader": "^0.19.1",
|
"style-loader": "^0.19.1",
|
||||||
"svelte": "^1.54.0",
|
"svelte": "^1.54.0",
|
||||||
"svelte-extras": "^1.6.0",
|
"svelte-extras": "^1.6.0",
|
||||||
|
@ -63,5 +65,25 @@
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
|
},
|
||||||
|
"standard": {
|
||||||
|
"globals": [
|
||||||
|
"fetch",
|
||||||
|
"IDBKeyRange",
|
||||||
|
"IDBObjectStore",
|
||||||
|
"indexedDB",
|
||||||
|
"requestAnimationFrame",
|
||||||
|
"requestIdleCallback",
|
||||||
|
"location",
|
||||||
|
"localStorage",
|
||||||
|
"URLSearchParams",
|
||||||
|
"IntersectionObserver",
|
||||||
|
"URL"
|
||||||
|
],
|
||||||
|
"ignore": [
|
||||||
|
"dist",
|
||||||
|
"cypress",
|
||||||
|
"routes/_utils/asyncModules.js"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { getAccount, getRelationship } from '../_api/user'
|
||||||
import { database } from '../_database/database'
|
import { database } from '../_database/database'
|
||||||
import { store } from '../_store/store'
|
import { store } from '../_store/store'
|
||||||
|
|
||||||
async function updateAccount(accountId, instanceName, accessToken) {
|
async function updateAccount (accountId, instanceName, accessToken) {
|
||||||
let localPromise = database.getAccount(instanceName, accountId)
|
let localPromise = database.getAccount(instanceName, accountId)
|
||||||
let remotePromise = getAccount(instanceName, accessToken, accountId).then(account => {
|
let remotePromise = getAccount(instanceName, accessToken, accountId).then(account => {
|
||||||
database.setAccount(instanceName, account)
|
database.setAccount(instanceName, account)
|
||||||
|
@ -21,7 +21,7 @@ async function updateAccount(accountId, instanceName, accessToken) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateRelationship(accountId, instanceName, accessToken) {
|
async function updateRelationship (accountId, instanceName, accessToken) {
|
||||||
let localPromise = database.getRelationship(instanceName, accountId)
|
let localPromise = database.getRelationship(instanceName, accountId)
|
||||||
let remotePromise = getRelationship(instanceName, accessToken, accountId).then(relationship => {
|
let remotePromise = getRelationship(instanceName, accessToken, accountId).then(relationship => {
|
||||||
database.setRelationship(instanceName, relationship)
|
database.setRelationship(instanceName, relationship)
|
||||||
|
@ -39,7 +39,7 @@ async function updateRelationship(accountId, instanceName, accessToken) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateProfileAndRelationship(accountId) {
|
export async function updateProfileAndRelationship (accountId) {
|
||||||
store.set({
|
store.set({
|
||||||
currentAccountProfile: null,
|
currentAccountProfile: null,
|
||||||
currentAccountRelationship: null
|
currentAccountRelationship: null
|
||||||
|
@ -51,4 +51,4 @@ export async function updateProfileAndRelationship(accountId) {
|
||||||
updateAccount(accountId, instanceName, accessToken),
|
updateAccount(accountId, instanceName, accessToken),
|
||||||
updateRelationship(accountId, instanceName, accessToken)
|
updateRelationship(accountId, instanceName, accessToken)
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,10 @@ import { database } from '../_database/database'
|
||||||
import { store } from '../_store/store'
|
import { store } from '../_store/store'
|
||||||
import { updateVerifyCredentialsForInstance } from './instances'
|
import { updateVerifyCredentialsForInstance } from './instances'
|
||||||
|
|
||||||
const REDIRECT_URI = (typeof location !== 'undefined' ?
|
const REDIRECT_URI = (typeof location !== 'undefined'
|
||||||
location.origin : 'https://pinafore.social') + '/settings/instances/add'
|
? location.origin : 'https://pinafore.social') + '/settings/instances/add'
|
||||||
|
|
||||||
async function redirectToOauth() {
|
async function redirectToOauth () {
|
||||||
let instanceName = store.get('instanceNameInSearch')
|
let instanceName = store.get('instanceNameInSearch')
|
||||||
let loggedInInstances = store.get('loggedInInstances')
|
let loggedInInstances = store.get('loggedInInstances')
|
||||||
instanceName = instanceName.replace(/^https?:\/\//, '').replace('/$', '').toLowerCase()
|
instanceName = instanceName.replace(/^https?:\/\//, '').replace('/$', '').toLowerCase()
|
||||||
|
@ -34,7 +34,7 @@ async function redirectToOauth() {
|
||||||
document.location.href = oauthUrl
|
document.location.href = oauthUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function logInToInstance() {
|
export async function logInToInstance () {
|
||||||
store.set({logInToInstanceLoading: true})
|
store.set({logInToInstanceLoading: true})
|
||||||
store.set({logInToInstanceError: null})
|
store.set({logInToInstanceError: null})
|
||||||
try {
|
try {
|
||||||
|
@ -42,16 +42,16 @@ export async function logInToInstance() {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
let error = `${err.message || err.name}. ` +
|
let error = `${err.message || err.name}. ` +
|
||||||
(navigator.onLine ?
|
(navigator.onLine
|
||||||
`Is this a valid Mastodon instance?` :
|
? `Is this a valid Mastodon instance?`
|
||||||
`Are you offline?`)
|
: `Are you offline?`)
|
||||||
store.set({logInToInstanceError: error})
|
store.set({logInToInstanceError: error})
|
||||||
} finally {
|
} finally {
|
||||||
store.set({logInToInstanceLoading: false})
|
store.set({logInToInstanceLoading: false})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function registerNewInstance(code) {
|
async function registerNewInstance (code) {
|
||||||
let currentRegisteredInstanceName = store.get('currentRegisteredInstanceName')
|
let currentRegisteredInstanceName = store.get('currentRegisteredInstanceName')
|
||||||
let currentRegisteredInstance = store.get('currentRegisteredInstance')
|
let currentRegisteredInstance = store.get('currentRegisteredInstance')
|
||||||
let instanceData = await getAccessTokenFromAuthCode(
|
let instanceData = await getAccessTokenFromAuthCode(
|
||||||
|
@ -85,7 +85,7 @@ async function registerNewInstance(code) {
|
||||||
goto('/')
|
goto('/')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function handleOauthCode(code) {
|
export async function handleOauthCode (code) {
|
||||||
try {
|
try {
|
||||||
store.set({logInToInstanceLoading: true})
|
store.set({logInToInstanceLoading: true})
|
||||||
await registerNewInstance(code)
|
await registerNewInstance(code)
|
||||||
|
@ -95,4 +95,3 @@ export async function handleOauthCode(code) {
|
||||||
store.set({logInToInstanceLoading: false})
|
store.set({logInToInstanceLoading: false})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { database } from '../_database/database'
|
||||||
import { goto } from 'sapper/runtime.js'
|
import { goto } from 'sapper/runtime.js'
|
||||||
import { cacheFirstUpdateAfter } from '../_utils/sync'
|
import { cacheFirstUpdateAfter } from '../_utils/sync'
|
||||||
|
|
||||||
export function changeTheme(instanceName, newTheme) {
|
export function changeTheme (instanceName, newTheme) {
|
||||||
let instanceThemes = store.get('instanceThemes')
|
let instanceThemes = store.get('instanceThemes')
|
||||||
instanceThemes[instanceName] = newTheme
|
instanceThemes[instanceName] = newTheme
|
||||||
store.set({instanceThemes: instanceThemes})
|
store.set({instanceThemes: instanceThemes})
|
||||||
|
@ -16,22 +16,22 @@ export function changeTheme(instanceName, newTheme) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function switchToInstance(instanceName) {
|
export function switchToInstance (instanceName) {
|
||||||
let instanceThemes = store.get('instanceThemes')
|
let instanceThemes = store.get('instanceThemes')
|
||||||
store.set({currentInstance: instanceName})
|
store.set({currentInstance: instanceName})
|
||||||
store.save()
|
store.save()
|
||||||
switchToTheme(instanceThemes[instanceName])
|
switchToTheme(instanceThemes[instanceName])
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function logOutOfInstance(instanceName) {
|
export async function logOutOfInstance (instanceName) {
|
||||||
let loggedInInstances = store.get('loggedInInstances')
|
let loggedInInstances = store.get('loggedInInstances')
|
||||||
let instanceThemes = store.get('instanceThemes')
|
let instanceThemes = store.get('instanceThemes')
|
||||||
let loggedInInstancesInOrder = store.get('loggedInInstancesInOrder')
|
let loggedInInstancesInOrder = store.get('loggedInInstancesInOrder')
|
||||||
let currentInstance = store.get('currentInstance')
|
let currentInstance = store.get('currentInstance')
|
||||||
loggedInInstancesInOrder.splice(loggedInInstancesInOrder.indexOf(instanceName), 1)
|
loggedInInstancesInOrder.splice(loggedInInstancesInOrder.indexOf(instanceName), 1)
|
||||||
let newInstance = instanceName === currentInstance ?
|
let newInstance = instanceName === currentInstance
|
||||||
loggedInInstancesInOrder[0] :
|
? loggedInInstancesInOrder[0]
|
||||||
currentInstance
|
: currentInstance
|
||||||
delete loggedInInstances[instanceName]
|
delete loggedInInstances[instanceName]
|
||||||
delete instanceThemes[instanceName]
|
delete instanceThemes[instanceName]
|
||||||
store.set({
|
store.set({
|
||||||
|
@ -47,13 +47,13 @@ export async function logOutOfInstance(instanceName) {
|
||||||
goto('/settings/instances')
|
goto('/settings/instances')
|
||||||
}
|
}
|
||||||
|
|
||||||
function setStoreVerifyCredentials(instanceName, thisVerifyCredentials) {
|
function setStoreVerifyCredentials (instanceName, thisVerifyCredentials) {
|
||||||
let verifyCredentials = store.get('verifyCredentials') || {}
|
let verifyCredentials = store.get('verifyCredentials') || {}
|
||||||
verifyCredentials[instanceName] = thisVerifyCredentials
|
verifyCredentials[instanceName] = thisVerifyCredentials
|
||||||
store.set({verifyCredentials: verifyCredentials})
|
store.set({verifyCredentials: verifyCredentials})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateVerifyCredentialsForInstance(instanceName) {
|
export async function updateVerifyCredentialsForInstance (instanceName) {
|
||||||
let loggedInInstances = store.get('loggedInInstances')
|
let loggedInInstances = store.get('loggedInInstances')
|
||||||
let accessToken = loggedInInstances[instanceName].access_token
|
let accessToken = loggedInInstances[instanceName].access_token
|
||||||
await cacheFirstUpdateAfter(
|
await cacheFirstUpdateAfter(
|
||||||
|
@ -62,4 +62,4 @@ export async function updateVerifyCredentialsForInstance(instanceName) {
|
||||||
verifyCredentials => database.setInstanceVerifyCredentials(instanceName, verifyCredentials),
|
verifyCredentials => database.setInstanceVerifyCredentials(instanceName, verifyCredentials),
|
||||||
verifyCredentials => setStoreVerifyCredentials(instanceName, verifyCredentials)
|
verifyCredentials => setStoreVerifyCredentials(instanceName, verifyCredentials)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { database } from '../_database/database'
|
||||||
import { getLists } from '../_api/lists'
|
import { getLists } from '../_api/lists'
|
||||||
import { cacheFirstUpdateAfter } from '../_utils/sync'
|
import { cacheFirstUpdateAfter } from '../_utils/sync'
|
||||||
|
|
||||||
export async function updateLists() {
|
export async function updateLists () {
|
||||||
let instanceName = store.get('currentInstance')
|
let instanceName = store.get('currentInstance')
|
||||||
let accessToken = store.get('accessToken')
|
let accessToken = store.get('accessToken')
|
||||||
|
|
||||||
|
@ -17,4 +17,4 @@ export async function updateLists() {
|
||||||
store.set({instanceLists: instanceLists})
|
store.set({instanceLists: instanceLists})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { mergeArrays } from '../_utils/arrays'
|
||||||
|
|
||||||
const FETCH_LIMIT = 20
|
const FETCH_LIMIT = 20
|
||||||
|
|
||||||
async function fetchTimelineItems(instanceName, accessToken, timelineName, lastTimelineItemId, online) {
|
async function fetchTimelineItems (instanceName, accessToken, timelineName, lastTimelineItemId, online) {
|
||||||
mark('fetchTimelineItems')
|
mark('fetchTimelineItems')
|
||||||
let items
|
let items
|
||||||
if (!online) {
|
if (!online) {
|
||||||
|
@ -26,7 +26,7 @@ async function fetchTimelineItems(instanceName, accessToken, timelineName, lastT
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addTimelineItems(instanceName, timelineName, newItems) {
|
async function addTimelineItems (instanceName, timelineName, newItems) {
|
||||||
console.log('addTimelineItems, length:', newItems.length)
|
console.log('addTimelineItems, length:', newItems.length)
|
||||||
mark('addTimelineItems')
|
mark('addTimelineItems')
|
||||||
let newIds = newItems.map(item => item.id)
|
let newIds = newItems.map(item => item.id)
|
||||||
|
@ -36,7 +36,7 @@ async function addTimelineItems(instanceName, timelineName, newItems) {
|
||||||
stop('addTimelineItems')
|
stop('addTimelineItems')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchTimelineItemsAndPossiblyFallBack() {
|
async function fetchTimelineItemsAndPossiblyFallBack () {
|
||||||
mark('fetchTimelineItemsAndPossiblyFallBack')
|
mark('fetchTimelineItemsAndPossiblyFallBack')
|
||||||
let timelineName = store.get('currentTimeline')
|
let timelineName = store.get('currentTimeline')
|
||||||
let instanceName = store.get('currentInstance')
|
let instanceName = store.get('currentInstance')
|
||||||
|
@ -49,7 +49,7 @@ async function fetchTimelineItemsAndPossiblyFallBack() {
|
||||||
stop('fetchTimelineItemsAndPossiblyFallBack')
|
stop('fetchTimelineItemsAndPossiblyFallBack')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initializeTimeline() {
|
export function initializeTimeline () {
|
||||||
mark('initializeTimeline')
|
mark('initializeTimeline')
|
||||||
let instanceName = store.get('currentInstance')
|
let instanceName = store.get('currentInstance')
|
||||||
let timeline = store.get('currentTimeline')
|
let timeline = store.get('currentTimeline')
|
||||||
|
@ -61,7 +61,7 @@ export function initializeTimeline() {
|
||||||
stop('initializeTimeline')
|
stop('initializeTimeline')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setupTimeline() {
|
export async function setupTimeline () {
|
||||||
mark('setupTimeline')
|
mark('setupTimeline')
|
||||||
if (!store.get('timelineItemIds')) {
|
if (!store.get('timelineItemIds')) {
|
||||||
await fetchTimelineItemsAndPossiblyFallBack()
|
await fetchTimelineItemsAndPossiblyFallBack()
|
||||||
|
@ -69,10 +69,10 @@ export async function setupTimeline() {
|
||||||
stop('setupTimeline')
|
stop('setupTimeline')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchTimelineItemsOnScrollToBottom() {
|
export async function fetchTimelineItemsOnScrollToBottom () {
|
||||||
let timelineName = store.get('currentTimeline')
|
let timelineName = store.get('currentTimeline')
|
||||||
let instanceName = store.get('currentInstance')
|
let instanceName = store.get('currentInstance')
|
||||||
store.setForTimeline(instanceName, timelineName, { runningUpdate: true })
|
store.setForTimeline(instanceName, timelineName, { runningUpdate: true })
|
||||||
await fetchTimelineItemsAndPossiblyFallBack()
|
await fetchTimelineItemsAndPossiblyFallBack()
|
||||||
store.setForTimeline(instanceName, timelineName, { runningUpdate: false })
|
store.setForTimeline(instanceName, timelineName, { runningUpdate: false })
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { paramsString } from '../_utils/ajax'
|
||||||
import noop from 'lodash/noop'
|
import noop from 'lodash/noop'
|
||||||
import WebSocketClient from '@gamestdio/websocket'
|
import WebSocketClient from '@gamestdio/websocket'
|
||||||
|
|
||||||
function getStreamName(timeline) {
|
function getStreamName (timeline) {
|
||||||
switch (timeline) {
|
switch (timeline) {
|
||||||
case 'local':
|
case 'local':
|
||||||
return 'public:local'
|
return 'public:local'
|
||||||
|
@ -18,7 +18,7 @@ function getStreamName(timeline) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUrl(streamingApi, accessToken, timeline) {
|
function getUrl (streamingApi, accessToken, timeline) {
|
||||||
let url = `${streamingApi}/api/v1/streaming`
|
let url = `${streamingApi}/api/v1/streaming`
|
||||||
let streamName = getStreamName(timeline)
|
let streamName = getStreamName(timeline)
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ function getUrl(streamingApi, accessToken, timeline) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StatusStream {
|
export class StatusStream {
|
||||||
constructor(streamingApi, accessToken, timeline, opts) {
|
constructor (streamingApi, accessToken, timeline, opts) {
|
||||||
let url = getUrl(streamingApi, accessToken, timeline)
|
let url = getUrl(streamingApi, accessToken, timeline)
|
||||||
|
|
||||||
const ws = new WebSocketClient(url, null, { backoff: 'exponential' })
|
const ws = new WebSocketClient(url, null, { backoff: 'exponential' })
|
||||||
|
@ -52,7 +52,7 @@ export class StatusStream {
|
||||||
this._ws = ws
|
this._ws = ws
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close () {
|
||||||
this._ws.close()
|
this._ws.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { get } from '../_utils/ajax'
|
import { get } from '../_utils/ajax'
|
||||||
import { basename } from './utils'
|
import { basename } from './utils'
|
||||||
|
|
||||||
export function getInstanceInfo(instanceName) {
|
export function getInstanceInfo (instanceName) {
|
||||||
let url = `${basename(instanceName)}/api/v1/instance`
|
let url = `${basename(instanceName)}/api/v1/instance`
|
||||||
return get(url)
|
return get(url)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { get } from '../_utils/ajax'
|
import { get } from '../_utils/ajax'
|
||||||
|
|
||||||
export function getLists(instanceName, accessToken) {
|
export function getLists (instanceName, accessToken) {
|
||||||
let url = `https://${instanceName}/api/v1/lists`
|
let url = `https://${instanceName}/api/v1/lists`
|
||||||
return get(url, {
|
return get(url, {
|
||||||
'Authorization': `Bearer ${accessToken}`
|
'Authorization': `Bearer ${accessToken}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
|
import { post, paramsString } from '../_utils/ajax'
|
||||||
|
import { basename } from './utils'
|
||||||
|
|
||||||
const WEBSITE = 'https://pinafore.social'
|
const WEBSITE = 'https://pinafore.social'
|
||||||
const SCOPES = 'read write follow'
|
const SCOPES = 'read write follow'
|
||||||
const CLIENT_NAME = 'Pinafore'
|
const CLIENT_NAME = 'Pinafore'
|
||||||
import { post, get, paramsString } from '../_utils/ajax'
|
|
||||||
import { basename } from './utils'
|
|
||||||
|
|
||||||
export function registerApplication(instanceName, redirectUri) {
|
export function registerApplication (instanceName, redirectUri) {
|
||||||
const url = `${basename(instanceName)}/api/v1/apps`
|
const url = `${basename(instanceName)}/api/v1/apps`
|
||||||
return post(url, {
|
return post(url, {
|
||||||
client_name: CLIENT_NAME,
|
client_name: CLIENT_NAME,
|
||||||
redirect_uris: redirectUri,
|
redirect_uris: redirectUri,
|
||||||
scopes: SCOPES,
|
scopes: SCOPES,
|
||||||
website: WEBSITE
|
website: WEBSITE
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateAuthLink(instanceName, clientId, redirectUri) {
|
export function generateAuthLink (instanceName, clientId, redirectUri) {
|
||||||
let params = paramsString({
|
let params = paramsString({
|
||||||
'client_id': clientId,
|
'client_id': clientId,
|
||||||
'redirect_uri': redirectUri,
|
'redirect_uri': redirectUri,
|
||||||
|
@ -24,7 +25,7 @@ export function generateAuthLink(instanceName, clientId, redirectUri) {
|
||||||
return `${basename(instanceName)}/oauth/authorize?${params}`
|
return `${basename(instanceName)}/oauth/authorize?${params}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAccessTokenFromAuthCode(instanceName, clientId, clientSecret, code, redirectUri) {
|
export function getAccessTokenFromAuthCode (instanceName, clientId, clientSecret, code, redirectUri) {
|
||||||
let url = `${basename(instanceName)}/oauth/token`
|
let url = `${basename(instanceName)}/oauth/token`
|
||||||
return post(url, {
|
return post(url, {
|
||||||
client_id: clientId,
|
client_id: clientId,
|
||||||
|
@ -33,4 +34,4 @@ export function getAccessTokenFromAuthCode(instanceName, clientId, clientSecret,
|
||||||
grant_type: 'authorization_code',
|
grant_type: 'authorization_code',
|
||||||
code: code
|
code: code
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { get, paramsString } from '../_utils/ajax'
|
import { get, paramsString } from '../_utils/ajax'
|
||||||
|
|
||||||
export function search(instanceName, accessToken, query) {
|
export function search (instanceName, accessToken, query) {
|
||||||
let url = `https://${instanceName}/api/v1/search?` + paramsString({
|
let url = `https://${instanceName}/api/v1/search?` + paramsString({
|
||||||
q: query,
|
q: query,
|
||||||
resolve: true
|
resolve: true
|
||||||
|
@ -9,4 +9,3 @@ export function search(instanceName, accessToken, query) {
|
||||||
'Authorization': `Bearer ${accessToken}`
|
'Authorization': `Bearer ${accessToken}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { get, paramsString } from '../_utils/ajax'
|
import { get, paramsString } from '../_utils/ajax'
|
||||||
import { basename } from './utils'
|
import { basename } from './utils'
|
||||||
|
|
||||||
function getTimelineUrlPath(timeline) {
|
function getTimelineUrlPath (timeline) {
|
||||||
switch (timeline) {
|
switch (timeline) {
|
||||||
case 'local':
|
case 'local':
|
||||||
case 'federated':
|
case 'federated':
|
||||||
|
@ -24,7 +24,7 @@ function getTimelineUrlPath(timeline) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTimeline(instanceName, accessToken, timeline, maxId, since) {
|
export function getTimeline (instanceName, accessToken, timeline, maxId, since) {
|
||||||
let timelineUrlName = getTimelineUrlPath(timeline)
|
let timelineUrlName = getTimelineUrlPath(timeline)
|
||||||
let url = `${basename(instanceName)}/api/v1/${timelineUrlName}`
|
let url = `${basename(instanceName)}/api/v1/${timelineUrlName}`
|
||||||
|
|
||||||
|
@ -67,4 +67,4 @@ export function getTimeline(instanceName, accessToken, timeline, maxId, since) {
|
||||||
return get(url, {
|
return get(url, {
|
||||||
'Authorization': `Bearer ${accessToken}`
|
'Authorization': `Bearer ${accessToken}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
import { get, paramsString } from '../_utils/ajax'
|
import { get, paramsString } from '../_utils/ajax'
|
||||||
import { basename } from './utils'
|
import { basename } from './utils'
|
||||||
|
|
||||||
export function getVerifyCredentials(instanceName, accessToken) {
|
export function getVerifyCredentials (instanceName, accessToken) {
|
||||||
let url = `${basename(instanceName)}/api/v1/accounts/verify_credentials`
|
let url = `${basename(instanceName)}/api/v1/accounts/verify_credentials`
|
||||||
return get(url, {
|
return get(url, {
|
||||||
'Authorization': `Bearer ${accessToken}`
|
'Authorization': `Bearer ${accessToken}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAccount(instanceName, accessToken, accountId) {
|
export function getAccount (instanceName, accessToken, accountId) {
|
||||||
let url = `${basename(instanceName)}/api/v1/accounts/${accountId}`
|
let url = `${basename(instanceName)}/api/v1/accounts/${accountId}`
|
||||||
return get(url, {
|
return get(url, {
|
||||||
'Authorization': `Bearer ${accessToken}`
|
'Authorization': `Bearer ${accessToken}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRelationship(instanceName, accessToken, accountId) {
|
export async function getRelationship (instanceName, accessToken, accountId) {
|
||||||
let url = `${basename(instanceName)}/api/v1/accounts/relationships`
|
let url = `${basename(instanceName)}/api/v1/accounts/relationships`
|
||||||
url += '?' + paramsString({id: accountId})
|
url += '?' + paramsString({id: accountId})
|
||||||
let res = await get(url, {
|
let res = await get(url, {
|
||||||
'Authorization': `Bearer ${accessToken}`
|
'Authorization': `Bearer ${accessToken}`
|
||||||
})
|
})
|
||||||
return res[0]
|
return res[0]
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,14 @@ const isLocalhost = process.browser && process.env.NODE_ENV !== 'production' &&
|
||||||
(document.location.hostname === 'localhost' ||
|
(document.location.hostname === 'localhost' ||
|
||||||
document.location.hostname === '127.0.0.1')
|
document.location.hostname === '127.0.0.1')
|
||||||
|
|
||||||
function targetIsLocalhost(instanceName) {
|
function targetIsLocalhost (instanceName) {
|
||||||
return process.browser && process.env.NODE_ENV !== 'production' &&
|
return process.browser && process.env.NODE_ENV !== 'production' &&
|
||||||
(instanceName === 'localhost:3000' || instanceName === '127.0.0.1:3000')
|
(instanceName === 'localhost:3000' || instanceName === '127.0.0.1:3000')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function basename(instanceName) {
|
export function basename (instanceName) {
|
||||||
if (isLocalhost && targetIsLocalhost(instanceName)) {
|
if (isLocalhost && targetIsLocalhost(instanceName)) {
|
||||||
return `http://${instanceName}`
|
return `http://${instanceName}`
|
||||||
}
|
}
|
||||||
return `https://${instanceName}`
|
return `https://${instanceName}`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import ConfirmationDialog from './ConfirmationDialog.html'
|
import ConfirmationDialog from './ConfirmationDialog.html'
|
||||||
|
|
||||||
export function showConfirmationDialog(options) {
|
export function showConfirmationDialog (options) {
|
||||||
let dialog = new ConfirmationDialog({
|
let dialog = new ConfirmationDialog({
|
||||||
target: document.getElementById('modal-dialog'),
|
target: document.getElementById('modal-dialog'),
|
||||||
data: Object.assign({
|
data: Object.assign({
|
||||||
|
@ -8,4 +8,4 @@ export function showConfirmationDialog(options) {
|
||||||
}, options)
|
}, options)
|
||||||
})
|
})
|
||||||
dialog.show()
|
dialog.show()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import ImageDialog from './ImageDialog.html'
|
import ImageDialog from './ImageDialog.html'
|
||||||
|
|
||||||
export function showImageDialog(poster, src, type, width, height, description) {
|
export function showImageDialog (poster, src, type, width, height, description) {
|
||||||
let imageDialog = new ImageDialog({
|
let imageDialog = new ImageDialog({
|
||||||
target: document.getElementById('modal-dialog'),
|
target: document.getElementById('modal-dialog'),
|
||||||
data: {
|
data: {
|
||||||
|
@ -14,4 +14,4 @@ export function showImageDialog(poster, src, type, width, height, description) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
imageDialog.show()
|
imageDialog.show()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import VideoDialog from './VideoDialog.html'
|
import VideoDialog from './VideoDialog.html'
|
||||||
|
|
||||||
export function showVideoDialog(poster, src, width, height, description) {
|
export function showVideoDialog (poster, src, width, height, description) {
|
||||||
let videoDialog = new VideoDialog({
|
let videoDialog = new VideoDialog({
|
||||||
target: document.getElementById('modal-dialog'),
|
target: document.getElementById('modal-dialog'),
|
||||||
data: {
|
data: {
|
||||||
|
@ -13,4 +13,4 @@ export function showVideoDialog(poster, src, width, height, description) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
videoDialog.show()
|
videoDialog.show()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { RealmStore } from '../../_utils/RealmStore'
|
import { RealmStore } from '../../_utils/RealmStore'
|
||||||
|
|
||||||
class PseudoVirtualListStore extends RealmStore {
|
class PseudoVirtualListStore extends RealmStore {
|
||||||
constructor(state) {
|
constructor (state) {
|
||||||
super(state, /* maxSize */ 10)
|
super(state, /* maxSize */ 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,4 +14,4 @@ if (process.browser && process.env.NODE_NODE !== 'production') {
|
||||||
window.pseudoVirtualListStore = pseudoVirtualListStore
|
window.pseudoVirtualListStore = pseudoVirtualListStore
|
||||||
}
|
}
|
||||||
|
|
||||||
export { pseudoVirtualListStore }
|
export { pseudoVirtualListStore }
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { RealmStore } from '../../_utils/RealmStore'
|
||||||
const VIEWPORT_RENDER_FACTOR = 4
|
const VIEWPORT_RENDER_FACTOR = 4
|
||||||
|
|
||||||
class VirtualListStore extends RealmStore {
|
class VirtualListStore extends RealmStore {
|
||||||
constructor(state) {
|
constructor (state) {
|
||||||
super(state, /* maxSize */ 10)
|
super(state, /* maxSize */ 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,77 +22,76 @@ virtualListStore.computeForRealm('itemHeights', {})
|
||||||
virtualListStore.compute('visibleItems',
|
virtualListStore.compute('visibleItems',
|
||||||
['items', 'scrollTop', 'itemHeights', 'offsetHeight'],
|
['items', 'scrollTop', 'itemHeights', 'offsetHeight'],
|
||||||
(items, scrollTop, itemHeights, offsetHeight) => {
|
(items, scrollTop, itemHeights, offsetHeight) => {
|
||||||
mark('compute visibleItems')
|
mark('compute visibleItems')
|
||||||
if (!items) {
|
if (!items) {
|
||||||
return null
|
return null
|
||||||
}
|
|
||||||
let renderBuffer = VIEWPORT_RENDER_FACTOR * offsetHeight
|
|
||||||
let visibleItems = []
|
|
||||||
let totalOffset = 0
|
|
||||||
let len = items.length
|
|
||||||
let i = -1
|
|
||||||
while (++i < len) {
|
|
||||||
let key = items[i]
|
|
||||||
let height = itemHeights[key] || 0
|
|
||||||
let currentOffset = totalOffset
|
|
||||||
totalOffset += height
|
|
||||||
let isBelowViewport = (currentOffset < scrollTop)
|
|
||||||
if (isBelowViewport) {
|
|
||||||
if (scrollTop - renderBuffer > currentOffset) {
|
|
||||||
continue // below the area we want to render
|
|
||||||
}
|
}
|
||||||
} else {
|
let renderBuffer = VIEWPORT_RENDER_FACTOR * offsetHeight
|
||||||
if (currentOffset > (scrollTop + height + renderBuffer)) {
|
let visibleItems = []
|
||||||
break // above the area we want to render
|
let totalOffset = 0
|
||||||
|
let len = items.length
|
||||||
|
let i = -1
|
||||||
|
while (++i < len) {
|
||||||
|
let key = items[i]
|
||||||
|
let height = itemHeights[key] || 0
|
||||||
|
let currentOffset = totalOffset
|
||||||
|
totalOffset += height
|
||||||
|
let isBelowViewport = (currentOffset < scrollTop)
|
||||||
|
if (isBelowViewport) {
|
||||||
|
if (scrollTop - renderBuffer > currentOffset) {
|
||||||
|
continue // below the area we want to render
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentOffset > (scrollTop + height + renderBuffer)) {
|
||||||
|
break // above the area we want to render
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visibleItems.push({
|
||||||
|
offset: currentOffset,
|
||||||
|
key: key,
|
||||||
|
index: i
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
stop('compute visibleItems')
|
||||||
visibleItems.push({
|
return visibleItems
|
||||||
offset: currentOffset,
|
|
||||||
key: key,
|
|
||||||
index: i
|
|
||||||
})
|
})
|
||||||
}
|
|
||||||
stop('compute visibleItems')
|
|
||||||
return visibleItems
|
|
||||||
})
|
|
||||||
|
|
||||||
virtualListStore.compute('heightWithoutFooter',
|
virtualListStore.compute('heightWithoutFooter',
|
||||||
['items', 'itemHeights'],
|
['items', 'itemHeights'],
|
||||||
(items, itemHeights) => {
|
(items, itemHeights) => {
|
||||||
if (!items) {
|
if (!items) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
let sum = 0
|
let sum = 0
|
||||||
let i = -1
|
let i = -1
|
||||||
let len = items.length
|
let len = items.length
|
||||||
while (++i < len) {
|
while (++i < len) {
|
||||||
sum += itemHeights[items[i]] || 0
|
sum += itemHeights[items[i]] || 0
|
||||||
}
|
}
|
||||||
return sum
|
return sum
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
virtualListStore.compute('height',
|
virtualListStore.compute('height',
|
||||||
['heightWithoutFooter', 'showFooter', 'footerHeight'],
|
['heightWithoutFooter', 'showFooter', 'footerHeight'],
|
||||||
(heightWithoutFooter, showFooter, footerHeight) => {
|
(heightWithoutFooter, showFooter, footerHeight) => {
|
||||||
return showFooter ? (heightWithoutFooter + footerHeight) : heightWithoutFooter
|
return showFooter ? (heightWithoutFooter + footerHeight) : heightWithoutFooter
|
||||||
})
|
})
|
||||||
|
|
||||||
virtualListStore.compute('length', ['items'], (items) => items ? items.length : 0)
|
virtualListStore.compute('length', ['items'], (items) => items ? items.length : 0)
|
||||||
|
|
||||||
virtualListStore.compute('allVisibleItemsHaveHeight',
|
virtualListStore.compute('allVisibleItemsHaveHeight',
|
||||||
['visibleItems', 'itemHeights'],
|
['visibleItems', 'itemHeights'],
|
||||||
(visibleItems, itemHeights) => {
|
(visibleItems, itemHeights) => {
|
||||||
if (!visibleItems) {
|
if (!visibleItems) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for (let visibleItem of visibleItems) {
|
for (let visibleItem of visibleItems) {
|
||||||
if (!itemHeights[visibleItem.key]) {
|
if (!itemHeights[visibleItem.key]) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
if (process.browser && process.env.NODE_ENV !== 'production') {
|
if (process.browser && process.env.NODE_ENV !== 'production') {
|
||||||
window.virtualListStore = virtualListStore
|
window.virtualListStore = virtualListStore
|
||||||
|
@ -100,4 +99,4 @@ if (process.browser && process.env.NODE_ENV !== 'production') {
|
||||||
|
|
||||||
export {
|
export {
|
||||||
virtualListStore
|
virtualListStore
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,18 @@ import { ACCOUNTS_STORE, RELATIONSHIPS_STORE } from './constants'
|
||||||
import { accountsCache, relationshipsCache } from './cache'
|
import { accountsCache, relationshipsCache } from './cache'
|
||||||
import { getGenericEntityWithId, setGenericEntityWithId } from './helpers'
|
import { getGenericEntityWithId, setGenericEntityWithId } from './helpers'
|
||||||
|
|
||||||
export async function getAccount(instanceName, accountId) {
|
export async function getAccount (instanceName, accountId) {
|
||||||
return await getGenericEntityWithId(ACCOUNTS_STORE, accountsCache, instanceName, accountId)
|
return getGenericEntityWithId(ACCOUNTS_STORE, accountsCache, instanceName, accountId)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setAccount(instanceName, account) {
|
export async function setAccount (instanceName, account) {
|
||||||
return await setGenericEntityWithId(ACCOUNTS_STORE, accountsCache, instanceName, account)
|
return setGenericEntityWithId(ACCOUNTS_STORE, accountsCache, instanceName, account)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRelationship(instanceName, accountId) {
|
export async function getRelationship (instanceName, accountId) {
|
||||||
return await getGenericEntityWithId(RELATIONSHIPS_STORE, relationshipsCache, instanceName, accountId)
|
return getGenericEntityWithId(RELATIONSHIPS_STORE, relationshipsCache, instanceName, accountId)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setRelationship(instanceName, relationship) {
|
export async function setRelationship (instanceName, relationship) {
|
||||||
return await setGenericEntityWithId(RELATIONSHIPS_STORE, relationshipsCache, instanceName, relationship)
|
return setGenericEntityWithId(RELATIONSHIPS_STORE, relationshipsCache, instanceName, relationship)
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ if (process.browser && process.env.NODE_ENV !== 'production') {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOrCreateInstanceCache(cache, instanceName) {
|
function getOrCreateInstanceCache (cache, instanceName) {
|
||||||
let cached = cache.caches[instanceName]
|
let cached = cache.caches[instanceName]
|
||||||
if (!cached) {
|
if (!cached) {
|
||||||
cached = cache.caches[instanceName] = new QuickLRU({maxSize: cache.maxSize})
|
cached = cache.caches[instanceName] = new QuickLRU({maxSize: cache.maxSize})
|
||||||
|
@ -39,20 +39,20 @@ function getOrCreateInstanceCache(cache, instanceName) {
|
||||||
return cached
|
return cached
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearCache(cache, instanceName) {
|
export function clearCache (cache, instanceName) {
|
||||||
delete cache.caches[instanceName]
|
delete cache.caches[instanceName]
|
||||||
}
|
}
|
||||||
export function setInCache(cache, instanceName, key, value) {
|
export function setInCache (cache, instanceName, key, value) {
|
||||||
let instanceCache = getOrCreateInstanceCache(cache, instanceName)
|
let instanceCache = getOrCreateInstanceCache(cache, instanceName)
|
||||||
return instanceCache.set(key, value)
|
return instanceCache.set(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getInCache(cache, instanceName, key) {
|
export function getInCache (cache, instanceName, key) {
|
||||||
let instanceCache = getOrCreateInstanceCache(cache, instanceName)
|
let instanceCache = getOrCreateInstanceCache(cache, instanceName)
|
||||||
return instanceCache.get(key)
|
return instanceCache.get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hasInCache(cache, instanceName, key) {
|
export function hasInCache (cache, instanceName, key) {
|
||||||
let instanceCache = getOrCreateInstanceCache(cache, instanceName)
|
let instanceCache = getOrCreateInstanceCache(cache, instanceName)
|
||||||
let res = instanceCache.has(key)
|
let res = instanceCache.has(key)
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
@ -63,4 +63,4 @@ export function hasInCache(cache, instanceName, key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { accountsCache, clearCache, metaCache, statusesCache } from './cache'
|
import { accountsCache, clearCache, metaCache, statusesCache } from './cache'
|
||||||
import { deleteDatabase } from './databaseLifecycle'
|
import { deleteDatabase } from './databaseLifecycle'
|
||||||
|
|
||||||
export async function clearDatabaseForInstance(instanceName) {
|
export async function clearDatabaseForInstance (instanceName) {
|
||||||
clearCache(statusesCache, instanceName)
|
clearCache(statusesCache, instanceName)
|
||||||
clearCache(accountsCache, instanceName)
|
clearCache(accountsCache, instanceName)
|
||||||
clearCache(metaCache, instanceName)
|
clearCache(metaCache, instanceName)
|
||||||
await deleteDatabase(instanceName)
|
await deleteDatabase(instanceName)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,4 +4,4 @@ export const META_STORE = 'meta'
|
||||||
export const ACCOUNTS_STORE = 'accounts'
|
export const ACCOUNTS_STORE = 'accounts'
|
||||||
export const RELATIONSHIPS_STORE = 'relationships'
|
export const RELATIONSHIPS_STORE = 'relationships'
|
||||||
export const NOTIFICATIONS_STORE = 'notifications'
|
export const NOTIFICATIONS_STORE = 'notifications'
|
||||||
export const NOTIFICATION_TIMELINES_STORE = 'notification_timelines'
|
export const NOTIFICATION_TIMELINES_STORE = 'notification_timelines'
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
import * as database from './databaseCore'
|
import * as database from './databaseCore'
|
||||||
|
|
||||||
export { database }
|
export { database }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export * from './accountsAndRelationships'
|
export * from './accountsAndRelationships'
|
||||||
export * from './clear'
|
export * from './clear'
|
||||||
export * from './meta'
|
export * from './meta'
|
||||||
export * from './timelines'
|
export * from './timelines'
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
const openReqs = {}
|
|
||||||
const databaseCache = {}
|
|
||||||
|
|
||||||
const DB_VERSION = 1
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
META_STORE,
|
META_STORE,
|
||||||
STATUS_TIMELINES_STORE,
|
STATUS_TIMELINES_STORE,
|
||||||
|
@ -13,7 +8,12 @@ import {
|
||||||
NOTIFICATION_TIMELINES_STORE
|
NOTIFICATION_TIMELINES_STORE
|
||||||
} from './constants'
|
} from './constants'
|
||||||
|
|
||||||
export function getDatabase(instanceName) {
|
const openReqs = {}
|
||||||
|
const databaseCache = {}
|
||||||
|
|
||||||
|
const DB_VERSION = 1
|
||||||
|
|
||||||
|
export function getDatabase (instanceName) {
|
||||||
if (!instanceName) {
|
if (!instanceName) {
|
||||||
throw new Error('instanceName is undefined in getDatabase()')
|
throw new Error('instanceName is undefined in getDatabase()')
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ export function getDatabase(instanceName) {
|
||||||
console.log('idb blocked')
|
console.log('idb blocked')
|
||||||
}
|
}
|
||||||
req.onupgradeneeded = (e) => {
|
req.onupgradeneeded = (e) => {
|
||||||
let db = req.result;
|
let db = req.result
|
||||||
db.createObjectStore(META_STORE, {keyPath: 'key'})
|
db.createObjectStore(META_STORE, {keyPath: 'key'})
|
||||||
db.createObjectStore(STATUSES_STORE, {keyPath: 'id'})
|
db.createObjectStore(STATUSES_STORE, {keyPath: 'id'})
|
||||||
db.createObjectStore(ACCOUNTS_STORE, {keyPath: 'id'})
|
db.createObjectStore(ACCOUNTS_STORE, {keyPath: 'id'})
|
||||||
|
@ -45,26 +45,26 @@ export function getDatabase(instanceName) {
|
||||||
return databaseCache[instanceName]
|
return databaseCache[instanceName]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function dbPromise(db, storeName, readOnlyOrReadWrite, cb) {
|
export async function dbPromise (db, storeName, readOnlyOrReadWrite, cb) {
|
||||||
return await new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const tx = db.transaction(storeName, readOnlyOrReadWrite)
|
const tx = db.transaction(storeName, readOnlyOrReadWrite)
|
||||||
let store = typeof storeName === 'string' ?
|
let store = typeof storeName === 'string'
|
||||||
tx.objectStore(storeName) :
|
? tx.objectStore(storeName)
|
||||||
storeName.map(name => tx.objectStore(name))
|
: storeName.map(name => tx.objectStore(name))
|
||||||
let res
|
let res
|
||||||
cb(store, (result) => {
|
cb(store, (result) => {
|
||||||
res = result
|
res = result
|
||||||
})
|
})
|
||||||
|
|
||||||
tx.oncomplete = () => resolve(res)
|
tx.oncomplete = () => resolve(res)
|
||||||
tx.onerror = () => reject(tx.error.name + ' ' + tx.error.message)
|
tx.onerror = () => reject(tx.error)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteDatabase(instanceName) {
|
export function deleteDatabase (instanceName) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// close any open requests
|
// close any open requests
|
||||||
let openReq = openReqs[instanceName];
|
let openReq = openReqs[instanceName]
|
||||||
if (openReq && openReq.result) {
|
if (openReq && openReq.result) {
|
||||||
openReq.result.close()
|
openReq.result.close()
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,6 @@ export function deleteDatabase(instanceName) {
|
||||||
delete databaseCache[instanceName]
|
delete databaseCache[instanceName]
|
||||||
let req = indexedDB.deleteDatabase(instanceName)
|
let req = indexedDB.deleteDatabase(instanceName)
|
||||||
req.onsuccess = () => resolve()
|
req.onsuccess = () => resolve()
|
||||||
req.onerror = () => reject(req.error.name + ' ' + req.error.message)
|
req.onerror = () => reject(req.error)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { dbPromise, getDatabase } from './databaseLifecycle'
|
import { dbPromise, getDatabase } from './databaseLifecycle'
|
||||||
import { getInCache, hasInCache, setInCache } from './cache'
|
import { getInCache, hasInCache, setInCache } from './cache'
|
||||||
|
|
||||||
export async function getGenericEntityWithId(store, cache, instanceName, id) {
|
export async function getGenericEntityWithId (store, cache, instanceName, id) {
|
||||||
if (hasInCache(cache, instanceName, id)) {
|
if (hasInCache(cache, instanceName, id)) {
|
||||||
return getInCache(cache, instanceName, id)
|
return getInCache(cache, instanceName, id)
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,10 @@ export async function getGenericEntityWithId(store, cache, instanceName, id) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setGenericEntityWithId(store, cache, instanceName, entity) {
|
export async function setGenericEntityWithId (store, cache, instanceName, entity) {
|
||||||
setInCache(cache, instanceName, entity.id, entity)
|
setInCache(cache, instanceName, entity.id, entity)
|
||||||
const db = await getDatabase(instanceName)
|
const db = await getDatabase(instanceName)
|
||||||
return await dbPromise(db, store, 'readwrite', (store) => {
|
return dbPromise(db, store, 'readwrite', (store) => {
|
||||||
store.put(entity)
|
store.put(entity)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { dbPromise, getDatabase } from './databaseLifecycle'
|
||||||
import { META_STORE } from './constants'
|
import { META_STORE } from './constants'
|
||||||
import { metaCache, hasInCache, getInCache, setInCache } from './cache'
|
import { metaCache, hasInCache, getInCache, setInCache } from './cache'
|
||||||
|
|
||||||
async function getMetaProperty(instanceName, key) {
|
async function getMetaProperty (instanceName, key) {
|
||||||
if (hasInCache(metaCache, instanceName, key)) {
|
if (hasInCache(metaCache, instanceName, key)) {
|
||||||
return getInCache(metaCache, instanceName, key)
|
return getInCache(metaCache, instanceName, key)
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,10 @@ async function getMetaProperty(instanceName, key) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setMetaProperty(instanceName, key, value) {
|
async function setMetaProperty (instanceName, key, value) {
|
||||||
setInCache(metaCache, instanceName, key, value)
|
setInCache(metaCache, instanceName, key, value)
|
||||||
const db = await getDatabase(instanceName)
|
const db = await getDatabase(instanceName)
|
||||||
return await dbPromise(db, META_STORE, 'readwrite', (store) => {
|
return dbPromise(db, META_STORE, 'readwrite', (store) => {
|
||||||
store.put({
|
store.put({
|
||||||
key: key,
|
key: key,
|
||||||
value: value
|
value: value
|
||||||
|
@ -27,26 +27,26 @@ async function setMetaProperty(instanceName, key, value) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getInstanceVerifyCredentials(instanceName) {
|
export async function getInstanceVerifyCredentials (instanceName) {
|
||||||
return await getMetaProperty(instanceName, 'verifyCredentials')
|
return getMetaProperty(instanceName, 'verifyCredentials')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setInstanceVerifyCredentials(instanceName, value) {
|
export async function setInstanceVerifyCredentials (instanceName, value) {
|
||||||
return await setMetaProperty(instanceName, 'verifyCredentials', value)
|
return setMetaProperty(instanceName, 'verifyCredentials', value)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getInstanceInfo(instanceName) {
|
export async function getInstanceInfo (instanceName) {
|
||||||
return await getMetaProperty(instanceName, 'instance')
|
return getMetaProperty(instanceName, 'instance')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setInstanceInfo(instanceName, value) {
|
export async function setInstanceInfo (instanceName, value) {
|
||||||
return await setMetaProperty(instanceName, 'instance', value)
|
return setMetaProperty(instanceName, 'instance', value)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getLists(instanceName) {
|
export async function getLists (instanceName) {
|
||||||
return await getMetaProperty(instanceName, 'lists')
|
return getMetaProperty(instanceName, 'lists')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setLists(instanceName, value) {
|
export async function setLists (instanceName, value) {
|
||||||
return await setMetaProperty(instanceName, 'lists', value)
|
return setMetaProperty(instanceName, 'lists', value)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,17 +7,17 @@ import {
|
||||||
} from './constants'
|
} from './constants'
|
||||||
import { getGenericEntityWithId } from './helpers'
|
import { getGenericEntityWithId } from './helpers'
|
||||||
|
|
||||||
function createKeyRange(timeline, maxId) {
|
function createKeyRange (timeline, maxId) {
|
||||||
let negBigInt = maxId && toReversePaddedBigInt(maxId)
|
let negBigInt = maxId && toReversePaddedBigInt(maxId)
|
||||||
let start = negBigInt ? (timeline + '\u0000' + negBigInt) : (timeline + '\u0000')
|
let start = negBigInt ? (timeline + '\u0000' + negBigInt) : (timeline + '\u0000')
|
||||||
let end = timeline + '\u0000\uffff'
|
let end = timeline + '\u0000\uffff'
|
||||||
return IDBKeyRange.bound(start, end, false, false)
|
return IDBKeyRange.bound(start, end, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getNotificationTimeline(instanceName, timeline, maxId, limit) {
|
async function getNotificationTimeline (instanceName, timeline, maxId, limit) {
|
||||||
let storeNames = [NOTIFICATION_TIMELINES_STORE, NOTIFICATIONS_STORE]
|
let storeNames = [NOTIFICATION_TIMELINES_STORE, NOTIFICATIONS_STORE]
|
||||||
const db = await getDatabase(instanceName)
|
const db = await getDatabase(instanceName)
|
||||||
return await dbPromise(db, storeNames, 'readonly', (stores, callback) => {
|
return dbPromise(db, storeNames, 'readonly', (stores, callback) => {
|
||||||
let [ timelineStore, notificationsStore ] = stores
|
let [ timelineStore, notificationsStore ] = stores
|
||||||
let keyRange = createKeyRange(timeline, maxId)
|
let keyRange = createKeyRange(timeline, maxId)
|
||||||
|
|
||||||
|
@ -34,10 +34,10 @@ async function getNotificationTimeline(instanceName, timeline, maxId, limit) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getStatusTimeline(instanceName, timeline, maxId, limit) {
|
async function getStatusTimeline (instanceName, timeline, maxId, limit) {
|
||||||
let storeNames = [STATUS_TIMELINES_STORE, STATUSES_STORE]
|
let storeNames = [STATUS_TIMELINES_STORE, STATUSES_STORE]
|
||||||
const db = await getDatabase(instanceName)
|
const db = await getDatabase(instanceName)
|
||||||
return await dbPromise(db, storeNames, 'readonly', (stores, callback) => {
|
return dbPromise(db, storeNames, 'readonly', (stores, callback) => {
|
||||||
let [ timelineStore, statusesStore ] = stores
|
let [ timelineStore, statusesStore ] = stores
|
||||||
let keyRange = createKeyRange(timeline, maxId)
|
let keyRange = createKeyRange(timeline, maxId)
|
||||||
|
|
||||||
|
@ -54,18 +54,18 @@ async function getStatusTimeline(instanceName, timeline, maxId, limit) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getTimeline(instanceName, timeline, maxId = null, limit = 20) {
|
export async function getTimeline (instanceName, timeline, maxId = null, limit = 20) {
|
||||||
return timeline === 'notifications' ?
|
return timeline === 'notifications'
|
||||||
await getNotificationTimeline(instanceName, timeline, maxId, limit) :
|
? getNotificationTimeline(instanceName, timeline, maxId, limit)
|
||||||
await getStatusTimeline(instanceName, timeline, maxId, limit)
|
: getStatusTimeline(instanceName, timeline, maxId, limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTimelineId(timeline, id) {
|
function createTimelineId (timeline, id) {
|
||||||
// reverse chronological order, prefixed by timeline
|
// reverse chronological order, prefixed by timeline
|
||||||
return timeline + '\u0000' + toReversePaddedBigInt(id)
|
return timeline + '\u0000' + toReversePaddedBigInt(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function insertTimelineNotifications(instanceName, timeline, notifications) {
|
async function insertTimelineNotifications (instanceName, timeline, notifications) {
|
||||||
let storeNames = [NOTIFICATION_TIMELINES_STORE, NOTIFICATIONS_STORE, ACCOUNTS_STORE]
|
let storeNames = [NOTIFICATION_TIMELINES_STORE, NOTIFICATIONS_STORE, ACCOUNTS_STORE]
|
||||||
for (let notification of notifications) {
|
for (let notification of notifications) {
|
||||||
setInCache(notificationsCache, instanceName, notification.id, notification)
|
setInCache(notificationsCache, instanceName, notification.id, notification)
|
||||||
|
@ -85,7 +85,7 @@ async function insertTimelineNotifications(instanceName, timeline, notifications
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function insertTimelineStatuses(instanceName, timeline, statuses) {
|
async function insertTimelineStatuses (instanceName, timeline, statuses) {
|
||||||
let storeNames = [STATUS_TIMELINES_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
let storeNames = [STATUS_TIMELINES_STORE, STATUSES_STORE, ACCOUNTS_STORE]
|
||||||
for (let status of statuses) {
|
for (let status of statuses) {
|
||||||
setInCache(statusesCache, instanceName, status.id, status)
|
setInCache(statusesCache, instanceName, status.id, status)
|
||||||
|
@ -111,16 +111,16 @@ async function insertTimelineStatuses(instanceName, timeline, statuses) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function insertTimelineItems(instanceName, timeline, timelineItems) {
|
export async function insertTimelineItems (instanceName, timeline, timelineItems) {
|
||||||
return timeline === 'notifications' ?
|
return timeline === 'notifications'
|
||||||
await insertTimelineNotifications(instanceName, timeline, timelineItems) :
|
? insertTimelineNotifications(instanceName, timeline, timelineItems)
|
||||||
await insertTimelineStatuses(instanceName, timeline, timelineItems)
|
: insertTimelineStatuses(instanceName, timeline, timelineItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getStatus(instanceName, statusId) {
|
export async function getStatus (instanceName, statusId) {
|
||||||
return await getGenericEntityWithId(STATUSES_STORE, statusesCache, instanceName, statusId)
|
return getGenericEntityWithId(STATUSES_STORE, statusesCache, instanceName, statusId)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getNotification(instanceName, notificationId) {
|
export async function getNotification (instanceName, notificationId) {
|
||||||
return await getGenericEntityWithId(NOTIFICATIONS_STORE, notificationsCache, instanceName, notificationId)
|
return getGenericEntityWithId(NOTIFICATIONS_STORE, notificationsCache, instanceName, notificationId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,4 +11,4 @@ export function toReversePaddedBigInt (id) {
|
||||||
res += (9 - parseInt(bigInt.charAt(i), 10)).toString(10)
|
res += (9 - parseInt(bigInt.charAt(i), 10)).toString(10)
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
export const DEFAULT_MEDIA_WIDTH = 300
|
export const DEFAULT_MEDIA_WIDTH = 300
|
||||||
export const DEFAULT_MEDIA_HEIGHT = 200
|
export const DEFAULT_MEDIA_HEIGHT = 200
|
||||||
|
|
|
@ -29,4 +29,4 @@ const themes = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
export { themes }
|
export { themes }
|
||||||
|
|
|
@ -4,4 +4,4 @@ const timelines = {
|
||||||
federated: { name: 'federated', label: 'Federated' }
|
federated: { name: 'federated', label: 'Federated' }
|
||||||
}
|
}
|
||||||
|
|
||||||
export { timelines }
|
export { timelines }
|
||||||
|
|
|
@ -2,12 +2,12 @@ import { Store } from 'svelte/store'
|
||||||
|
|
||||||
const LS = process.browser && localStorage
|
const LS = process.browser && localStorage
|
||||||
|
|
||||||
function safeParse(str) {
|
function safeParse (str) {
|
||||||
return !str ? undefined : (str === 'undefined' ? undefined : JSON.parse(str))
|
return !str ? undefined : (str === 'undefined' ? undefined : JSON.parse(str))
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LocalStorageStore extends Store {
|
export class LocalStorageStore extends Store {
|
||||||
constructor(state, keysToWatch) {
|
constructor (state, keysToWatch) {
|
||||||
super(state)
|
super(state)
|
||||||
if (!process.browser) {
|
if (!process.browser) {
|
||||||
return
|
return
|
||||||
|
@ -32,7 +32,7 @@ export class LocalStorageStore extends Store {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save () {
|
||||||
if (!process.browser) {
|
if (!process.browser) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -41,4 +41,4 @@ export class LocalStorageStore extends Store {
|
||||||
})
|
})
|
||||||
this._keysToSave = {}
|
this._keysToSave = {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { instanceComputations } from './instanceComputations'
|
import { instanceComputations } from './instanceComputations'
|
||||||
import { timelineComputations } from './timelineComputations'
|
import { timelineComputations } from './timelineComputations'
|
||||||
|
|
||||||
export function computations(store) {
|
export function computations (store) {
|
||||||
instanceComputations(store)
|
instanceComputations(store)
|
||||||
timelineComputations(store)
|
timelineComputations(store)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export function instanceComputations(store) {
|
export function instanceComputations (store) {
|
||||||
store.compute(
|
store.compute(
|
||||||
'isUserLoggedIn',
|
'isUserLoggedIn',
|
||||||
['currentInstance', 'loggedInInstances'],
|
['currentInstance', 'loggedInInstances'],
|
||||||
|
@ -70,5 +70,4 @@ export function instanceComputations(store) {
|
||||||
return list ? list.title : ''
|
return list ? list.title : ''
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
function timelineMixins(Store) {
|
function timelineMixins (Store) {
|
||||||
Store.prototype.setForTimeline = function (instanceName, timelineName, obj) {
|
Store.prototype.setForTimeline = function (instanceName, timelineName, obj) {
|
||||||
let timelines = this.get('timelines') || {}
|
let timelines = this.get('timelines') || {}
|
||||||
let timelineData = timelines[instanceName] || {}
|
let timelineData = timelines[instanceName] || {}
|
||||||
|
@ -14,6 +14,6 @@ function timelineMixins(Store) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mixins(Store) {
|
export function mixins (Store) {
|
||||||
timelineMixins(Store)
|
timelineMixins(Store)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { updateVerifyCredentialsForInstance } from '../_actions/instances'
|
import { updateVerifyCredentialsForInstance } from '../_actions/instances'
|
||||||
import { updateLists } from '../_actions/lists'
|
import { updateLists } from '../_actions/lists'
|
||||||
|
|
||||||
export function observers(store) {
|
export function observers (store) {
|
||||||
store.observe('currentInstance', (currentInstance) => {
|
store.observe('currentInstance', (currentInstance) => {
|
||||||
if (currentInstance) {
|
if (currentInstance) {
|
||||||
updateVerifyCredentialsForInstance(currentInstance)
|
updateVerifyCredentialsForInstance(currentInstance)
|
||||||
updateLists()
|
updateLists()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,20 +4,20 @@ import { mixins } from './mixins'
|
||||||
import { LocalStorageStore } from './LocalStorageStore'
|
import { LocalStorageStore } from './LocalStorageStore'
|
||||||
|
|
||||||
const KEYS_TO_STORE_IN_LOCAL_STORAGE = new Set([
|
const KEYS_TO_STORE_IN_LOCAL_STORAGE = new Set([
|
||||||
"currentInstance",
|
'currentInstance',
|
||||||
"currentRegisteredInstance",
|
'currentRegisteredInstance',
|
||||||
"currentRegisteredInstanceName",
|
'currentRegisteredInstanceName',
|
||||||
"instanceNameInSearch",
|
'instanceNameInSearch',
|
||||||
"instanceThemes",
|
'instanceThemes',
|
||||||
"loggedInInstances",
|
'loggedInInstances',
|
||||||
"loggedInInstancesInOrder",
|
'loggedInInstancesInOrder',
|
||||||
"autoplayGifs",
|
'autoplayGifs',
|
||||||
"markMediaAsSensitive",
|
'markMediaAsSensitive',
|
||||||
"pinnedPages"
|
'pinnedPages'
|
||||||
])
|
])
|
||||||
|
|
||||||
class PinaforeStore extends LocalStorageStore {
|
class PinaforeStore extends LocalStorageStore {
|
||||||
constructor(state) {
|
constructor (state) {
|
||||||
super(state, KEYS_TO_STORE_IN_LOCAL_STORAGE)
|
super(state, KEYS_TO_STORE_IN_LOCAL_STORAGE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,4 +45,4 @@ if (process.browser && process.env.NODE_ENV !== 'production') {
|
||||||
window.store = store // for debugging
|
window.store = store // for debugging
|
||||||
}
|
}
|
||||||
|
|
||||||
export { store }
|
export { store }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export function timelineComputations(store) {
|
export function timelineComputations (store) {
|
||||||
store.compute('currentTimelineData', ['currentInstance', 'currentTimeline', 'timelines'],
|
store.compute('currentTimelineData', ['currentInstance', 'currentTimeline', 'timelines'],
|
||||||
(currentInstance, currentTimeline, timelines) => {
|
(currentInstance, currentTimeline, timelines) => {
|
||||||
return ((timelines && timelines[currentInstance]) || {})[currentTimeline] || {}
|
return ((timelines && timelines[currentInstance]) || {})[currentTimeline] || {}
|
||||||
|
@ -8,4 +8,4 @@ export function timelineComputations(store) {
|
||||||
store.compute('runningUpdate', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.runningUpdate)
|
store.compute('runningUpdate', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.runningUpdate)
|
||||||
store.compute('initialized', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.initialized)
|
store.compute('initialized', ['currentTimelineData'], (currentTimelineData) => currentTimelineData.initialized)
|
||||||
store.compute('lastTimelineItemId', ['timelineItemIds'], (timelineItemIds) => timelineItemIds && timelineItemIds.length && timelineItemIds[timelineItemIds.length - 1])
|
store.compute('lastTimelineItemId', ['timelineItemIds'], (timelineItemIds) => timelineItemIds && timelineItemIds.length && timelineItemIds[timelineItemIds.length - 1])
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import { getRectFromEntry } from './getRectFromEntry'
|
import { getRectFromEntry } from './getRectFromEntry'
|
||||||
|
|
||||||
class AsyncLayout {
|
class AsyncLayout {
|
||||||
constructor(generateKeyFromNode) {
|
constructor (generateKeyFromNode) {
|
||||||
this._onIntersectionCallbacks = {}
|
this._onIntersectionCallbacks = {}
|
||||||
|
|
||||||
this._intersectionObserver = new IntersectionObserver(entries => {
|
this._intersectionObserver = new IntersectionObserver(entries => {
|
||||||
|
@ -13,7 +13,7 @@ class AsyncLayout {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
observe(key, node, callback) {
|
observe (key, node, callback) {
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ class AsyncLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unobserve(key, node) {
|
unobserve (key, node) {
|
||||||
if (key in this._onIntersectionCallbacks) {
|
if (key in this._onIntersectionCallbacks) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ class AsyncLayout {
|
||||||
delete this._onIntersectionCallbacks[key]
|
delete this._onIntersectionCallbacks[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
disconnect () {
|
||||||
if (this._intersectionObserver) {
|
if (this._intersectionObserver) {
|
||||||
this._intersectionObserver.disconnect()
|
this._intersectionObserver.disconnect()
|
||||||
this._intersectionObserver = null
|
this._intersectionObserver = null
|
||||||
|
@ -47,4 +47,4 @@ class AsyncLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { AsyncLayout }
|
export { AsyncLayout }
|
||||||
|
|
|
@ -6,37 +6,37 @@ import QuickLRU from 'quick-lru'
|
||||||
import { mark, stop } from './marks'
|
import { mark, stop } from './marks'
|
||||||
|
|
||||||
export class RealmStore extends Store {
|
export class RealmStore extends Store {
|
||||||
constructor(init, maxSize) {
|
constructor (init, maxSize) {
|
||||||
super(init)
|
super(init)
|
||||||
this.set({realms: new QuickLRU({maxSize: maxSize})})
|
this.set({realms: new QuickLRU({maxSize: maxSize})})
|
||||||
this._batches = {}
|
this._batches = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentRealm(realm) {
|
setCurrentRealm (realm) {
|
||||||
this.set({currentRealm: realm})
|
this.set({currentRealm: realm})
|
||||||
}
|
}
|
||||||
|
|
||||||
setForRealm(obj) {
|
setForRealm (obj) {
|
||||||
let realmName = this.get('currentRealm')
|
let realmName = this.get('currentRealm')
|
||||||
let realms = this.get('realms')
|
let realms = this.get('realms')
|
||||||
realms.set(realmName, Object.assign(realms.get(realmName) || {}, obj))
|
realms.set(realmName, Object.assign(realms.get(realmName) || {}, obj))
|
||||||
this.set({realms: realms})
|
this.set({realms: realms})
|
||||||
}
|
}
|
||||||
|
|
||||||
computeForRealm(key, defaultValue) {
|
computeForRealm (key, defaultValue) {
|
||||||
this.compute(key,
|
this.compute(key,
|
||||||
['realms', 'currentRealm'],
|
['realms', 'currentRealm'],
|
||||||
(realms, currentRealm) => {
|
(realms, currentRealm) => {
|
||||||
let realmData = realms.get(currentRealm)
|
let realmData = realms.get(currentRealm)
|
||||||
return (realmData && realmData[key]) || defaultValue
|
return (realmData && realmData[key]) || defaultValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update several values at once in a realm, assuming the key points
|
* Update several values at once in a realm, assuming the key points
|
||||||
* to a plain old javascript object.
|
* to a plain old javascript object.
|
||||||
*/
|
*/
|
||||||
batchUpdateForRealm(key, subKey, value) {
|
batchUpdateForRealm (key, subKey, value) {
|
||||||
let realm = this.get('currentRealm')
|
let realm = this.get('currentRealm')
|
||||||
let realmBatches = this._batches[realm]
|
let realmBatches = this._batches[realm]
|
||||||
if (!realmBatches) {
|
if (!realmBatches) {
|
||||||
|
@ -69,4 +69,4 @@ export class RealmStore extends Store {
|
||||||
stop('batchUpdate')
|
stop('batchUpdate')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export async function post(url, body) {
|
export async function post (url, body) {
|
||||||
return await (await fetch(url, {
|
return (await fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
|
@ -9,7 +9,7 @@ export async function post(url, body) {
|
||||||
})).json()
|
})).json()
|
||||||
}
|
}
|
||||||
|
|
||||||
export function paramsString(paramsObject) {
|
export function paramsString (paramsObject) {
|
||||||
let params = new URLSearchParams()
|
let params = new URLSearchParams()
|
||||||
Object.keys(paramsObject).forEach(key => {
|
Object.keys(paramsObject).forEach(key => {
|
||||||
params.set(key, paramsObject[key])
|
params.set(key, paramsObject[key])
|
||||||
|
@ -17,11 +17,11 @@ export function paramsString(paramsObject) {
|
||||||
return params.toString()
|
return params.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function get(url, headers = {}) {
|
export async function get (url, headers = {}) {
|
||||||
return await (await fetch(url, {
|
return (await fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: Object.assign(headers, {
|
headers: Object.assign(headers, {
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json'
|
||||||
})
|
})
|
||||||
})).json()
|
})).json()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Merge two arrays, assuming both input arrays have the same order
|
// Merge two arrays, assuming both input arrays have the same order
|
||||||
// and items are comparable
|
// and items are comparable
|
||||||
export function mergeArrays(leftArray, rightArray) {
|
export function mergeArrays (leftArray, rightArray) {
|
||||||
let leftIndex = 0
|
let leftIndex = 0
|
||||||
let rightIndex = 0
|
let rightIndex = 0
|
||||||
let merged = []
|
let merged = []
|
||||||
|
@ -30,4 +30,4 @@ export function mergeArrays(leftArray, rightArray) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return merged
|
return merged
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,12 @@ export function imgLoad (node, callback) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mouseover(node, callback) {
|
export function mouseover (node, callback) {
|
||||||
function onMouseEnter() {
|
function onMouseEnter () {
|
||||||
callback(true)
|
callback(true) // eslint-disable-line
|
||||||
}
|
}
|
||||||
function onMouseLeave() {
|
function onMouseLeave () {
|
||||||
callback(false)
|
callback(false) // eslint-disable-line
|
||||||
}
|
}
|
||||||
node.addEventListener('mouseenter', onMouseEnter)
|
node.addEventListener('mouseenter', onMouseEnter)
|
||||||
node.addEventListener('mouseleave', onMouseLeave)
|
node.addEventListener('mouseleave', onMouseLeave)
|
||||||
|
@ -33,4 +33,4 @@ export function mouseover(node, callback) {
|
||||||
node.removeEventListener('mouseleave', onMouseLeave)
|
node.removeEventListener('mouseleave', onMouseLeave)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
export const isFullscreen = () => !!(document.fullscreenElement ||
|
export const isFullscreen = () => !!(document.fullscreenElement ||
|
||||||
document.webkitFullscreenElement ||
|
document.webkitFullscreenElement ||
|
||||||
document.mozFullScreenElement);
|
document.mozFullScreenElement)
|
||||||
|
|
||||||
export const attachFullscreenListener = (listener) => {
|
export const attachFullscreenListener = (listener) => {
|
||||||
if ('onfullscreenchange' in document) {
|
if ('onfullscreenchange' in document) {
|
||||||
document.addEventListener('fullscreenchange', listener);
|
document.addEventListener('fullscreenchange', listener)
|
||||||
} else if ('onwebkitfullscreenchange' in document) {
|
} else if ('onwebkitfullscreenchange' in document) {
|
||||||
document.addEventListener('webkitfullscreenchange', listener);
|
document.addEventListener('webkitfullscreenchange', listener)
|
||||||
} else if ('onmozfullscreenchange' in document) {
|
} else if ('onmozfullscreenchange' in document) {
|
||||||
document.addEventListener('mozfullscreenchange', listener);
|
document.addEventListener('mozfullscreenchange', listener)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export const detachFullscreenListener = (listener) => {
|
export const detachFullscreenListener = (listener) => {
|
||||||
if ('onfullscreenchange' in document) {
|
if ('onfullscreenchange' in document) {
|
||||||
document.removeEventListener('fullscreenchange', listener);
|
document.removeEventListener('fullscreenchange', listener)
|
||||||
} else if ('onwebkitfullscreenchange' in document) {
|
} else if ('onwebkitfullscreenchange' in document) {
|
||||||
document.removeEventListener('webkitfullscreenchange', listener);
|
document.removeEventListener('webkitfullscreenchange', listener)
|
||||||
} else if ('onmozfullscreenchange' in document) {
|
} else if ('onmozfullscreenchange' in document) {
|
||||||
document.removeEventListener('mozfullscreenchange', listener);
|
document.removeEventListener('mozfullscreenchange', listener)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
// Get the bounding client rect from an IntersectionObserver entry.
|
// Get the bounding client rect from an IntersectionObserver entry.
|
||||||
// This is to work around a bug in Chrome: https://crbug.com/737228
|
// This is to work around a bug in Chrome: https://crbug.com/737228
|
||||||
|
|
||||||
let hasBoundingRectBug;
|
let hasBoundingRectBug
|
||||||
|
|
||||||
export function getRectFromEntry(entry) {
|
export function getRectFromEntry (entry) {
|
||||||
if (typeof hasBoundingRectBug !== 'boolean') {
|
if (typeof hasBoundingRectBug !== 'boolean') {
|
||||||
const boundingRect = entry.target.getBoundingClientRect();
|
const boundingRect = entry.target.getBoundingClientRect()
|
||||||
const observerRect = entry.boundingClientRect;
|
const observerRect = entry.boundingClientRect
|
||||||
hasBoundingRectBug = boundingRect.height !== observerRect.height ||
|
hasBoundingRectBug = boundingRect.height !== observerRect.height ||
|
||||||
boundingRect.top !== observerRect.top ||
|
boundingRect.top !== observerRect.top ||
|
||||||
boundingRect.width !== observerRect.width ||
|
boundingRect.width !== observerRect.width ||
|
||||||
boundingRect.bottom !== observerRect.bottom ||
|
boundingRect.bottom !== observerRect.bottom ||
|
||||||
boundingRect.left !== observerRect.left ||
|
boundingRect.left !== observerRect.left ||
|
||||||
boundingRect.right !== observerRect.right;
|
boundingRect.right !== observerRect.right
|
||||||
}
|
}
|
||||||
return hasBoundingRectBug ? entry.target.getBoundingClientRect() : entry.boundingClientRect;
|
return hasBoundingRectBug ? entry.target.getBoundingClientRect() : entry.boundingClientRect
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,14 @@ import {
|
||||||
importURLSearchParams,
|
importURLSearchParams,
|
||||||
importIntersectionObserver,
|
importIntersectionObserver,
|
||||||
importRequestIdleCallback,
|
importRequestIdleCallback,
|
||||||
importIndexedDBGetAllShim,
|
importIndexedDBGetAllShim
|
||||||
importDialogPolyfill
|
|
||||||
} from './asyncModules'
|
} from './asyncModules'
|
||||||
|
|
||||||
export function loadPolyfills() {
|
export function loadPolyfills () {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
typeof URLSearchParams === 'undefined' && importURLSearchParams(),
|
typeof URLSearchParams === 'undefined' && importURLSearchParams(),
|
||||||
typeof IntersectionObserver === 'undefined' && importIntersectionObserver(),
|
typeof IntersectionObserver === 'undefined' && importIntersectionObserver(),
|
||||||
typeof requestIdleCallback === 'undefined' && importRequestIdleCallback(),
|
typeof requestIdleCallback === 'undefined' && importRequestIdleCallback(),
|
||||||
!IDBObjectStore.prototype.getAll && importIndexedDBGetAllShim()
|
!IDBObjectStore.prototype.getAll && importIndexedDBGetAllShim()
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,4 +11,4 @@ const stop = enableMarks ? markyStop : noop
|
||||||
export {
|
export {
|
||||||
mark,
|
mark,
|
||||||
stop
|
stop
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,7 @@ const observe = online => {
|
||||||
meta.content = oldTheme || window.__themeColors['default']
|
meta.content = oldTheme || window.__themeColors['default']
|
||||||
} else {
|
} else {
|
||||||
let offlineThemeColor = window.__themeColors.offline
|
let offlineThemeColor = window.__themeColors.offline
|
||||||
if (meta.content !== offlineThemeColor)
|
if (meta.content !== offlineThemeColor) { oldTheme = meta.content }
|
||||||
oldTheme = meta.content
|
|
||||||
meta.content = offlineThemeColor
|
meta.content = offlineThemeColor
|
||||||
notifyOffline()
|
notifyOffline()
|
||||||
}
|
}
|
||||||
|
@ -30,5 +29,5 @@ if (!navigator.onLine) {
|
||||||
observe(false)
|
observe(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('offline', () => observe(false));
|
window.addEventListener('offline', () => observe(false))
|
||||||
window.addEventListener('online', () => observe(true));
|
window.addEventListener('online', () => observe(true))
|
||||||
|
|
|
@ -7,7 +7,7 @@ import Queue from 'tiny-queue'
|
||||||
const taskQueue = new Queue()
|
const taskQueue = new Queue()
|
||||||
let runningRequestIdleCallback = false
|
let runningRequestIdleCallback = false
|
||||||
|
|
||||||
function runTasks(deadline) {
|
function runTasks (deadline) {
|
||||||
while (taskQueue.length && deadline.timeRemaining() > 0) {
|
while (taskQueue.length && deadline.timeRemaining() > 0) {
|
||||||
taskQueue.shift()()
|
taskQueue.shift()()
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,10 @@ function runTasks(deadline) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function scheduleIdleTask(task) {
|
export function scheduleIdleTask (task) {
|
||||||
taskQueue.push(task)
|
taskQueue.push(task)
|
||||||
if (!runningRequestIdleCallback) {
|
if (!runningRequestIdleCallback) {
|
||||||
runningRequestIdleCallback = true
|
runningRequestIdleCallback = true
|
||||||
requestIdleCallback(runTasks)
|
requestIdleCallback(runTasks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import { toast } from './toast'
|
import { toast } from './toast'
|
||||||
|
|
||||||
function onUpdateFound(registration) {
|
function onUpdateFound (registration) {
|
||||||
const newWorker = registration.installing
|
const newWorker = registration.installing
|
||||||
|
|
||||||
newWorker.addEventListener('statechange', async () => {
|
newWorker.addEventListener('statechange', async () => {
|
||||||
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
|
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
|
||||||
toast.say('Update available. Refresh to update.')
|
toast.say('Update available. Refresh to update.')
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!location.origin.match('localhost') && 'serviceWorker' in navigator) {
|
if (!location.origin.match('localhost') && 'serviceWorker' in navigator) {
|
||||||
navigator.serviceWorker.register('/service-worker.js').then(registration => {
|
navigator.serviceWorker.register('/service-worker.js').then(registration => {
|
||||||
registration.addEventListener('updatefound', () => onUpdateFound(registration))
|
registration.addEventListener('updatefound', () => onUpdateFound(registration))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export function replaceAll(string, replacee, replacement) {
|
export function replaceAll (string, replacee, replacement) {
|
||||||
if (!string.length || !replacee.length || !replacement.length) {
|
if (!string.length || !replacee.length || !replacement.length) {
|
||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
|
@ -13,4 +13,4 @@ export function replaceAll(string, replacee, replacement) {
|
||||||
pos = idx + replacement.length
|
pos = idx + replacement.length
|
||||||
}
|
}
|
||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Hit both the cache and the network, setting state for the cached version first,
|
// Hit both the cache and the network, setting state for the cached version first,
|
||||||
// then the network version (as it's assumed to be fresher). Also update the db afterwards.
|
// then the network version (as it's assumed to be fresher). Also update the db afterwards.
|
||||||
export async function cacheFirstUpdateAfter(networkFetcher, dbFetcher, dbUpdater, stateSetter) {
|
export async function cacheFirstUpdateAfter (networkFetcher, dbFetcher, dbUpdater, stateSetter) {
|
||||||
let networkPromise = networkFetcher() // kick off network request immediately
|
let networkPromise = networkFetcher() // kick off network request immediately
|
||||||
let dbResponse
|
let dbResponse
|
||||||
try {
|
try {
|
||||||
|
@ -15,4 +15,4 @@ export async function cacheFirstUpdateAfter(networkFetcher, dbFetcher, dbUpdater
|
||||||
await fetchAndUpdatePromise
|
await fetchAndUpdatePromise
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { loadCSS } from 'fg-loadcss';
|
import { loadCSS } from 'fg-loadcss'
|
||||||
|
|
||||||
let meta = process.browser && document.querySelector('meta[name="theme-color"]')
|
let meta = process.browser && document.querySelector('meta[name="theme-color"]')
|
||||||
|
|
||||||
export function switchToTheme(themeName) {
|
export function switchToTheme (themeName) {
|
||||||
let clazzList = document.body.classList
|
let clazzList = document.body.classList
|
||||||
for (let i = 0; i < clazzList.length; i++) {
|
for (let i = 0; i < clazzList.length; i++) {
|
||||||
let clazz = clazzList.item(i)
|
let clazz = clazzList.item(i)
|
||||||
|
@ -16,4 +16,4 @@ export function switchToTheme(themeName) {
|
||||||
clazzList.add(`theme-${themeName}`)
|
clazzList.add(`theme-${themeName}`)
|
||||||
loadCSS(`/theme-${themeName}.css`)
|
loadCSS(`/theme-${themeName}.css`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,4 +15,4 @@ if (process.browser) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { toast }
|
export { toast }
|
||||||
|
|
29
server.js
29
server.js
|
@ -1,24 +1,23 @@
|
||||||
const fs = require('fs');
|
const app = require('express')()
|
||||||
const app = require('express')();
|
const compression = require('compression')
|
||||||
const compression = require('compression');
|
const sapper = require('sapper')
|
||||||
const sapper = require('sapper');
|
const serveStatic = require('serve-static')
|
||||||
const static = require('serve-static');
|
|
||||||
|
|
||||||
const { PORT = 4002 } = process.env;
|
const { PORT = 4002 } = process.env
|
||||||
|
|
||||||
// this allows us to do e.g. `fetch('/_api/blog')` on the server
|
// this allows us to do e.g. `fetch('/_api/blog')` on the server
|
||||||
const fetch = require('node-fetch');
|
const fetch = require('node-fetch')
|
||||||
global.fetch = (url, opts) => {
|
global.fetch = (url, opts) => {
|
||||||
if (url[0] === '/') url = `http://localhost:${PORT}${url}`;
|
if (url[0] === '/') url = `http://localhost:${PORT}${url}`
|
||||||
return fetch(url, opts);
|
return fetch(url, opts)
|
||||||
};
|
}
|
||||||
|
|
||||||
app.use(compression({ threshold: 0 }));
|
app.use(compression({ threshold: 0 }))
|
||||||
|
|
||||||
app.use(static('assets'));
|
app.use(serveStatic('assets'))
|
||||||
|
|
||||||
app.use(sapper());
|
app.use(sapper())
|
||||||
|
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
console.log(`listening on port ${PORT}`);
|
console.log(`listening on port ${PORT}`)
|
||||||
});
|
})
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
|
/* global __routes__ */
|
||||||
|
|
||||||
import { init } from 'sapper/runtime.js'
|
import { init } from 'sapper/runtime.js'
|
||||||
import { offlineNotifiction } from '../routes/_utils/offlineNotification'
|
import { offlineNotifiction } from '../routes/_utils/offlineNotification'
|
||||||
import { serviceWorkerClient } from '../routes/_utils/serviceWorkerClient'
|
import { serviceWorkerClient } from '../routes/_utils/serviceWorkerClient'
|
||||||
import { loadPolyfills } from '../routes/_utils/loadPolyfills'
|
import { loadPolyfills } from '../routes/_utils/loadPolyfills'
|
||||||
|
|
||||||
|
console.log(offlineNotifiction, serviceWorkerClient)
|
||||||
|
|
||||||
loadPolyfills().then(() => {
|
loadPolyfills().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__)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
|
/* global self, caches, __shell__, __assets__, __routes__ */
|
||||||
|
|
||||||
const timestamp = '__timestamp__'
|
const timestamp = '__timestamp__'
|
||||||
const ASSETS = `cache${timestamp}`
|
const ASSETS = `cache${timestamp}`
|
||||||
|
|
||||||
// `shell` is an array of all the files generated by webpack,
|
// `shell` is an array of all the files generated by webpack,
|
||||||
// `assets` is an array of everything in the `assets` directory
|
// `assets` is an array of everything in the `assets` directory
|
||||||
const to_cache = __shell__.concat(__assets__)
|
const toCache = __shell__.concat(__assets__)
|
||||||
const cached = new Set(to_cache)
|
const cached = new Set(toCache)
|
||||||
|
|
||||||
// `routes` is an array of `{ pattern: RegExp }` objects that
|
// `routes` is an array of `{ pattern: RegExp }` objects that
|
||||||
// match the pages in your app
|
// match the pages in your app
|
||||||
|
@ -14,7 +16,7 @@ self.addEventListener('install', event => {
|
||||||
event.waitUntil(
|
event.waitUntil(
|
||||||
caches
|
caches
|
||||||
.open(ASSETS)
|
.open(ASSETS)
|
||||||
.then(cache => cache.addAll(to_cache))
|
.then(cache => cache.addAll(toCache))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
self.skipWaiting()
|
self.skipWaiting()
|
||||||
})
|
})
|
||||||
|
@ -46,7 +48,7 @@ self.addEventListener('fetch', event => {
|
||||||
|
|
||||||
// don't try to handle e.g. data: URIs
|
// don't try to handle e.g. data: URIs
|
||||||
if (!url.protocol.startsWith('http')) {
|
if (!url.protocol.startsWith('http')) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// always serve assets and webpack-generated files from cache
|
// always serve assets and webpack-generated files from cache
|
||||||
|
|
|
@ -4,70 +4,70 @@ const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
|
||||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
|
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
|
||||||
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin')
|
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin')
|
||||||
|
|
||||||
const isDev = config.dev;
|
const isDev = config.dev
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: config.client.entry(),
|
entry: config.client.entry(),
|
||||||
output: config.client.output(),
|
output: config.client.output(),
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.js', '.html']
|
extensions: ['.js', '.html']
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.html$/,
|
test: /\.html$/,
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
use: {
|
use: {
|
||||||
loader: 'svelte-loader',
|
loader: 'svelte-loader',
|
||||||
options: {
|
options: {
|
||||||
hydratable: true,
|
hydratable: true,
|
||||||
emitCss: !isDev,
|
emitCss: !isDev,
|
||||||
cascade: false,
|
cascade: false,
|
||||||
store: true
|
store: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isDev && {
|
isDev && {
|
||||||
test: /\.css$/,
|
test: /\.css$/,
|
||||||
use: [
|
|
||||||
{ loader: 'style-loader' },
|
|
||||||
{ loader: 'css-loader' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
!isDev && {
|
|
||||||
test: /\.css$/,
|
|
||||||
/* disable while https://github.com/sveltejs/sapper/issues/79 is open */
|
|
||||||
/*use: ExtractTextPlugin.extract({
|
|
||||||
fallback: 'style-loader',
|
|
||||||
use: [{ loader: 'css-loader', options: { sourceMap:isDev } }]
|
|
||||||
}) */
|
|
||||||
use: [
|
use: [
|
||||||
{ loader: 'style-loader' },
|
{ loader: 'style-loader' },
|
||||||
{ loader: 'css-loader' }
|
{ loader: 'css-loader' }
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
].filter(Boolean)
|
!isDev && {
|
||||||
},
|
test: /\.css$/,
|
||||||
node: {
|
/* disable while https://github.com/sveltejs/sapper/issues/79 is open */
|
||||||
setImmediate: false
|
/* use: ExtractTextPlugin.extract({
|
||||||
|
fallback: 'style-loader',
|
||||||
|
use: [{ loader: 'css-loader', options: { sourceMap:isDev } }]
|
||||||
|
}) */
|
||||||
|
use: [
|
||||||
|
{ loader: 'style-loader' },
|
||||||
|
{ loader: 'css-loader' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
].filter(Boolean)
|
||||||
},
|
},
|
||||||
plugins: [
|
node: {
|
||||||
new webpack.optimize.CommonsChunkPlugin({
|
setImmediate: false
|
||||||
minChunks: 2,
|
},
|
||||||
async: true,
|
plugins: [
|
||||||
children: true
|
new webpack.optimize.CommonsChunkPlugin({
|
||||||
})
|
minChunks: 2,
|
||||||
].concat(isDev ? [
|
async: true,
|
||||||
new webpack.HotModuleReplacementPlugin()
|
children: true
|
||||||
] : [
|
})
|
||||||
|
].concat(isDev ? [
|
||||||
|
new webpack.HotModuleReplacementPlugin()
|
||||||
|
] : [
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
'process.browser': true,
|
'process.browser': true,
|
||||||
'process.env.NODE_ENV': '"production"'
|
'process.env.NODE_ENV': '"production"'
|
||||||
}),
|
}),
|
||||||
/* disable while https://github.com/sveltejs/sapper/issues/79 is open */
|
/* disable while https://github.com/sveltejs/sapper/issues/79 is open */
|
||||||
//new ExtractTextPlugin('main.css'),
|
// new ExtractTextPlugin('main.css'),
|
||||||
new LodashModuleReplacementPlugin(),
|
new LodashModuleReplacementPlugin(),
|
||||||
new webpack.optimize.ModuleConcatenationPlugin(),
|
new webpack.optimize.ModuleConcatenationPlugin(),
|
||||||
new UglifyJSPlugin({
|
new UglifyJSPlugin({
|
||||||
parallel: true,
|
parallel: true,
|
||||||
uglifyOptions: {
|
uglifyOptions: {
|
||||||
|
@ -82,11 +82,11 @@ module.exports = {
|
||||||
generateStatsFile: true,
|
generateStatsFile: true,
|
||||||
statsOptions: {
|
statsOptions: {
|
||||||
// allows usage with http://chrisbateman.github.io/webpack-visualizer/
|
// allows usage with http://chrisbateman.github.io/webpack-visualizer/
|
||||||
chunkModules: true,
|
chunkModules: true
|
||||||
},
|
},
|
||||||
openAnalyzer: false,
|
openAnalyzer: false,
|
||||||
logLevel: 'silent', // do not bother Webpacker, who runs with --json and parses stdout
|
logLevel: 'silent' // do not bother Webpacker, who runs with --json and parses stdout
|
||||||
}),
|
})
|
||||||
]).filter(Boolean),
|
]).filter(Boolean),
|
||||||
devtool: isDev && 'inline-source-map'
|
devtool: isDev && 'inline-source-map'
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,30 +1,27 @@
|
||||||
const config = require('sapper/webpack/config.js');
|
const config = require('sapper/webpack/config.js')
|
||||||
const webpack = require('webpack');
|
|
||||||
const ExtractTextPlugin = require("extract-text-webpack-plugin");
|
|
||||||
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: config.server.entry(),
|
entry: config.server.entry(),
|
||||||
output: config.server.output(),
|
output: config.server.output(),
|
||||||
target: 'node',
|
target: 'node',
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.js', '.html']
|
extensions: ['.js', '.html']
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.html$/,
|
test: /\.html$/,
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
use: {
|
use: {
|
||||||
loader: 'svelte-loader',
|
loader: 'svelte-loader',
|
||||||
options: {
|
options: {
|
||||||
css: false,
|
css: false,
|
||||||
cascade: false,
|
cascade: false,
|
||||||
store: true,
|
store: true,
|
||||||
generate: 'ssr'
|
generate: 'ssr'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
Loading…
Reference in New Issue