Add a way to resend unsent reports, fix for #14.

This commit is contained in:
Lucas Verney 2018-07-26 13:55:08 +02:00
parent 79b4625dc9
commit da7e582cf9
8 changed files with 119 additions and 26 deletions

View File

@ -7,6 +7,23 @@
<v-toolbar-title v-text="title" class="ma-0"></v-toolbar-title> <v-toolbar-title v-text="title" class="ma-0"></v-toolbar-title>
</router-link> </router-link>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-btn
v-if="unsentReportsLength > 0"
icon
role="button"
:aria-label="$t('buttons.uploadUnsents')"
:loading="isSendingReports"
:disabled="isSendingReports"
class="mr-3"
color="orange"
dark
@click="sendUnsentReports"
>
<v-badge color="orange" left>
<span slot="badge">{{ unsentReportsLength }}</span>
<v-icon>cloud_upload</v-icon>
</v-badge>
</v-btn>
<v-menu offset-y class="menu" v-if="$route.name === 'Onboarding' || $route.name === 'Map' || $route.name === 'SharedMap'"> <v-menu offset-y class="menu" v-if="$route.name === 'Onboarding' || $route.name === 'Map' || $route.name === 'SharedMap'">
<v-btn slot="activator" icon role="button" :aria-label="$t('buttons.menu')"> <v-btn slot="activator" icon role="button" :aria-label="$t('buttons.menu')">
<v-icon>more_vert</v-icon> <v-icon>more_vert</v-icon>
@ -31,6 +48,7 @@
</div> </div>
</v-toolbar> </v-toolbar>
<v-content> <v-content>
<ReportErrorModal v-model="hasReportError"></ReportErrorModal>
<ShareMapViewModal v-model="isShareMapViewModalShown"></ShareMapViewModal> <ShareMapViewModal v-model="isShareMapViewModalShown"></ShareMapViewModal>
<router-view></router-view> <router-view></router-view>
</v-content> </v-content>
@ -38,10 +56,14 @@
</template> </template>
<script> <script>
import { DELAY_BETWEEN_API_BATCH_REQUESTS } from '@/constants';
import ReportErrorModal from '@/components/ReportErrorModal.vue';
import ShareMapViewModal from '@/components/ShareMapViewModal.vue'; import ShareMapViewModal from '@/components/ShareMapViewModal.vue';
export default { export default {
components: { components: {
ReportErrorModal,
ShareMapViewModal, ShareMapViewModal,
}, },
computed: { computed: {
@ -51,9 +73,14 @@ export default {
isShareMapViewMenuEntryVisible() { isShareMapViewMenuEntryVisible() {
return this.$store.state.map.center.every(item => item !== null); return this.$store.state.map.center.every(item => item !== null);
}, },
unsentReportsLength() {
return this.$store.state.unsentReports.length;
},
}, },
data() { data() {
return { return {
hasReportError: false,
isSendingReports: false,
isShareMapViewModalShown: false, isShareMapViewModalShown: false,
title: "Cycl'Assist", title: "Cycl'Assist",
}; };
@ -68,6 +95,27 @@ export default {
goToSettings() { goToSettings() {
this.$router.push({ name: 'Settings' }); this.$router.push({ name: 'Settings' });
}, },
sendUnsentReports() {
if (!this.unsentReportsLength) {
this.isSendingReports = false;
return;
}
this.isSendingReports = true;
const index = this.unsentReportsLength - 1;
const report = this.$store.state.unsentReports[index];
this.$store.dispatch('saveReport', report)
.then(() => {
this.$store.dispatch('removeUnsentReport', { index });
this.hasReportError = false;
setTimeout(this.sendUnsentReports, DELAY_BETWEEN_API_BATCH_REQUESTS);
})
.catch((exc) => {
console.error(exc);
this.hasReportError = true;
this.isSendingReports = false;
});
},
}, },
}; };
</script> </script>

View File

@ -1,33 +1,19 @@
<template> <template>
<div> <div>
<Modal v-model="hasError"> <ReportErrorModal v-model="hasError"></ReportErrorModal>
<v-card>
<v-card-title class="subheading">{{ $t('reportDialog.unableToSendTitle') }}</v-card-title>
<v-card-text>{{ $t('reportDialog.unableToSendDescription') }} </v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="red darken-1"
@click="hasError = false"
dark
large
role="button"
>
{{ $t('misc.discard') }}
</v-btn>
<v-spacer></v-spacer>
</v-card-actions>
</v-card>
</Modal>
<v-bottom-sheet v-model="isActive"> <v-bottom-sheet v-model="isActive">
<v-card> <v-card>
<v-container fluid> <v-container fluid>
<v-layout row wrap> <v-layout row wrap>
<ReportTile v-for="type in REPORT_TYPES_ORDER" :type="type" :imageSrc="REPORT_TYPES[type].image" :label="$t(REPORT_TYPES[type].label)" :save="saveReport" :key="type"></ReportTile> <ReportTile
v-for="type in REPORT_TYPES_ORDER"
:type="type"
:imageSrc="REPORT_TYPES[type].image"
:label="$t(REPORT_TYPES[type].label)"
:save="saveReport"
:key="type"
>
</ReportTile>
</v-layout> </v-layout>
</v-container> </v-container>
</v-card> </v-card>
@ -38,7 +24,7 @@
<script> <script>
import { REPORT_TYPES, REPORT_TYPES_ORDER } from '@/constants'; import { REPORT_TYPES, REPORT_TYPES_ORDER } from '@/constants';
import Modal from '@/components/Modal.vue'; import ReportErrorModal from '@/components/ReportErrorModal.vue';
import ReportTile from './ReportTile.vue'; import ReportTile from './ReportTile.vue';
export default { export default {
@ -46,7 +32,7 @@ export default {
window.removeEventListener('keydown', this.hideReportDialogOnEsc); window.removeEventListener('keydown', this.hideReportDialogOnEsc);
}, },
components: { components: {
Modal, ReportErrorModal,
ReportTile, ReportTile,
}, },
computed: { computed: {

View File

@ -0,0 +1,42 @@
<template>
<Modal v-model="hasError">
<v-card>
<v-card-title class="subheading">{{ $t('reportDialog.unableToSendTitle') }}</v-card-title>
<v-card-text>{{ $t('reportDialog.unableToSendDescription') }} </v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn @click="hasError = false" large role="button">
{{ $t('misc.ok') }}
</v-btn>
<v-spacer></v-spacer>
</v-card-actions>
</v-card>
</Modal>
</template>
<script>
import Modal from '@/components/Modal.vue';
export default {
components: {
Modal,
},
computed: {
hasError: {
get() {
return this.value;
},
set(val) {
this.$emit('input', val);
},
},
},
props: {
value: Boolean,
},
};
</script>

View File

@ -139,3 +139,7 @@ export const TILE_SERVERS = {
export const DEFAULT_TILE_SERVER = 'cartodb-voyager'; export const DEFAULT_TILE_SERVER = 'cartodb-voyager';
export const GEOCODING_API_ENDPOINT = 'https://api-adresse.data.gouv.fr/search/'; export const GEOCODING_API_ENDPOINT = 'https://api-adresse.data.gouv.fr/search/';
// Delay in milliseconds between two consecutive calls to the backend API when
// doing batch requests
export const DELAY_BETWEEN_API_BATCH_REQUESTS = 1000;

View File

@ -14,6 +14,7 @@
"menu": "Menu", "menu": "Menu",
"recenterMap": "Recenter map", "recenterMap": "Recenter map",
"reportProblem": "Report problem", "reportProblem": "Report problem",
"uploadUnsents": "Upload unsent reports",
"upvote": "Upvote" "upvote": "Upvote"
}, },
"geolocation": { "geolocation": {

View File

@ -10,6 +10,7 @@ import {
IS_LOADING, IS_LOADING,
PUSH_REPORT, PUSH_REPORT,
PUSH_UNSENT_REPORT, PUSH_UNSENT_REPORT,
REMOVE_UNSENT_REPORT,
SET_CURRENT_MAP_CENTER, SET_CURRENT_MAP_CENTER,
SET_CURRENT_MAP_ZOOM, SET_CURRENT_MAP_ZOOM,
SET_CURRENT_POSITION, SET_CURRENT_POSITION,
@ -48,6 +49,10 @@ export function saveUnsentReport({ commit }, { report }) {
commit(PUSH_UNSENT_REPORT, { report }); commit(PUSH_UNSENT_REPORT, { report });
} }
export function removeUnsentReport({ commit }, { index }) {
commit(REMOVE_UNSENT_REPORT, { index });
}
export function hideReportDetails({ commit }) { export function hideReportDetails({ commit }) {
return commit(SHOW_REPORT_DETAILS, { id: null, userAsked: null }); return commit(SHOW_REPORT_DETAILS, { id: null, userAsked: null });
} }

View File

@ -3,6 +3,7 @@ export const IS_DONE_LOADING = 'IS_DONE_LOADING';
export const IS_LOADING = 'IS_LOADING'; export const IS_LOADING = 'IS_LOADING';
export const PUSH_REPORT = 'PUSH_REPORT'; export const PUSH_REPORT = 'PUSH_REPORT';
export const PUSH_UNSENT_REPORT = 'PUSH_UNSENT_REPORT'; export const PUSH_UNSENT_REPORT = 'PUSH_UNSENT_REPORT';
export const REMOVE_UNSENT_REPORT = 'REMOVE_UNSENT_REPORT';
export const SET_CURRENT_MAP_CENTER = 'SET_CURRENT_MAP_CENTER'; export const SET_CURRENT_MAP_CENTER = 'SET_CURRENT_MAP_CENTER';
export const SET_CURRENT_MAP_ZOOM = 'SET_CURRENT_MAP_ZOOM'; export const SET_CURRENT_MAP_ZOOM = 'SET_CURRENT_MAP_ZOOM';
export const SET_CURRENT_POSITION = 'SET_CURRENT_POSITION'; export const SET_CURRENT_POSITION = 'SET_CURRENT_POSITION';

View File

@ -110,6 +110,12 @@ export const mutations = {
localStorage.setItem('unsentReports', JSON.stringify(state.unsentReports)); localStorage.setItem('unsentReports', JSON.stringify(state.unsentReports));
} }
}, },
[types.REMOVE_UNSENT_REPORT](state, { index }) {
state.unsentReports.splice(index, 1);
if (storageAvailable('localStorage')) {
localStorage.setItem('unsentReports', JSON.stringify(state.unsentReports));
}
},
[types.SET_CURRENT_MAP_CENTER](state, { center }) { [types.SET_CURRENT_MAP_CENTER](state, { center }) {
Vue.set(state.map, 'center', center); Vue.set(state.map, 'center', center);
}, },