Skip to content

Commit

Permalink
Don't use localStorage when the browser doesn't support it (#971)
Browse files Browse the repository at this point in the history
* Wrap localStorage with support checks

* Add changelog
  • Loading branch information
ukutaht authored Apr 28, 2021
1 parent 6319e7f commit 255b4b2
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.

### Fixed
- Fix weekly report time range plausible/analytics#951
- Make sure embedded dashboards can run when user has blocked third-party cookies plausible/analytics#971

### Removed
- Removes AppSignal monitoring package
Expand Down
7 changes: 4 additions & 3 deletions assets/js/dashboard/query.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'
import { Link, withRouter } from 'react-router-dom'
import {formatDay, formatMonthYYYY, nowForSite, parseUTCDate} from './date'
import * as storage from './storage'

const PERIODS = ['realtime', 'day', 'month', '7d', '30d', '6mo', '12mo', 'custom']

Expand All @@ -10,9 +11,9 @@ export function parseQuery(querystring, site) {
const periodKey = `period__${ site.domain}`

if (PERIODS.includes(period)) {
if (period !== 'custom' && period !== 'realtime') window.localStorage[periodKey] = period
} else if (window.localStorage[periodKey]) {
period = window.localStorage[periodKey]
if (period !== 'custom' && period !== 'realtime') storage.setItem(periodKey, period)
} else if (storage.getItem(periodKey)) {
period = storage.getItem(periodKey)
} else {
period = '30d'
}
Expand Down
5 changes: 3 additions & 2 deletions assets/js/dashboard/stats/conversions/prop-breakdown.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { Link } from 'react-router-dom'

import * as storage from '../../storage'
import Bar from '../bar'
import numberFormatter from '../../number-formatter'
import * as api from '../../api'
Expand All @@ -10,7 +11,7 @@ export default class PropertyBreakdown extends React.Component {
super(props)
let propKey = props.goal.prop_names[0]
this.storageKey = 'goalPropTab__' + props.site.domain + props.goal.name
const storedKey = window.localStorage[this.storageKey]
const storedKey = storage.getItem(this.storageKey)
if (props.goal.prop_names.includes(storedKey)) {
propKey = storedKey
}
Expand Down Expand Up @@ -70,7 +71,7 @@ export default class PropertyBreakdown extends React.Component {
}

changePropKey(newKey) {
window.localStorage[this.storageKey] = newKey
storage.setItem(this.storageKey, newKey)
this.setState({propKey: newKey, loading: true}, this.fetchPropBreakdown)
}

Expand Down
5 changes: 3 additions & 2 deletions assets/js/dashboard/stats/devices/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { Link } from 'react-router-dom'

import * as storage from '../../storage'
import LazyLoader from '../../lazy-loader'
import Browsers from './browsers'
import OperatingSystems from './operating-systems'
Expand Down Expand Up @@ -117,7 +118,7 @@ export default class Devices extends React.Component {
constructor(props) {
super(props)
this.tabKey = 'deviceTab__' + props.site.domain
const storedTab = window.localStorage[this.tabKey]
const storedTab = storage.getItem(this.tabKey)
this.state = {
mode: storedTab || 'size'
}
Expand All @@ -135,7 +136,7 @@ export default class Devices extends React.Component {

setMode(mode) {
return () => {
window.localStorage[this.tabKey] = mode
storage.setItem(this.tabKey, mode)
this.setState({mode})
}
}
Expand Down
5 changes: 3 additions & 2 deletions assets/js/dashboard/stats/pages/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { Link } from 'react-router-dom'

import * as storage from '../../storage'
import Visits from './pages'
import EntryPages from './entry-pages'
import ExitPages from './exit-pages'
Expand All @@ -16,7 +17,7 @@ export default class Pages extends React.Component {
constructor(props) {
super(props)
this.tabKey = 'pageTab__' + props.site.domain
const storedTab = window.localStorage[this.tabKey]
const storedTab = storage.getItem(this.tabKey)
this.state = {
mode: storedTab || 'pages'
}
Expand All @@ -34,7 +35,7 @@ export default class Pages extends React.Component {

setMode(mode) {
return () => {
window.localStorage[this.tabKey] = mode
storage.setItem(this.tabKey, mode)
this.setState({mode})
}
}
Expand Down
5 changes: 3 additions & 2 deletions assets/js/dashboard/stats/sources/source-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { Link } from 'react-router-dom'
import FlipMove from 'react-flip-move';

import * as storage from '../../storage'
import FadeIn from '../../fade-in'
import Bar from '../bar'
import MoreLink from '../more-link'
Expand Down Expand Up @@ -210,15 +211,15 @@ export default class SourceList extends React.Component {
constructor(props) {
super(props)
this.tabKey = 'sourceTab__' + props.site.domain
const storedTab = window.localStorage[this.tabKey]
const storedTab = storage.getItem(this.tabKey)
this.state = {
tab: storedTab || 'all'
}
}

setTab(tab) {
return () => {
window.localStorage[this.tabKey] = tab
storage.setItem(this.tabKey, tab)
this.setState({tab})
}
}
Expand Down
35 changes: 35 additions & 0 deletions assets/js/dashboard/storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// This module checks if localStorage is available and uses it for persistent frontend storage
// if possible. Localstorage can be blocked by browsers when people block third-party cookies and
// the dashboard is running in embedded mode. In those cases, store stuff in a regular object instead.

const memStore = {}

// https://stackoverflow.com/a/16427747
function testLocalStorageAvailability(){
try {
const testItem = 'test';
localStorage.setItem(testItem, testItem);
localStorage.removeItem(testItem);
return true;
} catch(e) {
return false;
}
}

const isLocalStorageAvailable = testLocalStorageAvailability()

export function setItem(key, value) {
if (isLocalStorageAvailable) {
window.localStorage.setItem(key, value)
} else {
memStore[key] = value
}
}

export function getItem(key) {
if (isLocalStorageAvailable) {
return window.localStorage.getItem(key)
} else {
return memStore[key]
}
}

0 comments on commit 255b4b2

Please sign in to comment.