Add a way to resend unsent reports, fix for #14.
This commit is contained in:
parent
79b4625dc9
commit
da7e582cf9
48
src/App.vue
48
src/App.vue
@ -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>
|
||||||
|
@ -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: {
|
||||||
|
42
src/components/ReportErrorModal.vue
Normal file
42
src/components/ReportErrorModal.vue
Normal 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>
|
@ -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;
|
||||||
|
@ -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": {
|
||||||
|
@ -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 });
|
||||||
}
|
}
|
||||||
|
@ -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';
|
||||||
|
@ -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);
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user