fix: detect private browsing and safari blocked cookies (#733)
* WIP: detect private browsing and safari blocked cookies * just check for indexeddb * just check for indexeddb * change warning text * change text * change text again * change text again fixes #444
This commit is contained in:
		
							parent
							
								
									c0f857336a
								
							
						
					
					
						commit
						0e524f3e9a
					
				
					 9 changed files with 111 additions and 12 deletions
				
			
		| 
						 | 
				
			
			@ -3,7 +3,17 @@
 | 
			
		|||
// the build process and write it to inline-script-checksum.json.
 | 
			
		||||
window.__themeColors = process.env.THEME_COLORS
 | 
			
		||||
 | 
			
		||||
if (localStorage.store_currentInstance && localStorage.store_instanceThemes) {
 | 
			
		||||
const hasLocalStorage = (() => {
 | 
			
		||||
  try {
 | 
			
		||||
    // iOS safari throws here if cookies are disabled
 | 
			
		||||
    let unused = localStorage.length // eslint-disable-line
 | 
			
		||||
    return true
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
})()
 | 
			
		||||
 | 
			
		||||
if (hasLocalStorage && localStorage.store_currentInstance && localStorage.store_instanceThemes) {
 | 
			
		||||
  let safeParse = (str) => str === 'undefined' ? undefined : JSON.parse(str)
 | 
			
		||||
  let theme = safeParse(localStorage.store_instanceThemes)[safeParse(localStorage.store_currentInstance)]
 | 
			
		||||
  if (theme && theme !== 'default') {
 | 
			
		||||
| 
						 | 
				
			
			@ -18,14 +28,14 @@ if (localStorage.store_currentInstance && localStorage.store_instanceThemes) {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (!localStorage.store_currentInstance) {
 | 
			
		||||
if (!hasLocalStorage || !localStorage.store_currentInstance) {
 | 
			
		||||
  // if not logged in, show all these 'hidden-from-ssr' elements
 | 
			
		||||
  let style = document.createElement('style')
 | 
			
		||||
  style.textContent = '.hidden-from-ssr { opacity: 1 !important; }'
 | 
			
		||||
  document.head.appendChild(style)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (localStorage.store_disableCustomScrollbars === 'true') {
 | 
			
		||||
if (hasLocalStorage && localStorage.store_disableCustomScrollbars === 'true') {
 | 
			
		||||
  // if user has disabled custom scrollbars, remove this style
 | 
			
		||||
  let theScrollbarStyle = document.getElementById('theScrollbarStyle')
 | 
			
		||||
  theScrollbarStyle.setAttribute('media', 'only x') // disables the style
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										5
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -6003,6 +6003,11 @@
 | 
			
		|||
        "json5": "^0.5.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "localstorage-memory": {
 | 
			
		||||
      "version": "1.0.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/localstorage-memory/-/localstorage-memory-1.0.3.tgz",
 | 
			
		||||
      "integrity": "sha512-t9P8WB6DcVttbw/W4PIE8HOqum8Qlvx5SjR6oInwR9Uia0EEmyUeBh7S+weKByW+l/f45Bj4L/dgZikGFDM6ng=="
 | 
			
		||||
    },
 | 
			
		||||
    "locate-path": {
 | 
			
		||||
      "version": "2.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -72,6 +72,7 @@
 | 
			
		|||
    "idb-keyval": "^3.1.0",
 | 
			
		||||
    "indexeddb-getall-shim": "^1.3.5",
 | 
			
		||||
    "intersection-observer": "^0.5.1",
 | 
			
		||||
    "localstorage-memory": "^1.0.3",
 | 
			
		||||
    "lodash-es": "^4.17.11",
 | 
			
		||||
    "lodash-webpack-plugin": "^0.11.5",
 | 
			
		||||
    "mini-css-extract-plugin": "^0.4.5",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,10 +3,18 @@
 | 
			
		|||
 | 
			
		||||
  <form class="add-new-instance" on:submit='onSubmit(event)' aria-labelledby="add-an-instance-h1">
 | 
			
		||||
 | 
			
		||||
    {#if !hasIndexedDB}
 | 
			
		||||
      <div class="form-error form-error-user-error" role="alert">
 | 
			
		||||
        Your browser doesn't support IndexedDB, possibly because it's in private browsing mode
 | 
			
		||||
        or blocking cookies. Pinafore stores all data locally, and requires IndexedDB to work
 | 
			
		||||
        correctly.
 | 
			
		||||
      </div>
 | 
			
		||||
    {/if}
 | 
			
		||||
 | 
			
		||||
    {#if $logInToInstanceError && $logInToInstanceErrorForText === $instanceNameInSearch}
 | 
			
		||||
    <div class="form-error form-error-user-error" role="alert">
 | 
			
		||||
      Error: {$logInToInstanceError}
 | 
			
		||||
    </div>
 | 
			
		||||
      <div class="form-error form-error-user-error" role="alert">
 | 
			
		||||
        Error: {$logInToInstanceError}
 | 
			
		||||
      </div>
 | 
			
		||||
    {/if}
 | 
			
		||||
 | 
			
		||||
    <noscript>
 | 
			
		||||
| 
						 | 
				
			
			@ -69,19 +77,25 @@
 | 
			
		|||
  import { store } from '../../../_store/store'
 | 
			
		||||
  import { logInToInstance, handleOauthCode } from '../../../_actions/addInstance'
 | 
			
		||||
  import ExternalLink from '../../../_components/ExternalLink.html'
 | 
			
		||||
  import { testHasIndexedDB } from '../../../_utils/testStorage'
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    async oncreate () {
 | 
			
		||||
      let codeMatch = location.search.match(/code=([^&]+)/)
 | 
			
		||||
      if (codeMatch) {
 | 
			
		||||
        handleOauthCode(codeMatch[1])
 | 
			
		||||
        return handleOauthCode(codeMatch[1])
 | 
			
		||||
      }
 | 
			
		||||
      let hasIndexedDB = await testHasIndexedDB()
 | 
			
		||||
      this.set({ hasIndexedDB })
 | 
			
		||||
    },
 | 
			
		||||
    components: {
 | 
			
		||||
      SettingsLayout,
 | 
			
		||||
      ExternalLink
 | 
			
		||||
    },
 | 
			
		||||
    store: () => store,
 | 
			
		||||
    data: () => ({
 | 
			
		||||
      hasIndexedDB: true
 | 
			
		||||
    }),
 | 
			
		||||
    methods: {
 | 
			
		||||
      onSubmit (event) {
 | 
			
		||||
        event.preventDefault()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,11 @@
 | 
			
		|||
import { Store } from 'svelte/store'
 | 
			
		||||
import { safeLocalStorage as LS } from '../_utils/safeLocalStorage'
 | 
			
		||||
 | 
			
		||||
let lifecycle
 | 
			
		||||
if (process.browser) {
 | 
			
		||||
  lifecycle = require('page-lifecycle/dist/lifecycle.mjs').default
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const LS = process.browser && localStorage
 | 
			
		||||
 | 
			
		||||
function safeParse (str) {
 | 
			
		||||
  return !str ? undefined : (str === 'undefined' ? undefined : JSON.parse(str))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										6
									
								
								routes/_utils/safeLocalStorage.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								routes/_utils/safeLocalStorage.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
import localStorageMemory from 'localstorage-memory'
 | 
			
		||||
import { testHasLocalStorage } from './testStorage'
 | 
			
		||||
 | 
			
		||||
const safeLocalStorage = testHasLocalStorage() ? localStorage : localStorageMemory
 | 
			
		||||
 | 
			
		||||
export { safeLocalStorage }
 | 
			
		||||
							
								
								
									
										42
									
								
								routes/_utils/testStorage.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								routes/_utils/testStorage.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
// LocalStorage and IDB may be disabled in private mode, when "blocking cookies" in Safari,
 | 
			
		||||
// or other cases
 | 
			
		||||
 | 
			
		||||
import { thunk } from './thunk'
 | 
			
		||||
 | 
			
		||||
const testKey = '__test__'
 | 
			
		||||
 | 
			
		||||
export const testHasLocalStorage = thunk(() => {
 | 
			
		||||
  try {
 | 
			
		||||
    localStorage.setItem(testKey, testKey)
 | 
			
		||||
    if (!localStorage.length || localStorage.getItem(testKey) !== testKey) {
 | 
			
		||||
      return false
 | 
			
		||||
    }
 | 
			
		||||
    localStorage.removeItem(testKey)
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
  return true
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const testHasIndexedDB = thunk(async () => {
 | 
			
		||||
  if (typeof indexedDB === 'undefined') {
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    let idbFailed = await new Promise(resolve => {
 | 
			
		||||
      let db = indexedDB.open(testKey)
 | 
			
		||||
      db.onerror = () => resolve(true)
 | 
			
		||||
      db.onsuccess = () => {
 | 
			
		||||
        indexedDB.deleteDatabase(testKey)
 | 
			
		||||
        resolve(false)
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    if (idbFailed) {
 | 
			
		||||
      return false
 | 
			
		||||
    }
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
  return true
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										11
									
								
								routes/_utils/thunk.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								routes/_utils/thunk.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
export function thunk (func) {
 | 
			
		||||
  let cached
 | 
			
		||||
  let runOnce
 | 
			
		||||
  return () => {
 | 
			
		||||
    if (!runOnce) {
 | 
			
		||||
      cached = func()
 | 
			
		||||
      runOnce = true
 | 
			
		||||
    }
 | 
			
		||||
    return cached
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +52,17 @@ html{scrollbar-face-color:var(--scrollbar-face-color);scrollbar-track-color:var(
 | 
			
		|||
// the build process and write it to inline-script-checksum.json.
 | 
			
		||||
window.__themeColors = {"default":"royalblue","scarlet":"#e04e41","seafoam":"#177380","hotpants":"hotpink","oaken":"saddlebrown","majesty":"blueviolet","gecko":"#4ab92f","ozark":"#5263af","cobalt":"#08439b","sorcery":"#ae91e8","punk":"#e04e41","riot":"hotpink","hacker":"#4ab92f","pitchblack":"#000"}
 | 
			
		||||
 | 
			
		||||
if (localStorage.store_currentInstance && localStorage.store_instanceThemes) {
 | 
			
		||||
const hasLocalStorage = (() => {
 | 
			
		||||
  try {
 | 
			
		||||
    // iOS safari throws here if cookies are disabled
 | 
			
		||||
    let unused = localStorage.length // eslint-disable-line
 | 
			
		||||
    return true
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
})()
 | 
			
		||||
 | 
			
		||||
if (hasLocalStorage && localStorage.store_currentInstance && localStorage.store_instanceThemes) {
 | 
			
		||||
  let safeParse = (str) => str === 'undefined' ? undefined : JSON.parse(str)
 | 
			
		||||
  let theme = safeParse(localStorage.store_instanceThemes)[safeParse(localStorage.store_currentInstance)]
 | 
			
		||||
  if (theme && theme !== 'default') {
 | 
			
		||||
| 
						 | 
				
			
			@ -67,14 +77,14 @@ if (localStorage.store_currentInstance && localStorage.store_instanceThemes) {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (!localStorage.store_currentInstance) {
 | 
			
		||||
if (!hasLocalStorage || !localStorage.store_currentInstance) {
 | 
			
		||||
  // if not logged in, show all these 'hidden-from-ssr' elements
 | 
			
		||||
  let style = document.createElement('style')
 | 
			
		||||
  style.textContent = '.hidden-from-ssr { opacity: 1 !important; }'
 | 
			
		||||
  document.head.appendChild(style)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (localStorage.store_disableCustomScrollbars === 'true') {
 | 
			
		||||
if (hasLocalStorage && localStorage.store_disableCustomScrollbars === 'true') {
 | 
			
		||||
  // if user has disabled custom scrollbars, remove this style
 | 
			
		||||
  let theScrollbarStyle = document.getElementById('theScrollbarStyle')
 | 
			
		||||
  theScrollbarStyle.setAttribute('media', 'only x') // disables the style
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue