diff --git a/app/src/components/ApiWaitOverlay.vue b/app/src/components/ApiWaitOverlay.vue index 243d3bda..a4eff136 100644 --- a/app/src/components/ApiWaitOverlay.vue +++ b/app/src/components/ApiWaitOverlay.vue @@ -4,15 +4,37 @@ no-center :show="waiting" > - @@ -25,7 +47,20 @@ export default { name: 'ApiWaitOverlay', computed: { - ...mapGetters(['waiting']) + ...mapGetters(['waiting', 'lastAction']), + + progress () { + const progress = this.lastAction.progress + if (!progress) return null + return { + values: progress, max: progress.reduce((sum, value) => (sum + value), 0) + } + }, + + messages () { + const messages = this.lastAction.messages + return messages.length > 0 ? this.lastAction.messages : null + } } } @@ -35,6 +70,7 @@ export default { position: sticky; top: 5vh; margin: 0 5%; + padding: 3rem 0; @include media-breakpoint-up(md) { margin: 0 10%; @@ -43,4 +79,33 @@ export default { margin: 0 20%; } } + +.card-body { + padding-bottom: 2rem; +} + +.progress { + margin-top: 2rem; +} + +.list-group { + max-height: 50vh; + overflow-y: auto; + + // Hide all message except the last one if the mouse isn't hovering the list group. + &:not(:hover) .list-group-item:not(:last-of-type) { + display: none; + } +} + +.pacman { + animation: back-and-forth 4s linear infinite; + + @keyframes back-and-forth { + 0%, 100% { transform: translateX(-50vw) scale(1); } + 49% { transform: translateX(50vw) scale(1); } + 50% { transform: translateX(50vw) scale(-1); } + 99% { transform: translateX(-50vw) scale(-1); } + } +} diff --git a/app/src/helpers/api.js b/app/src/helpers/api.js index 4f224b9d..64a33f60 100644 --- a/app/src/helpers/api.js +++ b/app/src/helpers/api.js @@ -52,7 +52,7 @@ export function objectToParams (object, { addLocale = false } = {}) { * @return {DigestedResponse} Parsed response's json, response's text or an error. */ export async function handleResponse (response) { - store.commit('UPDATE_WAITING', false) + store.dispatch('SERVER_RESPONDED') if (!response.ok) return handleErrors(response) // FIXME the api should always return json objects const responseText = await response.text() @@ -174,7 +174,7 @@ export default { * @return {Promise} Promise that resolve the api responses as an array. */ post (uri, data = {}) { - store.commit('UPDATE_WAITING', true) + store.dispatch('WAITING_FOR_RESPONSE', [uri, 'POST']) return this.fetch('POST', uri, data).then(handleResponse) }, @@ -186,7 +186,7 @@ export default { * @return {Promise} Promise that resolve the api responses as an array. */ put (uri, data = {}) { - store.commit('UPDATE_WAITING', true) + store.dispatch('WAITING_FOR_RESPONSE', [uri, 'PUT']) return this.fetch('PUT', uri, data).then(handleResponse) }, @@ -198,9 +198,9 @@ export default { * @return {Promise<('ok'|Error)>} Promise that resolve the api responses as an array. */ delete (uri, data = {}) { - store.commit('UPDATE_WAITING', true) + store.dispatch('WAITING_FOR_RESPONSE', [uri, 'DELETE']) return this.fetch('DELETE', uri, data).then(response => { - store.commit('UPDATE_WAITING', false) + store.dispatch('SERVER_RESPONDED') return response.ok ? 'ok' : handleErrors(response) }) } diff --git a/app/src/store/info.js b/app/src/store/info.js index c94d0601..346ddd1d 100644 --- a/app/src/store/info.js +++ b/app/src/store/info.js @@ -1,12 +1,14 @@ +import Vue from 'vue' import api, { timeout } from '@/helpers/api' import router from '@/router' export default { state: { + host: window.location.host, connected: localStorage.getItem('connected') === 'true', yunohost: null, // yunohost app infos: Object {version, repo} waiting: false, - host: window.location.host + history: [] }, mutations: { @@ -21,6 +23,18 @@ export default { 'UPDATE_WAITING' (state, boolean) { state.waiting = boolean + }, + + 'ADD_HISTORY_ENTRY' (state, [uri, method, date]) { + state.history.push({ uri, method, date, messages: [] }) + }, + + 'ADD_MESSAGE' (state, message) { + state.history[state.history.length - 1].messages.push(message) + }, + + 'UPDATE_PROGRESS' (state, progress) { + Vue.set(state.history[state.history.length - 1], 'progress', progress) } }, @@ -80,15 +94,46 @@ export default { }) }, - 'DISPATCH_MESSAGE' (store, message) { - console.log(message) + 'WAITING_FOR_RESPONSE' ({ commit }, [uri, method]) { + commit('UPDATE_WAITING', true) + commit('ADD_HISTORY_ENTRY', [uri, method, Date.now()]) + }, + + 'SERVER_RESPONDED' ({ state, dispatch, commit }) { + if (!state.waiting) return + commit('UPDATE_WAITING', false) + }, + + 'DISPATCH_MESSAGE' ({ commit }, messages) { + const typeToColor = { error: 'danger' } + for (const type in messages) { + const message = { + text: messages[type], + type: type in typeToColor ? typeToColor[type] : type + } + let progressBar = message.text.match(/^\[#*\+*\.*\] > /) + if (progressBar) { + progressBar = progressBar[0] + message.text = message.text.replace(progressBar, '') + const progress = { '#': 0, '+': 0, '.': 0 } + for (const char of progressBar) { + if (char in progress) progress[char] += 1 + } + commit('UPDATE_PROGRESS', Object.values(progress)) + } + if (message.text) { + commit('ADD_MESSAGE', message) + } + } } }, getters: { + host: state => state.host, connected: state => (state.connected), yunohost: state => (state.yunohost), waiting: state => state.waiting, - host: state => state.host + history: state => state.history, + lastAction: state => state.history[state.history.length - 1] } }