funkwhale/front/src/components/federation/FetchButton.vue

230 lines
7.0 KiB
Vue

<script setup lang="ts">
import type { BackendError } from '~/types'
import axios from 'axios'
import { useTimeoutFn } from '@vueuse/core'
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import Modal from '~/components/ui/Modal.vue'
import Button from '~/components/ui/Button.vue'
const { t } = useI18n()
interface Events {
(e: 'refresh'): void
}
interface Props {
url: string
}
const emit = defineEmits<Events>()
const props = defineProps<Props>()
const MAX_POLLS = 15
const pollsCount = ref(0)
const showModal = ref(false)
const data = ref()
const errors = ref([] as string[])
const isLoading = ref(false)
const isPolling = ref(false)
const fetch = async () => {
showModal.value = true
isLoading.value = true
isPolling.value = false
errors.value = []
pollsCount.value = 0
data.value = undefined
try {
const response = await axios.post(props.url)
data.value = response.data
startPolling()
} catch (error) {
errors.value = (error as BackendError).backendErrors
}
isLoading.value = false
}
const poll = async () => {
isPolling.value = true
showModal.value = true
try {
const response = await axios.get(`federation/fetches/${data.value?.id}/`)
data.value = response.data
if (response.data.status === 'pending' && pollsCount.value++ < MAX_POLLS) {
startPolling()
}
} catch (error) {
errors.value = (error as BackendError).backendErrors
}
isPolling.value = false
}
const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
</script>
<template>
<Button
secondary
icon="bi-arrow-clockwise"
:loading="isLoading"
low-height
@click="fetch"
>
<div>
<slot />
</div>
<Modal
v-model="showModal"
:title="t('components.federation.FetchButton.header.refresh')"
class="small"
:cancel="t('components.federation.FetchButton.button.close')"
>
<div class="scrolling content">
<template v-if="data && data.status != 'pending'">
<div
v-if="data.status === 'skipped'"
class="ui message"
>
<h4 class="header">
{{ t('components.federation.FetchButton.header.skipped') }}
</h4>
<p>
{{ t('components.federation.FetchButton.description.skipped') }}
</p>
</div>
<div
v-else-if="data.status === 'finished'"
class="ui success message"
>
<h4 class="header">
{{ t('components.federation.FetchButton.header.success') }}
</h4>
<p>
{{ t('components.federation.FetchButton.description.success') }}
</p>
</div>
<div
v-else-if="data.status === 'errored'"
class="ui error message"
>
<h4 class="header">
{{ t('components.federation.FetchButton.header.failure') }}
</h4>
<p>
{{ t('components.federation.FetchButton.description.failure') }}
</p>
<table class="ui very basic collapsing celled table">
<tbody>
<tr>
<td>
{{ t('components.federation.FetchButton.table.error.label.type') }}
</td>
<td>
{{ data.detail.error_code }}
</td>
</tr>
<tr>
<td>
{{ t('components.federation.FetchButton.table.error.label.detail') }}
</td>
<td>
<span v-if="data.detail.error_code === 'http' && data.detail.status_code">
{{ t('components.federation.FetchButton.table.error.value.httpStatus', {status: data.detail.status_code}) }}
</span>
<span v-else-if="['http', 'request'].includes(data.detail.error_code)">
{{ t('components.federation.FetchButton.table.error.value.httpError') }}
</span>
<span v-else-if="data.detail.error_code === 'timeout'">
{{ t('components.federation.FetchButton.table.error.value.timeoutError') }}
</span>
<span v-else-if="data.detail.error_code === 'connection'">
{{ t('components.federation.FetchButton.table.error.value.connectionError') }}
</span>
<span v-else-if="['invalid_json', 'invalid_jsonld', 'missing_jsonld_type'].includes(data.detail.error_code)">
{{ t('components.federation.FetchButton.table.error.value.invalidJsonError') }}
</span>
<span v-else-if="data.detail.error_code === 'validation'">
{{ t('components.federation.FetchButton.table.error.value.invalidAttributesError') }}
</span>
<span v-else-if="data.detail.error_code === 'unhandled'">
{{ t('components.federation.FetchButton.table.error.value.unknownError') }}
</span>
<span v-else>
{{ t('components.federation.FetchButton.table.error.value.unknownError') }}
</span>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<div
v-else-if="isLoading"
class="ui active inverted dimmer"
>
<div class="ui text loader">
{{ t('components.federation.FetchButton.loader.fetchRequest') }}
</div>
</div>
<div
v-else-if="isPolling"
class="ui active inverted dimmer"
>
<div class="ui text loader">
{{ t('components.federation.FetchButton.loader.awaitingResult') }}
</div>
</div>
<div
v-if="errors.length > 0"
role="alert"
class="ui negative message"
>
<h4 class="header">
{{ t('components.federation.FetchButton.header.saveFailure') }}
</h4>
<ul class="list">
<li
v-for="(error, key) in errors"
:key="key"
>
{{ error }}
</li>
</ul>
</div>
<div
v-else-if="data && data.status === 'pending' && pollsCount >= MAX_POLLS"
role="alert"
class="ui warning message"
>
<h4 class="header">
{{ t('components.federation.FetchButton.header.pending') }}
</h4>
<p>
{{ t('components.federation.FetchButton.description.pending') }}
</p>
</div>
</div>
<div class="actions">
<Button
v-if="data && data.status === 'finished'"
@click.prevent="showModal = false; emit('refresh')"
>
{{ t('components.federation.FetchButton.button.reload') }}
</Button>
</div>
</Modal>
</Button>
</template>