Skip to content

Commit

Permalink
refactor: refactored settings in preparation for server-side storage …
Browse files Browse the repository at this point in the history
…of settings

fix: removed empty file
  • Loading branch information
ErikBjare committed Mar 15, 2022
1 parent f7038e2 commit 00e1995
Show file tree
Hide file tree
Showing 17 changed files with 271 additions and 339 deletions.
3 changes: 2 additions & 1 deletion src/components/InputTimeInterval.vue
Expand Up @@ -59,7 +59,7 @@ export default {
},
data() {
return {
duration: JSON.parse(JSON.stringify(this.defaultDuration)), // Make a copy of defaultDuration
duration: null,
mode: 'last_duration',
start: null,
end: null,
Expand All @@ -86,6 +86,7 @@ export default {
},
},
mounted() {
this.duration = this.defaultDuration;
this.valueChanged();
},
methods: {
Expand Down
34 changes: 13 additions & 21 deletions src/components/NewReleaseNotification.vue
@@ -1,14 +1,14 @@
<template lang="pug">
div
b-alert(v-if="isVisible", variant="info", show)
| A new release, v{{ latestVersion }}, is available for
| #[a(href="https://activitywatch.net/downloads/" target="_blank" class="alert-link") download],
| A new release, v{{ latestVersion }}, is available for
| #[a(href="https://activitywatch.net/downloads/" target="_blank" class="alert-link") download],
| you can also #[a(href="javascript:void(0);" class="alert-link" @click="disableCheck") disable]
| future reminders and checks for updates.
button(type="button", class="close", @click="isVisible=false") &times;

b-alert(v-if="isFollowUpVisible", variant="success", show)
| Checking for new releases is now disabled, you can re-enable it in the
| Checking for new releases is now disabled, you can re-enable it in the
| #[router-link(to="/settings" class="alert-link" @click.native="isFollowUpVisible=false") settings page].
button(type="button", class="close", @click="isFollowUpVisible=false") &times;
</template>
Expand All @@ -32,7 +32,6 @@ export default {
return {
isVisible: false,
isFollowUpVisible: false,
data: null, // data should have isEnabled, nextCheckTime, howOftenToCheck, timesChecked
currentVersion: null,
latestVersion: null,
latestVersionDate: null,
Expand All @@ -41,8 +40,17 @@ export default {
LONG_BACKOFF_PERIOD: LONG_BACKOFF_PERIOD,
};
},
computed: {
data: {
get: function () {
return this.$store.state.settings.newReleaseCheckData;
},
set: function (data) {
this.$store.dispatch('settings/update', { newReleaseCheckData: data });
},
},
},
async mounted() {
this.retrieveData();
if (this.data && (!this.data.isEnabled || moment() < moment(this.data.nextCheckTime))) return;
await this.retrieveCurrentVersion();
Expand All @@ -67,20 +75,8 @@ export default {
timesChecked: this.isVisible ? 1 : 0,
};
}
this.saveData();
},
methods: {
retrieveData() {
if (localStorage.getItem('newReleaseCheckData')) {
try {
this.data = JSON.parse(localStorage.getItem('newReleaseCheckData'));
} catch (err) {
console.error('newReleaseCheckData not found in localStorage: ', err);
localStorage.removeItem('newReleaseCheckData');
}
}
},
async retrieveCurrentVersion() {
try {
const response = await this.$aw.getInfo();
Expand Down Expand Up @@ -123,10 +119,6 @@ export default {
if (this.latestVersionDate) return moment() >= this.latestVersionDate.add(7, 'days');
return false;
},
saveData() {
const parsed = JSON.stringify(this.data);
localStorage.setItem('newReleaseCheckData', parsed);
},
disableCheck() {
this.isVisible = false;
this.isFollowUpVisible = true;
Expand Down
58 changes: 26 additions & 32 deletions src/components/UserSatisfactionPoll.vue
Expand Up @@ -86,27 +86,34 @@ export default {
// options is an array of [1, ..., NUM_OPTIONS]
options: range(1, NUM_OPTIONS + 1),
rating: null,
data: null,
};
},
mounted() {
// Check if initialTimestamp (first time that the user runs the web app) exists
let initialTimestamp = moment();
if (localStorage.initialTimestamp) {
initialTimestamp = moment(localStorage.initialTimestamp);
} else {
localStorage.initialTimestamp = initialTimestamp;
}
computed: {
data: {
get() {
return this.$store.state.settings.userSatisfactionPollData;
},
set(value) {
const data = this.$store.state.settings.userSatisfactionPollData;
this.$store.dispatch('settings/update', {
userSatisfactionPollData: { ...data, ...value },
});
},
},
},
async mounted() {
await this.$store.dispatch('settings/ensureLoaded');
// Get the rest of the data
this.retrieveData();
if (!this.data) {
this.data = {
isEnabled: true,
nextPollTime: initialTimestamp.add(INITIAL_WAIT_PERIOD, 'seconds'),
nextPollTime: this.$store.state.settings.initialTimestamp.add(
INITIAL_WAIT_PERIOD,
'seconds'
),
timesPollIsShown: 0,
};
this.saveData();
}
if (!this.data.isEnabled) {
Expand All @@ -124,28 +131,14 @@ export default {
if (this.data.timesPollIsShown > 2) {
this.data.isEnabled = false;
}
this.saveData();
},
methods: {
retrieveData() {
if (localStorage.getItem('userSatisfactionPollData')) {
try {
this.data = JSON.parse(localStorage.getItem('userSatisfactionPollData'));
} catch (err) {
console.error('userSatisfactionPollData not found in localStorage: ', err);
localStorage.removeItem('userSatisfactionPollData');
}
}
},
saveData() {
const parsed = JSON.stringify(this.data);
localStorage.setItem('userSatisfactionPollData', parsed);
},
submit() {
this.isPollVisible = false;
this.data.isEnabled = false;
this.saveData();
const data = this.data;
data.isEnabled = false;
this.data = data;
if (parseInt(this.rating) >= 6) {
this.isPosFollowUpVisible = true;
} else {
Expand All @@ -154,8 +147,9 @@ export default {
},
dontShowAgain() {
this.isPollVisible = false;
this.data.isEnabled = false;
this.saveData();
const data = this.data;
data.isEnabled = false;
this.data = data;
},
},
};
Expand Down
5 changes: 0 additions & 5 deletions src/main.js
Expand Up @@ -13,11 +13,6 @@ import { Datetime } from 'vue-datetime';
import 'vue-datetime/dist/vue-datetime.css';
Vue.component('datetime', Datetime);

// Setup default settings
if (!('startOfDay' in localStorage)) {
localStorage.startOfDay = '04:00';
}

// Load the Varela Round font
import 'typeface-varela-round';

Expand Down
2 changes: 2 additions & 0 deletions src/store/index.js
Expand Up @@ -2,6 +2,7 @@ import Vue from 'vue';
import Vuex from 'vuex';
import activity from './modules/activity';
import buckets from './modules/buckets';
import settings from './modules/settings';
import categories from './modules/categories';
import views from './modules/views';
//import createLogger from '../../../src/plugins/logger';
Expand All @@ -14,6 +15,7 @@ export default new Vuex.Store({
modules: {
activity,
buckets,
settings,
categories,
views,
},
Expand Down
136 changes: 136 additions & 0 deletions src/store/modules/settings.ts
@@ -0,0 +1,136 @@
import moment, { Moment } from 'moment';
import NewReleaseNotification from '~/components/NewReleaseNotification.vue';

const SHORT_BACKOFF_PERIOD = NewReleaseNotification.SHORT_BACKOFF_PERIOD;

interface State {
// Timestamp when user was first seen (first time webapp is run)
initialTimestamp: Moment;

startOfDay: string;
durationDefault: number;
useColorFallback: boolean;
landingpage: string;

// light or dark
theme: string;

newReleaseCheckData: Record<string, any>;
userSatisfactionPollData: Record<string, any>;

// Set to true if settings loaded
_loaded: boolean;
}

// initial state, default settings
const _state: State = {
initialTimestamp: moment(),
startOfDay: '04:00',
durationDefault: 4 * 60 * 60,
useColorFallback: false,
landingpage: '/home',
theme: 'light',
newReleaseCheckData: {
isEnabled: true,
nextCheckTime: moment().add(SHORT_BACKOFF_PERIOD, 'seconds'),
howOftenToCheck: SHORT_BACKOFF_PERIOD,
timesChecked: 0,
},
userSatisfactionPollData: {},
_loaded: false,
};

// getters
const getters = {
loaded(state) {
return state._loaded;
},
};

// actions
const actions = {
async ensureLoaded({ dispatch, getters }) {
if (!getters.loaded) {
await dispatch('load');
}
},
async load({ commit }) {
if (typeof localStorage === 'undefined') {
console.error('localStorage is not supported');
return;
}
// Fetch from localStorage first, if exists
const storage = {};
for (const key in localStorage) {
// Skip built-in properties like length, setItem, etc.
// Also skip keys starting with underscore, as they are local to the vuex store.
if (Object.prototype.hasOwnProperty.call(localStorage, key) && !key.startsWith('_')) {
const value = localStorage.getItem(key);
//console.log(`${key}: ${value}`);

// Keys ending with 'Data' are JSON-serialized objects
if (key.includes('Data')) {
try {
storage[key] = JSON.parse(value);
} catch (e) {
console.error('failed to parse', key, value);
}
} else {
storage[key] = value;
}
}
}
commit('setState', storage);

// TODO: Then fetch from server
//const getSettingsFromServer = async () => {
// const { data } = await this.$aw._get('/0/settings');
// return data;
//};
},
async save({ state, dispatch }) {
// First save to localStorage
for (const key of Object.keys(state)) {
const value = state[key];
if (typeof value === 'object') {
localStorage.setItem(key, JSON.stringify(value));
} else {
localStorage.setItem(key, value);
}
}

// TODO: Save to backend
//const updateSettingOnServer = async (key: string, value: string) => {
// console.log({ key, value });
// const headers = { 'Content-Type': 'application/json' };
// const { data } = await this.$aw._post('/0/settings', { key, value }, headers);
// return data;
//};

// After save, reload from localStorage
await dispatch('load');
},
async update({ commit, dispatch }, new_state: Record<string, any>) {
console.log(`Updating state ${new_state}`);
commit('setState', new_state);
await dispatch('save');
},
};

// mutations
const mutations = {
setState(state: State, new_state: Record<string, any>) {
for (const key of Object.keys(new_state)) {
state[key] = new_state[key];
}
state._loaded = true;
},
};

export default {
namespaced: true,
state: _state,
getters,
actions,
mutations,
};
13 changes: 0 additions & 13 deletions src/util/settings.ts

This file was deleted.

7 changes: 6 additions & 1 deletion src/views/Timeline.vue
Expand Up @@ -27,11 +27,13 @@ export default {
return {
buckets: null,
daterange: null,
timeintervalDefaultDuration: Number.parseInt(localStorage.durationDefault) || 60 * 60,
maxDuration: 31 * 24 * 60 * 60,
};
},
computed: {
timeintervalDefaultDuration() {
return Number(this.$store.state.settings.durationDefault);
},
num_events() {
return _.sumBy(this.buckets, 'events.length');
},
Expand All @@ -41,6 +43,9 @@ export default {
this.getBuckets();
},
},
async mounted() {
await this.$store.dispatch('settings/ensureLoaded');
},
methods: {
getBuckets: async function () {
if (this.daterange == null) return;
Expand Down

0 comments on commit 00e1995

Please sign in to comment.