feat: locally store stats so users can see them (no ui yet)

This commit is contained in:
David Ralph 2021-06-30 11:27:54 +01:00
parent 1d44b2792e
commit 2dcaa5270d
28 changed files with 78 additions and 45 deletions

View File

@ -31,7 +31,7 @@ export default class App extends React.PureComponent {
}
});
window.analytics.tabLoad();
window.stats.tabLoad();
}
render() {

View File

@ -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
};

View File

@ -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', '')}`);
}
}

View File

@ -1,5 +1,5 @@
export default function Lightbox(props) {
window.analytics.postEvent('modal', 'Opened lightbox');
window.stats.postEvent('modal', 'Opened lightbox');
return (
<>

View File

@ -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');
}
}

View File

@ -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 (

View File

@ -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 (

View File

@ -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)) {

View File

@ -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,

View File

@ -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)) {

View File

@ -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();
}

View File

@ -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)) {

View File

@ -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() {

View File

@ -10,6 +10,9 @@ export default function ExperimentalSettings() {
<h2>{experimental.title}</h2>
<p>{experimental.warning}</p>
<Checkbox name='animations' text={window.language.modals.main.settings.sections.appearance.animations} element='.other'/>
<h3>Usage Stats</h3>
<p>Allows you to see stats such as how many tabs you have opened, quotes favourited etc. It also sends this data anonymously to our<a className='modalLink' href='https://github.com/mue/umami'>umami</a> instance.</p>
<Checkbox name='stats' text='Enable Usage Stats' element='.other'/>
<h3>{experimental.developer}</h3>
<Checkbox name='debug' text='Debug hotkey (Ctrl + #)' element='.other'/>
<Slider title='Debug timeout' name='debugtimeout' min='0' max='5000' default='0' step='100' display=' miliseconds' element='.other' />

View File

@ -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');
}

View File

@ -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 = () => {

View File

@ -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({

View File

@ -19,7 +19,7 @@ export default class Favourite extends React.PureComponent {
this.setState({
favourited: <StarIcon2 onClick={this.favourite} className='topicons' />
});
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: <StarIcon onClick={this.favourite} className='topicons' />
});
window.analytics.postEvent('feature', 'Background unfavourite');
window.stats.postEvent('feature', 'Background unfavourite');
}
}

View File

@ -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');
}
}

View File

@ -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) {

View File

@ -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);

View File

@ -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();
}

View File

@ -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() {

View File

@ -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;
}

View File

@ -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: () => ''
}

View File

@ -234,5 +234,13 @@
{
"name": "backgroundFilterAmount",
"value": 0
},
{
"name": "stats",
"value": false
},
{
"name": "statsData",
"value": "{}"
}
]

View File

@ -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) {

View File

@ -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));
}
}