mirror of
https://github.com/DCsunset/taskwarrior-webui.git
synced 2025-08-19 06:53:06 +02:00
feat(frontend): add settings and auto refresh
This commit is contained in:
parent
e0c6f98cf9
commit
099543c0e1
4 changed files with 181 additions and 4 deletions
120
frontend/components/SettingsDialog.vue
Normal file
120
frontend/components/SettingsDialog.vue
Normal 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>
|
|
@ -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
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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 [];
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue