From 2dcaa5270da7e3d12f7190a5cacf2af4abfc361e Mon Sep 17 00:00:00 2001 From: David Ralph Date: Wed, 30 Jun 2021 11:27:54 +0100 Subject: [PATCH] feat: locally store stats so users can see them (no ui yet) --- src/App.jsx | 2 +- src/components/modals/ErrorBoundary.jsx | 2 +- src/components/modals/Modals.jsx | 4 +-- .../modals/main/marketplace/Lightbox.jsx | 2 +- .../main/marketplace/sections/Added.jsx | 6 ++--- .../main/marketplace/sections/Marketplace.jsx | 10 +++---- .../main/marketplace/sections/Sideload.jsx | 2 +- .../modals/main/settings/Checkbox.jsx | 2 +- .../modals/main/settings/Dropdown.jsx | 2 +- src/components/modals/main/settings/Radio.jsx | 2 +- .../modals/main/settings/ResetModal.jsx | 2 +- .../modals/main/settings/Switch.jsx | 2 +- .../main/settings/sections/Advanced.jsx | 2 +- .../main/settings/sections/Experimental.jsx | 3 +++ .../modals/main/settings/sections/Order.jsx | 2 +- .../settings/sections/background/Colour.jsx | 2 +- .../modals/main/tabs/backend/Tabs.jsx | 2 +- .../widgets/background/Favourite.jsx | 4 +-- .../widgets/background/Maximise.jsx | 4 +-- .../widgets/background/PhotoInformation.jsx | 2 +- src/components/widgets/navbar/Notes.jsx | 4 +-- .../widgets/quicklinks/QuickLinks.jsx | 4 +-- src/components/widgets/quote/Quote.jsx | 6 ++--- src/components/widgets/search/Search.jsx | 4 +-- src/index.js | 10 +++---- src/modules/default_settings.json | 8 ++++++ src/modules/helpers/settings.js | 2 +- .../helpers/{analytics.js => stats.js} | 26 +++++++++++++++++-- 28 files changed, 78 insertions(+), 45 deletions(-) rename src/modules/helpers/{analytics.js => stats.js} (62%) diff --git a/src/App.jsx b/src/App.jsx index 81b5e59c..4135e71d 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -31,7 +31,7 @@ export default class App extends React.PureComponent { } }); - window.analytics.tabLoad(); + window.stats.tabLoad(); } render() { diff --git a/src/components/modals/ErrorBoundary.jsx b/src/components/modals/ErrorBoundary.jsx index f888927a..90fde9fa 100644 --- a/src/components/modals/ErrorBoundary.jsx +++ b/src/components/modals/ErrorBoundary.jsx @@ -13,7 +13,7 @@ export default class ErrorBoundary extends React.PureComponent { static getDerivedStateFromError(error) { console.log(error); - window.analytics.postEvent('modal', 'Error occurred'); + window.stats.postEvent('modal', 'Error occurred'); return { error: true }; diff --git a/src/components/modals/Modals.jsx b/src/components/modals/Modals.jsx index 439bc207..9948a630 100644 --- a/src/components/modals/Modals.jsx +++ b/src/components/modals/Modals.jsx @@ -27,7 +27,7 @@ export default class Modals extends React.PureComponent { this.setState({ welcomeModal: true }); - window.analytics.postEvent('modal', 'Opened welcome'); + window.stats.postEvent('modal', 'Opened welcome'); } // hide refresh reminder once the user has refreshed the page @@ -47,7 +47,7 @@ export default class Modals extends React.PureComponent { }); if (action !== false) { - window.analytics.postEvent('modal', `Opened ${type.replace('Modal', '')}`); + window.stats.postEvent('modal', `Opened ${type.replace('Modal', '')}`); } } diff --git a/src/components/modals/main/marketplace/Lightbox.jsx b/src/components/modals/main/marketplace/Lightbox.jsx index d430bc6f..1a552e1f 100644 --- a/src/components/modals/main/marketplace/Lightbox.jsx +++ b/src/components/modals/main/marketplace/Lightbox.jsx @@ -1,5 +1,5 @@ export default function Lightbox(props) { - window.analytics.postEvent('modal', 'Opened lightbox'); + window.stats.postEvent('modal', 'Opened lightbox'); return ( <> diff --git a/src/components/modals/main/marketplace/sections/Added.jsx b/src/components/modals/main/marketplace/sections/Added.jsx index a85a73bf..71ca2c90 100644 --- a/src/components/modals/main/marketplace/sections/Added.jsx +++ b/src/components/modals/main/marketplace/sections/Added.jsx @@ -42,7 +42,7 @@ export default class Added extends React.PureComponent { }, button: this.buttons.uninstall }); - window.analytics.postEvent('marketplace', 'Item viewed'); + window.stats.postEvent('marketplace', 'Item viewed'); } else { this.setState({ item: {} @@ -60,7 +60,7 @@ export default class Added extends React.PureComponent { installed: JSON.parse(localStorage.getItem('installed')) }); - window.analytics.postEvent('marketplace', 'Uninstall'); + window.stats.postEvent('marketplace', 'Uninstall'); } sortAddons(value, sendEvent) { @@ -87,7 +87,7 @@ export default class Added extends React.PureComponent { }); if (sendEvent) { - window.analytics.postEvent('marketplace', 'Sort'); + window.stats.postEvent('marketplace', 'Sort'); } } diff --git a/src/components/modals/main/marketplace/sections/Marketplace.jsx b/src/components/modals/main/marketplace/sections/Marketplace.jsx index 699fe3b7..1de2b55d 100644 --- a/src/components/modals/main/marketplace/sections/Marketplace.jsx +++ b/src/components/modals/main/marketplace/sections/Marketplace.jsx @@ -68,7 +68,7 @@ export default class Marketplace extends React.PureComponent { button: button }); - window.analytics.postEvent('marketplace-item', `${this.state.item.display_name} viewed`); + window.stats.postEvent('marketplace-item', `${this.state.item.display_name} viewed`); } else { this.setState({ item: {} @@ -106,8 +106,8 @@ export default class Marketplace extends React.PureComponent { button: (type === 'install') ? this.buttons.uninstall : this.buttons.install }); - window.analytics.postEvent('marketplace-item', `${this.state.item.display_name} ${(type === 'install' ? 'installed': 'uninstalled')}`); - window.analytics.postEvent('marketplace', (type === 'install' ? 'Install': 'Uninstall')); + window.stats.postEvent('marketplace-item', `${this.state.item.display_name} ${(type === 'install' ? 'installed': 'uninstalled')}`); + window.stats.postEvent('marketplace', (type === 'install' ? 'Install': 'Uninstall')); } sortMarketplace(value, sendEvent) { @@ -134,7 +134,7 @@ export default class Marketplace extends React.PureComponent { }); if (sendEvent) { - window.analytics.postEvent('marketplace', 'Sort'); + window.stats.postEvent('marketplace', 'Sort'); } } @@ -164,7 +164,7 @@ export default class Marketplace extends React.PureComponent { const featured = () => { const openFeatured = () => { - window.analytics.postEvent('marketplace', 'Featured clicked'); + window.stats.postEvent('marketplace', 'Featured clicked'); window.open(this.state.featured.buttonLink); } return ( diff --git a/src/components/modals/main/marketplace/sections/Sideload.jsx b/src/components/modals/main/marketplace/sections/Sideload.jsx index eb6d8904..0586d0cb 100644 --- a/src/components/modals/main/marketplace/sections/Sideload.jsx +++ b/src/components/modals/main/marketplace/sections/Sideload.jsx @@ -10,7 +10,7 @@ export default function Sideload() { const install = (input) => { MarketplaceFunctions.install(input.type, input); toast(window.language.toasts.installed); - window.analytics.postEvent('marketplace', 'Sideload'); + window.stats.postEvent('marketplace', 'Sideload'); }; return ( diff --git a/src/components/modals/main/settings/Checkbox.jsx b/src/components/modals/main/settings/Checkbox.jsx index 037a9e5b..0652d563 100644 --- a/src/components/modals/main/settings/Checkbox.jsx +++ b/src/components/modals/main/settings/Checkbox.jsx @@ -21,7 +21,7 @@ export default class Checkbox extends React.PureComponent { checked: (this.state.checked === true) ? false : true }); - window.analytics.postEvent('setting', `${this.props.name} ${(this.state.checked === true) ? 'enabled' : 'disabled'}`); + window.stats.postEvent('setting', `${this.props.name} ${(this.state.checked === true) ? 'enabled' : 'disabled'}`); if (this.props.element) { if (!document.querySelector(this.props.element)) { diff --git a/src/components/modals/main/settings/Dropdown.jsx b/src/components/modals/main/settings/Dropdown.jsx index 7409de91..590d014e 100644 --- a/src/components/modals/main/settings/Dropdown.jsx +++ b/src/components/modals/main/settings/Dropdown.jsx @@ -22,7 +22,7 @@ export default class Dropdown extends React.PureComponent { return; } - window.analytics.postEvent('setting', `${this.props.name} from ${this.state.value} to ${value}`); + window.stats.postEvent('setting', `${this.props.name} from ${this.state.value} to ${value}`); this.setState({ value: value, diff --git a/src/components/modals/main/settings/Radio.jsx b/src/components/modals/main/settings/Radio.jsx index 02ff4d6b..4875a215 100644 --- a/src/components/modals/main/settings/Radio.jsx +++ b/src/components/modals/main/settings/Radio.jsx @@ -29,7 +29,7 @@ export default class Radio extends React.PureComponent { value: value }); - window.analytics.postEvent('setting', `${this.props.name} from ${this.state.value} to ${value}`); + window.stats.postEvent('setting', `${this.props.name} from ${this.state.value} to ${value}`); if (this.props.element) { if (!document.querySelector(this.props.element)) { diff --git a/src/components/modals/main/settings/ResetModal.jsx b/src/components/modals/main/settings/ResetModal.jsx index a247d3ff..bda18039 100644 --- a/src/components/modals/main/settings/ResetModal.jsx +++ b/src/components/modals/main/settings/ResetModal.jsx @@ -4,7 +4,7 @@ export default function ResetModal(props) { const language = window.language.modals.main.settings.sections.advanced.reset_modal; const reset = () => { - window.analytics.postEvent('setting', 'Reset'); + window.stats.postEvent('setting', 'Reset'); SettingsFunctions.setDefaultSettings('reset'); window.location.reload(); } diff --git a/src/components/modals/main/settings/Switch.jsx b/src/components/modals/main/settings/Switch.jsx index 7b45a6af..f7a295fe 100644 --- a/src/components/modals/main/settings/Switch.jsx +++ b/src/components/modals/main/settings/Switch.jsx @@ -21,7 +21,7 @@ export default class Switch extends React.PureComponent { checked: (this.state.checked === true) ? false : true }); - window.analytics.postEvent('setting', `${this.props.name} ${(this.state.checked === true) ? 'enabled' : 'disabled'}`); + window.stats.postEvent('setting', `${this.props.name} ${(this.state.checked === true) ? 'enabled' : 'disabled'}`); if (this.props.element) { if (!document.querySelector(this.props.element)) { diff --git a/src/components/modals/main/settings/sections/Advanced.jsx b/src/components/modals/main/settings/sections/Advanced.jsx index 408f147c..b1683d02 100644 --- a/src/components/modals/main/settings/sections/Advanced.jsx +++ b/src/components/modals/main/settings/sections/Advanced.jsx @@ -28,7 +28,7 @@ export default class AdvancedSettings extends React.PureComponent { }); toast(window.language.toasts.imported); - window.analytics.postEvent('tab', 'Settings imported'); + window.stats.postEvent('tab', 'Settings imported'); } render() { diff --git a/src/components/modals/main/settings/sections/Experimental.jsx b/src/components/modals/main/settings/sections/Experimental.jsx index ae837e75..0503adfd 100644 --- a/src/components/modals/main/settings/sections/Experimental.jsx +++ b/src/components/modals/main/settings/sections/Experimental.jsx @@ -10,6 +10,9 @@ export default function ExperimentalSettings() {

{experimental.title}

{experimental.warning}

+

Usage Stats

+

Allows you to see stats such as how many tabs you have opened, quotes favourited etc. It also sends this data anonymously to ourumami instance.

+

{experimental.developer}

diff --git a/src/components/modals/main/settings/sections/Order.jsx b/src/components/modals/main/settings/sections/Order.jsx index 35d9075f..dc96b893 100644 --- a/src/components/modals/main/settings/sections/Order.jsx +++ b/src/components/modals/main/settings/sections/Order.jsx @@ -83,7 +83,7 @@ export default class OrderSettings extends React.PureComponent { componentDidUpdate() { localStorage.setItem('order', JSON.stringify(this.state.items)); - window.analytics.postEvent('setting', 'Widget order'); + window.stats.postEvent('setting', 'Widget order'); EventBus.dispatch('refresh', 'widgets'); } diff --git a/src/components/modals/main/settings/sections/background/Colour.jsx b/src/components/modals/main/settings/sections/background/Colour.jsx index 2a2779c9..f075738f 100644 --- a/src/components/modals/main/settings/sections/background/Colour.jsx +++ b/src/components/modals/main/settings/sections/background/Colour.jsx @@ -104,7 +104,7 @@ export default class ColourSettings extends React.PureComponent { return newState; }); - window.analytics.postEvent('setting', 'Changed backgroundtype from colour to gradient'); + window.stats.postEvent('setting', 'Changed backgroundtype from colour to gradient'); } currentGradientSettings = () => { diff --git a/src/components/modals/main/tabs/backend/Tabs.jsx b/src/components/modals/main/tabs/backend/Tabs.jsx index bcc9c368..089392d0 100644 --- a/src/components/modals/main/tabs/backend/Tabs.jsx +++ b/src/components/modals/main/tabs/backend/Tabs.jsx @@ -14,7 +14,7 @@ export default class Tabs extends React.PureComponent { onClick = (tab) => { if (tab !== this.state.currentTab) { - window.analytics.postEvent('tab', `Changed ${this.state.currentTab} to ${tab}`); + window.stats.postEvent('tab', `Changed ${this.state.currentTab} to ${tab}`); } this.setState({ diff --git a/src/components/widgets/background/Favourite.jsx b/src/components/widgets/background/Favourite.jsx index 4282acb0..8bb094a3 100644 --- a/src/components/widgets/background/Favourite.jsx +++ b/src/components/widgets/background/Favourite.jsx @@ -19,7 +19,7 @@ export default class Favourite extends React.PureComponent { this.setState({ favourited: }); - window.analytics.postEvent('feature', 'Background favourite'); + window.stats.postEvent('feature', 'Background favourite'); } else { const url = document.getElementById('backgroundImage').style.backgroundImage.replace('url("', '').replace('")', ''); @@ -38,7 +38,7 @@ export default class Favourite extends React.PureComponent { this.setState({ favourited: }); - window.analytics.postEvent('feature', 'Background unfavourite'); + window.stats.postEvent('feature', 'Background unfavourite'); } } diff --git a/src/components/widgets/background/Maximise.jsx b/src/components/widgets/background/Maximise.jsx index bdf28277..f10028d8 100644 --- a/src/components/widgets/background/Maximise.jsx +++ b/src/components/widgets/background/Maximise.jsx @@ -44,14 +44,14 @@ export default class Maximise extends React.PureComponent { }); this.setAttribute(0, 100); - window.analytics.postEvent('feature', 'Background maximise'); + window.stats.postEvent('feature', 'Background maximise'); } else { this.setState({ hidden: false }); this.setAttribute(localStorage.getItem('blur'), localStorage.getItem('brightness'), true); - window.analytics.postEvent('feature', 'Background unmaximise'); + window.stats.postEvent('feature', 'Background unmaximise'); } } diff --git a/src/components/widgets/background/PhotoInformation.jsx b/src/components/widgets/background/PhotoInformation.jsx index 5c061b24..4a2a04d6 100644 --- a/src/components/widgets/background/PhotoInformation.jsx +++ b/src/components/widgets/background/PhotoInformation.jsx @@ -18,7 +18,7 @@ const downloadImage = async (info) => { document.body.appendChild(link); link.click(); document.body.removeChild(link); - window.analytics.postEvent('feature', 'Background download'); + window.stats.postEvent('feature', 'Background download'); }; export default function PhotoInformation(props) { diff --git a/src/components/widgets/navbar/Notes.jsx b/src/components/widgets/navbar/Notes.jsx index 46a891d9..f221138b 100644 --- a/src/components/widgets/navbar/Notes.jsx +++ b/src/components/widgets/navbar/Notes.jsx @@ -25,7 +25,7 @@ export default class Notes extends React.PureComponent { }; pin() { - window.analytics.postEvent('feature', 'Notes pin'); + window.stats.postEvent('feature', 'Notes pin'); document.getElementById('noteContainer').classList.toggle('visibilityshow'); if (localStorage.getItem('notesPinned') === 'true') { @@ -36,7 +36,7 @@ export default class Notes extends React.PureComponent { } copy() { - window.analytics.postEvent('feature', 'Notes copied'); + window.stats.postEvent('feature', 'Notes copied'); // this.state.notes doesnt work for some reason navigator.clipboard.writeText(localStorage.getItem('notes')); toast(window.language.toasts.notes); diff --git a/src/components/widgets/quicklinks/QuickLinks.jsx b/src/components/widgets/quicklinks/QuickLinks.jsx index 02f8791c..2fbd6b53 100644 --- a/src/components/widgets/quicklinks/QuickLinks.jsx +++ b/src/components/widgets/quicklinks/QuickLinks.jsx @@ -32,7 +32,7 @@ export default class QuickLinks extends React.PureComponent { items: data }); - window.analytics.postEvent('feature', 'Quicklink delete'); + window.stats.postEvent('feature', 'Quicklink delete'); } addLink = () => { @@ -75,7 +75,7 @@ export default class QuickLinks extends React.PureComponent { url: '' }); - window.analytics.postEvent('feature', 'Quicklink add'); + window.stats.postEvent('feature', 'Quicklink add'); this.toggleAdd(); } diff --git a/src/components/widgets/quote/Quote.jsx b/src/components/widgets/quote/Quote.jsx index 7f08f34c..3c8024d8 100644 --- a/src/components/widgets/quote/Quote.jsx +++ b/src/components/widgets/quote/Quote.jsx @@ -150,13 +150,13 @@ export default class Quote extends React.PureComponent { } copyQuote = () => { - window.analytics.postEvent('feature', 'Quote copied'); + window.stats.postEvent('feature', 'Quote copied'); navigator.clipboard.writeText(`${this.state.quote} - ${this.state.author}`); toast(window.language.toasts.quote); } tweetQuote = () => { - window.analytics.postEvent('feature', 'Quote tweet'); + window.stats.postEvent('feature', 'Quote tweet'); window.open(`https://twitter.com/intent/tweet?text=${this.state.quote} - ${this.state.author} on @getmue`, '_blank').focus(); } @@ -173,7 +173,7 @@ export default class Quote extends React.PureComponent { }); } - window.analytics.postEvent('feature', 'Quote favourite'); + window.stats.postEvent('feature', 'Quote favourite'); } init() { diff --git a/src/components/widgets/search/Search.jsx b/src/components/widgets/search/Search.jsx index c22482c6..fc54be27 100644 --- a/src/components/widgets/search/Search.jsx +++ b/src/components/widgets/search/Search.jsx @@ -44,7 +44,7 @@ export default class Search extends React.PureComponent { } setTimeout(() => { - window.analytics.postEvent('feature', 'Voice search'); + window.stats.postEvent('feature', 'Voice search'); window.location.href = this.state.url + `?${this.state.query}=` + searchText.value; }, 1000); }; @@ -59,7 +59,7 @@ export default class Search extends React.PureComponent { value = document.getElementById('searchtext').value || 'mue fast'; } - window.analytics.postEvent('feature', 'Search'); + window.stats.postEvent('feature', 'Search'); window.location.href = this.state.url + `?${this.state.query}=` + value; } diff --git a/src/index.js b/src/index.js index bd9c64e9..7e921a67 100644 --- a/src/index.js +++ b/src/index.js @@ -10,8 +10,8 @@ import 'react-toastify/dist/ReactToastify.min.css'; import '@fontsource/lexend-deca/400.css'; -// this is opt-in btw -import Analytics from './modules/helpers/analytics'; +// this is opt-in btw, allows you to see your stats etc +import Stats from './modules/helpers/stats'; // language import merge from '@material-ui/utils/esm/deepmerge'; @@ -39,10 +39,10 @@ if (window.languagecode !== 'en_GB' || window.languagecode !== 'en_US') { } window.constants = Constants; -if (localStorage.getItem('analytics') === 'true' && localStorage.getItem('offlineMode') !== 'true') { - window.analytics = new Analytics(window.constants.UMAMI_ID); +if (localStorage.getItem('stats') === 'true' && localStorage.getItem('offlineMode') !== 'true') { + window.stats = new Stats(window.constants.UMAMI_ID); } else { - window.analytics = { + window.stats = { tabLoad: () => '', postEvent: () => '' } diff --git a/src/modules/default_settings.json b/src/modules/default_settings.json index 62f787a5..f751f1f6 100644 --- a/src/modules/default_settings.json +++ b/src/modules/default_settings.json @@ -234,5 +234,13 @@ { "name": "backgroundFilterAmount", "value": 0 + }, + { + "name": "stats", + "value": false + }, + { + "name": "statsData", + "value": "{}" } ] diff --git a/src/modules/helpers/settings.js b/src/modules/helpers/settings.js index 57e92c9c..441e09c3 100644 --- a/src/modules/helpers/settings.js +++ b/src/modules/helpers/settings.js @@ -27,7 +27,7 @@ export default class SettingsFunctions { settings[key] = localStorage.getItem(key); }); saveFile(settings, 'mue-settings.json'); - window.analytics.postEvent('tab', 'Settings exported'); + window.stats.postEvent('tab', 'Settings exported'); } static setItem(key, value) { diff --git a/src/modules/helpers/analytics.js b/src/modules/helpers/stats.js similarity index 62% rename from src/modules/helpers/analytics.js rename to src/modules/helpers/stats.js index f3ea767f..d5e182ad 100644 --- a/src/modules/helpers/analytics.js +++ b/src/modules/helpers/stats.js @@ -1,10 +1,12 @@ -export default class Analytics { +export default class Stats { constructor(id) { this.id = id; this.domain = window.constants.UMAMI_DOMAIN; } async postEvent(type, name) { + const value = name.toLowerCase().replaceAll(' ', '-'); + await fetch(this.domain + '/api/collect', { method: 'POST', headers: { @@ -17,13 +19,29 @@ export default class Analytics { website: this.id, url: '/', event_type: type, - event_value: name.toLowerCase().replaceAll(' ', '-'), + event_value: value, hostname: 'localhost', language: localStorage.getItem('language').replace('_', '-'), screen: `${window.screen.width}x${window.screen.height}` } }) }); + + let data = JSON.parse(localStorage.getItem('statsData')); + // tl;dr this creates the objects if they don't exist + // this really needs a cleanup at some point + if (!data[type] || !data[type][value]) { + if (!data[type]) { + data[type] = {}; + } + + if (!data[type][value]) { + data[type][value] = 1; + } + } else { + data[type][value] = data[type][value] + 1; + } + localStorage.setItem('statsData', JSON.stringify(data)); } async tabLoad() { @@ -45,5 +63,9 @@ export default class Analytics { } }) }); + + let data = JSON.parse(localStorage.getItem('statsData')); + data['tabs-opened'] = data['tabs-opened'] + 1 || 1; + localStorage.setItem('statsData', JSON.stringify(data)); } } \ No newline at end of file