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