feat(frontend): add settings and auto refresh

This commit is contained in:
DCsunset 2020-07-15 03:20:39 -07:00
parent e0c6f98cf9
commit 099543c0e1
4 changed files with 181 additions and 4 deletions

View file

@ -0,0 +1,120 @@
<template>
<v-dialog
v-model="showDialog"
max-width="400px"
@keydown.esc="closeDialog"
>
<v-card>
<v-card-title>
Settings
</v-card-title>
<v-card-text>
<v-form ref="formRef">
<v-list-item>
<v-list-item-content>
<v-list-item-title>
Dark
</v-list-item-title>
<v-list-item-subtitle>
default theme
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-checkbox v-model="settings.dark" />
</v-list-item-action>
</v-list-item>
<v-list-item>
<v-list-item-content>
<v-list-item-title>
Auto Refresh
</v-list-item-title>
<v-list-item-subtitle>
in minutes (0 means no refresh)
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-text-field
v-model="settings.autoRefresh"
style="width: 30px"
:rules="numberRules"
/>
</v-list-item-action>
</v-list-item>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn @click="closeDialog">
Cancel
</v-btn>
<v-btn @click="reset">
Reset
</v-btn>
<v-btn color="primary" @click="save">
Save
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script lang="ts">
import { defineComponent, computed, ref, reactive } from '@vue/composition-api';
export default defineComponent({
props: {
value: {
type: Boolean,
required: true
}
},
setup(props, context) {
const showDialog = computed({
get: () => props.value,
set: val => context.emit('input', val)
});
const numberRules = [
(str: string) => (str && !isNaN(+str) && +str >= 0) || 'invalid'
];
const formRef = ref(null);
const settings = reactive({
dark: context.root.$store.state.settings.dark,
autoRefresh: context.root.$store.state.settings.autoRefresh
});
const reset = () => {
settings.dark = context.root.$store.state.settings.dark;
settings.autoRefresh = context.root.$store.state.settings.autoRefresh;
};
const closeDialog = () => {
showDialog.value = false;
reset();
};
const save = () => {
const valid = (formRef as any).value.validate();
if (valid) {
context.root.$store.dispatch('updateSettings', {
...settings
});
closeDialog();
}
};
return {
showDialog,
closeDialog,
save,
reset,
settings,
numberRules,
formRef
};
}
});
</script>

View file

@ -1,5 +1,7 @@
<template>
<v-app>
<SettingsDialog v-model="settingsDialog" />
<v-snackbar
v-model="snackbar"
:color="notification.color"
@ -24,9 +26,17 @@
Taskwarrior WebUI
</v-toolbar-title>
<v-spacer />
<v-icon class="mr-2" size="28px" @click="dark = !dark">
<v-icon class="mr-4" size="28px" @click="dark = !dark" title="Theme">
{{ dark ? 'mdi-brightness-4' : 'mdi-brightness-7' }}
</v-icon>
<v-icon
class="mr-2"
size="28px"
title="Settings"
@click="settingsDialog = true"
>
mdi-cog
</v-icon>
</v-app-bar>
<v-content>
@ -38,10 +48,15 @@
</template>
<script lang="ts">
import { defineComponent, computed, onErrorCaptured } from '@vue/composition-api';
import { defineComponent, computed, onErrorCaptured, ref } from '@vue/composition-api';
import SettingsDialog from '../components/SettingsDialog.vue';
export default defineComponent({
setup(_props, context) {
context.root.$store.dispatch('fetchSettings');
context.root.$vuetify.theme.dark = context.root.$store.state.settings.dark;
const dark = computed({
get: () => context.root.$vuetify.theme.dark,
set: val => {
@ -49,6 +64,8 @@ export default defineComponent({
}
});
const settingsDialog = ref(false);
const notification = computed(() => context.root.$store.state.notification);
const snackbar = computed({
get: () => context.root.$store.state.snackbar,
@ -79,7 +96,10 @@ export default defineComponent({
return {
dark,
snackbar,
notification
notification,
settingsDialog,
SettingsDialog
};
}
});

View file

@ -35,11 +35,28 @@
<script lang="ts">
import { defineComponent, ref, computed, watch, ComputedRef } from '@vue/composition-api';
import TaskList from '../components/TaskList.vue';
import { Task } from 'taskwarrior-lib';
export default defineComponent({
setup(_props, context) {
context.root.$store.dispatch('fetchTasks');
let interval: NodeJS.Timeout | null = null;
const setAutoRefresh = () => {
console.log('Setting inverval');
if (interval)
clearInterval(interval);
const freq = +context.root.$store.state.settings.autoRefresh;
if (freq > 0) {
interval = setInterval(() => {
console.log('Refreshing...');
context.root.$store.dispatch('fetchTasks');
}, +context.root.$store.state.settings.autoRefresh * 60000);
}
};
setAutoRefresh();
watch(() => context.root.$store.state.settings, setAutoRefresh);
const mode = ref('Tasks');
const allModes = ['Tasks', 'Projects'];
@ -60,7 +77,7 @@ export default defineComponent({
if (project.value)
return context.root.$store.state.tasks.filter(
task => task.project === project.value
(task: Task) => task.project === project.value
);
return [];

View file

@ -8,6 +8,10 @@ export const state = () => ({
notification: {
color: '',
text: ''
},
settings: {
dark: false,
autoRefresh: '5' // in minutes
}
});
@ -21,6 +25,10 @@ export const getters: GetterTree<RootState, RootState> = {
};
export const mutations: MutationTree<RootState> = {
setSettings(state, settings) {
state.settings = settings;
},
setTasks(state, tasks: Task[]) {
state.tasks = tasks;
},
@ -37,6 +45,18 @@ export const mutations: MutationTree<RootState> = {
};
export const actions: ActionTree<RootState, RootState> = {
fetchSettings(context) {
const settings = localStorage.getItem('settings');
if (settings) {
context.commit('setSettings', JSON.parse(settings));
}
},
updateSettings(context, settings) {
context.commit('setSettings', settings);
localStorage.setItem('settings', JSON.stringify(settings));
},
async fetchTasks(context) {
const tasks: Task[] = await this.$axios.$get('/api/tasks');
context.commit('setTasks', tasks);