Client Fixes (#9021)

* pass the timezoneoffset to the server

* implement yesterdaily modal timer

* fix typos

* todo

* task editing: checklist should not overlay the tags box

* make tags selector use two columns

* show all tags

* do not allow users to go to /login and /register if logged in
This commit is contained in:
Matteo Pagliazzi
2017-09-07 14:16:39 +02:00
committed by GitHub
parent adeee244e3
commit 5ec7815cfe
6 changed files with 116 additions and 60 deletions

View File

@@ -1,6 +1,9 @@
<template lang="pug">
div
yesterdaily-modal(:yesterDailies='yesterDailies')
yesterdaily-modal(
:yesterDailies='yesterDailies',
@hide="runYesterDailiesAction()",
)
armoire-empty
new-stuff
death
@@ -227,9 +230,11 @@ export default {
this.$root.$emit('show::modal', 'quest-invitation');
},
},
async mounted () {
Promise.all(['user.fetch', 'tasks.fetchUserTasks'])
.then(() => {
mounted () {
Promise.all([
this.$store.dispatch('user:fetch'),
this.$store.dispatch('tasks:fetchUserTasks'),
]).then(() => {
if (this.user.flags.newStuff) {
this.$root.$emit('show::modal', 'new-stuff');
}
@@ -253,18 +258,39 @@ export default {
playSound (sound) {
this.$root.$emit('playSound', sound);
},
async runYesterDailies () {
scheduleNextCron () {
// Open yesterdailies modal the next time cron runs
const dayStart = this.user.preferences.dayStart;
let nextCron = moment().hours(dayStart).minutes(0).seconds(0).milliseconds(0);
const currentHour = moment().format('H');
if (currentHour >= dayStart) {
nextCron = nextCron.add(1, 'day');
}
// Setup a listener that executes 10 seconds after the next cron time
const nextCronIn = Number(nextCron.format('x')) - Date.now() + 10 * 1000;
setInterval(() => {
this.user.needsCron = true; // @TODO move to action? not sent to the server
this.runYesterDailies();
}, nextCronIn);
},
async runYesterDailies (forceRun = false) {
// @TODO: Hopefully we don't need this even we load correctly
if (this.isRunningYesterdailies) return;
if (!this.user.needsCron) {
if (!this.user.needsCron && !forceRun) {
this.handleUserNotifications(this.user.notifications);
this.scheduleNextCron();
return;
}
let dailys = this.$store.state.tasks.data.dailys;
this.isRunningYesterdailies = true;
let yesterDay = moment().subtract('1', 'day').startOf('day').add({ hours: this.user.preferences.dayStart });
let yesterDay = moment().subtract('1', 'day').startOf('day').add({
hours: this.user.preferences.dayStart,
});
dailys.forEach((task) => {
if (task && task.group.approval && task.group.approval.requested) return;
if (task.completed) return;
@@ -273,14 +299,29 @@ export default {
});
if (this.yesterDailies.length === 0) {
this.isRunningYesterdailies = false;
await axios.post('/api/v3/cron');
this.handleUserNotifications(this.user.notifications);
this.runYesterDailiesAction();
return;
}
this.$root.$emit('show::modal', 'yesterdaily');
},
async runYesterDailiesAction () {
// Run Cron
await axios.post('/api/v3/cron');
// Notifications
// Sync
// @TODO add a loading spinner somewhere
await Promise.all([
this.$store.dispatch('user:fetch', {forceLoad: true}),
this.$store.dispatch('tasks:fetchUserTasks', {forceLoad: true}),
]);
this.handleUserNotifications(this.user.notifications);
this.isRunningYesterdailies = false;
this.scheduleNextCron();
},
transferGroupNotification (notification) {
if (!this.user.groupNotifications) this.user.groupNotifications = [];
this.user.groupNotifications.push(notification);
@@ -442,16 +483,6 @@ export default {
this.user.notifications = []; // reset the notifications
},
// @TODO: I think I have these handled in the http interceptor
// this.$on('responseError500', function(ev, error){
// Notification.error(error);
// });
// this.$on('responseError', function(ev, error){
// Notification.error(error, true);
// });
// this.$on('responseText', function(ev, error){
// Notification.text(error);
// });
},
};
</script>

View File

@@ -123,7 +123,9 @@
span.category-select(v-if='task.tags && task.tags.length === 0') {{$t('none')}}
span.category-select(v-else) {{getTagsFor(task)[0]}}
.category-box(v-if="showTagsSelect")
.form-check(
.container
.row
.form-check.col-6(
v-for="tag in user.tags",
:key="tag.id",
)
@@ -131,6 +133,7 @@
input.custom-control-input(type="checkbox", :value="tag.id", v-model="task.tags")
span.custom-control-indicator
span.custom-control-description(v-once) {{ tag.name }}
.row
button.btn.btn-primary(@click="showTagsSelect = !showTagsSelect") {{$t('close')}}
.option(v-if="task.type === 'habit'")
label(v-once) {{ $t('resetStreak') }}
@@ -145,7 +148,9 @@
span.category-select(v-else)
span(v-for='memberId in assignedMembers') {{memberNamesById[memberId]}}
.category-box(v-if="showAssignedSelect")
.form-check(
.container
.row
.form-check.col-6(
v-for="member in members",
:key="member._id",
)
@@ -153,6 +158,8 @@
input.custom-control-input(type="checkbox", :value="member._id", v-model="assignedMembers", @change='toggleAssignment(member._id)')
span.custom-control-indicator
span.custom-control-description(v-once) {{ member.profile.name }}
.row
button.btn.btn-primary(@click="showAssignedSelect = !showAssignedSelect") {{$t('close')}}
.option.group-options(v-if='groupId')
@@ -313,6 +320,12 @@
bottom: 0px;
left: 40px;
top: auto;
z-index: 11;
.container {
max-height: 90vh;
overflow: auto;
}
}
.checklist-group {

View File

@@ -1,5 +1,12 @@
<template lang="pug">
b-modal#yesterdaily(size='m', :hide-header="true", :hide-footer="true")
b-modal#yesterdaily(
size="m",
:hide-header="true",
:hide-footer="true",
:no-close-on-backdrop="true",
:no-close-on-esc="true",
@hide="$emit('hide')",
)
.modal-body
h1.header-welcome.text-center {{ $t('welcomeBack') }}
p.call-to-action.text-center {{ $t('checkOffYesterDailies') }}
@@ -44,7 +51,6 @@
</style>
<script>
import axios from 'axios';
import moment from 'moment';
import { mapState } from 'client/libs/store';
import bModal from 'bootstrap-vue/lib/components/modal';
@@ -71,9 +77,6 @@ export default {
},
methods: {
async close () {
await axios.post('/api/v3/cron');
// @TODO: Better way to sync user?
window.location.href = '/';
this.$root.$emit('hide::modal', 'yesterdaily');
},
},

View File

@@ -283,6 +283,10 @@ router.beforeEach(function routerGuard (to, from, next) {
return next({name: to.path === '/' ? 'home' : 'login'});
}
if (isUserLoggedIn && (to.name === 'login' || to.name === 'register')) {
return next({name: 'tasks'});
}
next();
});

View File

@@ -7,7 +7,7 @@ import changeClassOp from 'common/script/ops/changeClass';
import disableClassesOp from 'common/script/ops/disableClasses';
export function fetch (store, forceLoad = false) { // eslint-disable-line no-shadow
export function fetch (store, options = {}) { // eslint-disable-line no-shadow
return loadAsyncResource({
store,
path: 'user',
@@ -15,7 +15,7 @@ export function fetch (store, forceLoad = false) { // eslint-disable-line no-sha
deserialize (response) {
return response.data.data;
},
forceLoad,
forceLoad: options.forceLoad,
});
}

View File

@@ -5,6 +5,7 @@ import * as commonConstants from 'common/script/constants';
import { DAY_MAPPING } from 'common/script/cron';
import { asyncResourceFactory } from 'client/libs/asyncResource';
import axios from 'axios';
import moment from 'moment';
import actions from './actions';
import getters from './getters';
@@ -22,6 +23,10 @@ if (AUTH_SETTINGS) {
AUTH_SETTINGS = JSON.parse(AUTH_SETTINGS);
axios.defaults.headers.common['x-api-user'] = AUTH_SETTINGS.auth.apiId;
axios.defaults.headers.common['x-api-key'] = AUTH_SETTINGS.auth.apiToken;
const timezoneOffset = moment().zone(); // eg, 240 - this will be converted on server as -(offset/60)
axios.defaults.headers.common['x-user-timezoneOffset'] = timezoneOffset;
isUserLoggedIn = true;
}