diff --git a/src/resources/js/app.js b/src/resources/js/app.js --- a/src/resources/js/app.js +++ b/src/resources/js/app.js @@ -11,33 +11,13 @@ import SupportForm from '../vue/Widgets/SupportForm' import { Tab } from 'bootstrap' import { loadLangAsync, i18n } from './locale' - -const loader = '
Loading
' +import { clearFormValidation, pick, startLoading, stopLoading } from './utils' const routerState = { afterLogin: null, isLoggedIn: !!localStorage.getItem('token') } -let isLoading = 0 - -// Lock the UI with the 'loading...' element -const startLoading = () => { - isLoading++ - let loading = $('#app > .app-loader').removeClass('fadeOut') - if (!loading.length) { - $('#app').append($(loader)) - } -} - -// Hide "loading" overlay -const stopLoading = () => { - if (isLoading > 0) { - $('#app > .app-loader').addClass('fadeOut') - isLoading--; - } -} - let loadingRoute // Note: This has to be before the app is created @@ -97,11 +77,7 @@ } }, methods: { - // Clear (bootstrap) form validation state - clearFormValidation(form) { - $(form).find('.is-invalid').removeClass('is-invalid') - $(form).find('.invalid-feedback').remove() - }, + clearFormValidation, hasPermission(type) { const key = 'enable' + type.charAt(0).toUpperCase() + type.slice(1) return !!(this.authInfo && this.authInfo.statusInfo[key]) @@ -129,6 +105,9 @@ return false }, + isDegraded() { + return this.authInfo && this.authInfo.isAccountDegraded + }, // Set user state to "logged in" loginUser(response, dashboard, update) { if (!update) { @@ -187,37 +166,9 @@ return `${this.appName}` }, - // Display "loading" overlay inside of the specified element - addLoader(elem, small = true, style = null) { - if (style) { - $(elem).css(style) - } else { - $(elem).css('position', 'relative') - } - - $(elem).append(small ? $(loader).addClass('small') : $(loader)) - }, - // Create an object copy with specified properties only - pick(obj, properties) { - let result = {} - - properties.forEach(prop => { - if (prop in obj) { - result[prop] = obj[prop] - } - }) - - return result - }, - // Remove loader element added in addLoader() - removeLoader(elem) { - $(elem).find('.app-loader').remove() - }, + pick, startLoading, stopLoading, - isLoading() { - return isLoading > 0 - }, tab(e) { e.preventDefault() new Tab(e.target).show() @@ -241,7 +192,7 @@ app.updateBodyClass('error') }, errorHandler(error) { - this.stopLoading() + stopLoading() const status = error.response ? error.response.status : 500 const message = error.response ? error.response.statusText : '' @@ -258,32 +209,6 @@ this.errorPage(status, message) } }, - downloadFile(url, filename) { - // TODO: This might not be a best way for big files as the content - // will be stored (temporarily) in browser memory - // TODO: This method does not show the download progress in the browser - // but it could be implemented in the UI, axios has 'progress' property - axios.get(url, { responseType: 'blob' }) - .then(response => { - const link = document.createElement('a') - - if (!filename) { - const contentDisposition = response.headers['content-disposition'] - filename = 'unknown' - - if (contentDisposition) { - const match = contentDisposition.match(/filename="?(.+)"?/); - if (match && match.length === 2) { - filename = match[1]; - } - } - } - - link.href = window.URL.createObjectURL(response.data) - link.download = filename - link.click() - }) - }, price(price, currency) { // TODO: Set locale argument according to the currently used locale return ((price || 0) / 100).toLocaleString('de-DE', { style: 'currency', currency: currency || 'CHF' }) @@ -303,9 +228,6 @@ $(event.target).closest('tr').find('a').trigger('click') } }, - isDegraded() { - return this.authInfo && this.authInfo.isAccountDegraded - }, pageName(path) { let page = this.$route.path @@ -405,6 +327,11 @@ // on a running application. We need this for browser testing. config.headers['X-Test-Payment-Provider'] = window.config.paymentProvider + let loader = config.loader + if (loader) { + startLoading(loader) + } + return config }, error => { @@ -420,9 +347,19 @@ response.config.onFinish() } + let loader = response.config.loader + if (loader) { + stopLoading(loader) + } + return response }, error => { + let loader = error.config.loader + if (loader) { + stopLoading(loader) + } + // Do not display the error in a toast message, pass the error as-is if (axios.isCancel(error) || error.config.ignoreErrors) { return Promise.reject(error) diff --git a/src/resources/js/utils.js b/src/resources/js/utils.js new file mode 100644 --- /dev/null +++ b/src/resources/js/utils.js @@ -0,0 +1,134 @@ + +/** + * Clear (bootstrap) form validation state + */ +const clearFormValidation = (form) => { + $(form).find('.is-invalid').removeClass('is-invalid') + $(form).find('.invalid-feedback').remove() +} + +/** + * File downloader + */ +const downloadFile = (url, filename) => { + // TODO: This might not be a best way for big files as the content + // will be stored (temporarily) in browser memory + // TODO: This method does not show the download progress in the browser + // but it could be implemented in the UI, axios has 'progress' property + axios.get(url, { responseType: 'blob' }) + .then(response => { + const link = document.createElement('a') + + if (!filename) { + const contentDisposition = response.headers['content-disposition'] + filename = 'unknown' + + if (contentDisposition) { + const match = contentDisposition.match(/filename="?(.+)"?/); + if (match && match.length === 2) { + filename = match[1]; + } + } + } + + link.href = window.URL.createObjectURL(response.data) + link.download = filename + link.click() + }) +} + +/** + * Create an object copy with specified properties only + */ +const pick = (obj, properties) => { + let result = {} + + properties.forEach(prop => { + if (prop in obj) { + result[prop] = obj[prop] + } + }) + + return result +} + +const loader = '
Loading
' + +let isLoading = 0 + +/** + * Display the 'loading...' element, lock the UI + * + * @param array|string|DOMElement|null|bool|jQuery $element Supported input: + * - DOMElement or jQuery collection or selector string: for element-level loader inside + * - array: for element-level loader inside the element specified in the first array element + * - undefined, null or true: for page-level loader + * @param object $style Additional element style + */ +const startLoading = (element, style = null) => { + let small = false + + if (Array.isArray(element)) { + style = element[1] + element = element[0] + } + + if (element && element !== true) { + // The loader inside some page element + small = true + + if (style) { + small = style.small + delete style.small + $(element).css(style) + } else { + $(element).css('position', 'relative') + } + } else { + // The full page loader + isLoading++ + let loading = $('#app > .app-loader').removeClass('fadeOut') + if (loading.length) { + return + } + + element = $('#app') + } + + const loaderElement = $(loader) + + if (small) { + loaderElement.addClass('small') + } + + $(element).append(loaderElement) + + return loaderElement +} + +/** + * Hide the "loading" element + * + * @param array|string|DOMElement|null|bool|jQuery $element + * @see startLoading() + */ +const stopLoading = (element) => { + if (element && element !== true) { + if (Array.isArray(element)) { + element = element[0] + } + + $(element).find('.app-loader').remove() + } else if (isLoading > 0) { + $('#app > .app-loader').addClass('fadeOut') + isLoading--; + } +} + +export { + clearFormValidation, + downloadFile, + pick, + startLoading, + stopLoading +} diff --git a/src/resources/vue/Admin/Distlist.vue b/src/resources/vue/Admin/Distlist.vue --- a/src/resources/vue/Admin/Distlist.vue +++ b/src/resources/vue/Admin/Distlist.vue @@ -83,11 +83,8 @@ } }, created() { - this.$root.startLoading() - - axios.get('/api/v4/groups/' + this.$route.params.list) + axios.get('/api/v4/groups/' + this.$route.params.list, { loader: true }) .then(response => { - this.$root.stopLoading() this.list = response.data }) .catch(this.$root.errorHandler) diff --git a/src/resources/vue/Admin/Resource.vue b/src/resources/vue/Admin/Resource.vue --- a/src/resources/vue/Admin/Resource.vue +++ b/src/resources/vue/Admin/Resource.vue @@ -67,11 +67,8 @@ } }, created() { - this.$root.startLoading() - - axios.get('/api/v4/resources/' + this.$route.params.resource) + axios.get('/api/v4/resources/' + this.$route.params.resource, { loader: true }) .then(response => { - this.$root.stopLoading() this.resource = response.data }) .catch(this.$root.errorHandler) diff --git a/src/resources/vue/Admin/SharedFolder.vue b/src/resources/vue/Admin/SharedFolder.vue --- a/src/resources/vue/Admin/SharedFolder.vue +++ b/src/resources/vue/Admin/SharedFolder.vue @@ -106,11 +106,8 @@ } }, created() { - this.$root.startLoading() - - axios.get('/api/v4/shared-folders/' + this.$route.params.folder) + axios.get('/api/v4/shared-folders/' + this.$route.params.folder, { loader: true }) .then(response => { - this.$root.stopLoading() this.folder = response.data }) .catch(this.$root.errorHandler) diff --git a/src/resources/vue/Admin/Stats.vue b/src/resources/vue/Admin/Stats.vue --- a/src/resources/vue/Admin/Stats.vue +++ b/src/resources/vue/Admin/Stats.vue @@ -28,16 +28,12 @@ loadChart(name) { const chart = $('
').attr({ id: 'chart-' + name }).appendTo(this.$el) - this.$root.addLoader(chart) - - axios.get('/api/v4/stats/chart/' + name) + axios.get('/api/v4/stats/chart/' + name, { loader: chart }) .then(response => { - this.$root.removeLoader(chart) this.drawChart(name, response.data) }) .catch(error => { console.error(error) - this.$root.removeLoader(chart) chart.append($('').text(this.$t('msg.loading-failed'))) }) } diff --git a/src/resources/vue/Admin/User.vue b/src/resources/vue/Admin/User.vue --- a/src/resources/vue/Admin/User.vue +++ b/src/resources/vue/Admin/User.vue @@ -548,15 +548,11 @@ created() { const user_id = this.$route.params.user - this.$root.startLoading() - - axios.get('/api/v4/users/' + user_id) + axios.get('/api/v4/users/' + user_id, { loader: true }) .then(response => { - this.$root.stopLoading() - this.user = response.data - const financesTab = '#user-finances' + const loader = '#user-finances' const keys = ['first_name', 'last_name', 'external_email', 'billing_address', 'phone', 'organization'] let country = this.user.settings.country @@ -573,16 +569,11 @@ // TODO: currencies, multi-wallets, accounts // Get more info about the wallet (e.g. payment provider related) - this.$root.addLoader(financesTab) - axios.get('/api/v4/wallets/' + this.user.wallets[0].id) + axios.get('/api/v4/wallets/' + this.user.wallets[0].id, { loader }) .then(response => { - this.$root.removeLoader(financesTab) this.wallet = response.data this.setMandateState() }) - .catch(error => { - this.$root.removeLoader(financesTab) - }) // Create subscriptions list axios.get('/api/v4/users/' + user_id + '/skus') diff --git a/src/resources/vue/App.vue b/src/resources/vue/App.vue --- a/src/resources/vue/App.vue +++ b/src/resources/vue/App.vue @@ -29,23 +29,22 @@ const token = localStorage.getItem('token') if (token) { - this.$root.startLoading() axios.defaults.headers.common.Authorization = 'Bearer ' + token const post = { refresh_token: localStorage.getItem("refreshToken") } - axios.post('/api/auth/info?refresh=1', post, { ignoreErrors: true }) + axios.post('/api/auth/info?refresh=1', post, { ignoreErrors: true, loader: true }) .then(response => { this.$root.loginUser(response.data, false) - this.$root.stopLoading() - this.isLoading = false }) .catch(error => { - // Release lock on the router-view, otherwise links (e.g. Logout) will not work - this.isLoading = false // Handle the error, on 401 display the logon page this.$root.errorHandler(error) }) + .finally(() => { + // Release lock on the router-view, otherwise links (e.g. Logout) will not work + this.isLoading = false + }) } else { this.isLoading = false } diff --git a/src/resources/vue/CompanionApp.vue b/src/resources/vue/CompanionApp.vue --- a/src/resources/vue/CompanionApp.vue +++ b/src/resources/vue/CompanionApp.vue @@ -63,16 +63,11 @@ } }, mounted() { - this.$root.startLoading() - - axios.get('/api/v4/companion/pairing') + axios.get('/api/v4/companion/pairing', { loading: true }) .then(response => { - this.$root.stopLoading() this.qrcode = response.data.qrcode }) .catch(this.$root.errorHandler) - }, - methods: { - }, + } } diff --git a/src/resources/vue/Distlist/Info.vue b/src/resources/vue/Distlist/Info.vue --- a/src/resources/vue/Distlist/Info.vue +++ b/src/resources/vue/Distlist/Info.vue @@ -93,11 +93,8 @@ this.list_id = this.$route.params.list if (this.list_id != 'new') { - this.$root.startLoading() - - axios.get('/api/v4/groups/' + this.list_id) + axios.get('/api/v4/groups/' + this.list_id, { loader: true }) .then(response => { - this.$root.stopLoading() this.list = response.data this.status = response.data.statusInfo }) diff --git a/src/resources/vue/Distlist/List.vue b/src/resources/vue/Distlist/List.vue --- a/src/resources/vue/Distlist/List.vue +++ b/src/resources/vue/Distlist/List.vue @@ -54,11 +54,8 @@ } }, created() { - this.$root.startLoading() - - axios.get('/api/v4/groups') + axios.get('/api/v4/groups', { loader: true }) .then(response => { - this.$root.stopLoading() this.lists = response.data }) .catch(this.$root.errorHandler) diff --git a/src/resources/vue/Domain/Info.vue b/src/resources/vue/Domain/Info.vue --- a/src/resources/vue/Domain/Info.vue +++ b/src/resources/vue/Domain/Info.vue @@ -145,11 +145,8 @@ this.domain_id = this.$route.params.domain if (this.domain_id !== 'new') { - this.$root.startLoading() - - axios.get('/api/v4/domains/' + this.domain_id) + axios.get('/api/v4/domains/' + this.domain_id, { loader: true }) .then(response => { - this.$root.stopLoading() this.domain = response.data this.spf_whitelist = this.domain.config.spf_whitelist || [] diff --git a/src/resources/vue/Domain/List.vue b/src/resources/vue/Domain/List.vue --- a/src/resources/vue/Domain/List.vue +++ b/src/resources/vue/Domain/List.vue @@ -49,11 +49,8 @@ } }, created() { - this.$root.startLoading() - - axios.get('/api/v4/domains') + axios.get('/api/v4/domains', { loader: true }) .then(response => { - this.$root.stopLoading() this.domains = response.data }) .catch(this.$root.errorHandler) diff --git a/src/resources/vue/File/Info.vue b/src/resources/vue/File/Info.vue --- a/src/resources/vue/File/Info.vue +++ b/src/resources/vue/File/Info.vue @@ -102,11 +102,8 @@ this.fileId = this.$route.params.file - this.$root.startLoading() - - axios.get('/api/v4/files/' + this.fileId) + axios.get('/api/v4/files/' + this.fileId, { loader: true }) .then(response => { - this.$root.stopLoading() this.file = response.data if (this.file.isOwner) { diff --git a/src/resources/vue/File/List.vue b/src/resources/vue/File/List.vue --- a/src/resources/vue/File/List.vue +++ b/src/resources/vue/File/List.vue @@ -93,7 +93,7 @@ fileDownload(file) { // This is not an appropriate method for big files, we can consider // using it still for very small files. - // this.$root.downloadFile('api/v4/files/' + file.id + '?download=1', file.name) + // downloadFile('api/v4/files/' + file.id + '?download=1', file.name) // This method first makes a request to the API to get the download URL (which does not // require authentication) and then use it to download the file. diff --git a/src/resources/vue/Page.vue b/src/resources/vue/Page.vue --- a/src/resources/vue/Page.vue +++ b/src/resources/vue/Page.vue @@ -18,11 +18,8 @@ return } - this.$root.startLoading() - - axios.get('/content/page/' + page, { ignoreErrors: true }) + axios.get('/content/page/' + page, { ignoreErrors: true, loader: true }) .then(response => { - this.$root.stopLoading() this.content = response.data }) .catch(this.$root.errorHandler) diff --git a/src/resources/vue/PasswordReset.vue b/src/resources/vue/PasswordReset.vue --- a/src/resources/vue/PasswordReset.vue +++ b/src/resources/vue/PasswordReset.vue @@ -122,19 +122,17 @@ let params = {} if (bylink === true) { - this.$root.startLoading() params.ignoreErrors = true + params.loader = true } this.$root.clearFormValidation($('#step2 form')) axios.post('/api/auth/password-reset/verify', post, params).then(response => { - this.$root.stopLoading() this.userId = response.data.userId this.displayForm(3, true) }).catch(error => { if (bylink === true) { - this.$root.stopLoading() this.$root.errorPage(404, '', this.$t('password.link-invalid')) } }) diff --git a/src/resources/vue/Resource/Info.vue b/src/resources/vue/Resource/Info.vue --- a/src/resources/vue/Resource/Info.vue +++ b/src/resources/vue/Resource/Info.vue @@ -101,11 +101,8 @@ this.resource_id = this.$route.params.resource if (this.resource_id != 'new') { - this.$root.startLoading() - - axios.get('/api/v4/resources/' + this.resource_id) + axios.get('/api/v4/resources/' + this.resource_id, { loader: true }) .then(response => { - this.$root.stopLoading() this.resource = response.data this.status = response.data.statusInfo @@ -117,11 +114,8 @@ }) .catch(this.$root.errorHandler) } else { - this.$root.startLoading() - - axios.get('/api/v4/domains') + axios.get('/api/v4/domains', { loader: true }) .then(response => { - this.$root.stopLoading() this.domains = response.data this.resource.domain = this.domains[0].namespace }) diff --git a/src/resources/vue/Resource/List.vue b/src/resources/vue/Resource/List.vue --- a/src/resources/vue/Resource/List.vue +++ b/src/resources/vue/Resource/List.vue @@ -54,11 +54,8 @@ } }, created() { - this.$root.startLoading() - - axios.get('/api/v4/resources') + axios.get('/api/v4/resources', { loader: true }) .then(response => { - this.$root.stopLoading() this.resources = response.data }) .catch(this.$root.errorHandler) diff --git a/src/resources/vue/Rooms.vue b/src/resources/vue/Rooms.vue --- a/src/resources/vue/Rooms.vue +++ b/src/resources/vue/Rooms.vue @@ -46,12 +46,8 @@ return } - this.$root.startLoading() - - axios.get('/api/v4/meet/rooms') + axios.get('/api/v4/meet/rooms', { loader: true }) .then(response => { - this.$root.stopLoading() - this.rooms = response.data.list if (response.data.count) { this.roomRoute = '/meet/' + encodeURI(this.rooms[0].name) diff --git a/src/resources/vue/Settings.vue b/src/resources/vue/Settings.vue --- a/src/resources/vue/Settings.vue +++ b/src/resources/vue/Settings.vue @@ -61,12 +61,8 @@ this.wallet = this.$root.authInfo.wallet }, mounted() { - this.$root.startLoading() - - axios.get('/api/v4/password-policy') + axios.get('/api/v4/password-policy', { loader: true }) .then(response => { - this.$root.stopLoading() - if (response.data.list) { this.passwordPolicy = response.data.list this.config = response.data.config diff --git a/src/resources/vue/SharedFolder/Info.vue b/src/resources/vue/SharedFolder/Info.vue --- a/src/resources/vue/SharedFolder/Info.vue +++ b/src/resources/vue/SharedFolder/Info.vue @@ -107,21 +107,15 @@ this.folder_id = this.$route.params.folder if (this.folder_id != 'new') { - this.$root.startLoading() - - axios.get('/api/v4/shared-folders/' + this.folder_id) + axios.get('/api/v4/shared-folders/' + this.folder_id, { loader: true }) .then(response => { - this.$root.stopLoading() this.folder = response.data this.status = response.data.statusInfo }) .catch(this.$root.errorHandler) } else { - this.$root.startLoading() - - axios.get('/api/v4/domains') + axios.get('/api/v4/domains', { loader: true }) .then(response => { - this.$root.stopLoading() this.domains = response.data this.folder.domain = this.domains[0].namespace }) diff --git a/src/resources/vue/SharedFolder/List.vue b/src/resources/vue/SharedFolder/List.vue --- a/src/resources/vue/SharedFolder/List.vue +++ b/src/resources/vue/SharedFolder/List.vue @@ -53,11 +53,8 @@ } }, created() { - this.$root.startLoading() - - axios.get('/api/v4/shared-folders') + axios.get('/api/v4/shared-folders', { loader: true }) .then(response => { - this.$root.stopLoading() this.folders = response.data }) .catch(this.$root.errorHandler) diff --git a/src/resources/vue/Signup.vue b/src/resources/vue/Signup.vue --- a/src/resources/vue/Signup.vue +++ b/src/resources/vue/Signup.vue @@ -138,8 +138,7 @@ let param = this.$route.params.param; if (this.$route.name == 'signup-invite') { - this.$root.startLoading() - axios.get('/api/auth/signup/invitations/' + param) + axios.get('/api/auth/signup/invitations/' + param, { loader: true }) .then(response => { this.invitation = response.data this.login = response.data.login @@ -149,7 +148,6 @@ this.plan = response.data.plan this.is_domain = response.data.is_domain this.setDomain(response.data) - this.$root.stopLoading() this.displayForm(3, true) }) .catch(error => { @@ -185,9 +183,7 @@ // Composes plan selection page step0() { if (!this.plans.length) { - this.$root.startLoading() - axios.get('/api/auth/signup/plans').then(response => { - this.$root.stopLoading() + axios.get('/api/auth/signup/plans', { loader: true }).then(response => { this.plans = response.data.plans }) .catch(error => { diff --git a/src/resources/vue/User/Info.vue b/src/resources/vue/User/Info.vue --- a/src/resources/vue/User/Info.vue +++ b/src/resources/vue/User/Info.vue @@ -174,12 +174,8 @@ this.user_id = this.$route.params.user if (this.user_id !== 'new') { - this.$root.startLoading() - - axios.get('/api/v4/users/' + this.user_id) + axios.get('/api/v4/users/' + this.user_id, { loader: true }) .then(response => { - this.$root.stopLoading() - this.user = response.data this.user.first_name = response.data.settings.first_name this.user.last_name = response.data.settings.last_name @@ -241,16 +237,10 @@ // Note: we use $nextTick() because we have to wait for the HTML elements to exist this.$nextTick().then(() => { if (mode == 'link' && !this.passwordLinkCode) { - const element = $('#password-link') - this.$root.addLoader(element) - axios.post('/api/v4/password-reset/code') + axios.post('/api/v4/password-reset/code', {}, { loader: '#password-link' }) .then(response => { - this.$root.removeLoader(element) this.passwordLinkCode = response.data.short_code + '-' + response.data.code }) - .catch(error => { - this.$root.removeLoader(element) - }) } else if (mode == 'input') { $('#password').focus(); } diff --git a/src/resources/vue/Wallet.vue b/src/resources/vue/Wallet.vue --- a/src/resources/vue/Wallet.vue +++ b/src/resources/vue/Wallet.vue @@ -188,6 +188,7 @@ import { Modal } from 'bootstrap' import TransactionLog from './Widgets/TransactionLog' import PaymentLog from './Widgets/PaymentLog' + import { downloadFile } from '../js/utils' import { library } from '@fortawesome/fontawesome-svg-core' @@ -225,23 +226,14 @@ this.walletId = this.$root.authInfo.wallets[0].id - this.$root.startLoading() - axios.get('/api/v4/wallets/' + this.walletId) + axios.get('/api/v4/wallets/' + this.walletId, { loader: true }) .then(response => { - this.$root.stopLoading() this.wallet = response.data - const receiptsTab = $('#wallet-receipts') - - this.$root.addLoader(receiptsTab) - axios.get('/api/v4/wallets/' + this.walletId + '/receipts') + axios.get('/api/v4/wallets/' + this.walletId + '/receipts', { loader: '#wallet-receipts' }) .then(response => { - this.$root.removeLoader(receiptsTab) this.receipts = response.data.list }) - .catch(error => { - this.$root.removeLoader(receiptsTab) - }) if (this.wallet.provider == 'stripe') { this.stripeInit() @@ -269,20 +261,15 @@ }, methods: { loadMandate() { - const mandate_form = $('#mandate-form') + const loader = '#mandate-form' - this.$root.removeLoader(mandate_form) + this.$root.stopLoading(loader) if (!this.mandate.id || this.mandate.isPending) { - this.$root.addLoader(mandate_form) - axios.get('/api/v4/payments/mandate') + axios.get('/api/v4/payments/mandate', { loader }) .then(response => { - this.$root.removeLoader(mandate_form) this.mandate = response.data }) - .catch(error => { - this.$root.removeLoader(mandate_form) - }) } }, selectPaymentMethod(method) { @@ -384,14 +371,11 @@ this.dialog.show() this.$nextTick().then(() => { - const form = $('#payment-method') const type = nextForm == 'manual' ? 'oneoff' : 'recurring' + const loader = ['#payment-method', { 'min-height': '10em', small: false }] - this.$root.addLoader(form, false, { 'min-height': '10em' }) - - axios.get('/api/v4/payments/methods', { params: { type } }) + axios.get('/api/v4/payments/methods', { params: { type }, loader }) .then(response => { - this.$root.removeLoader(form) this.paymentMethods = response.data }) }) @@ -406,7 +390,7 @@ }, receiptDownload() { const receipt = $('#receipt-id').val() - this.$root.downloadFile('/api/v4/wallets/' + this.walletId + '/receipts/' + receipt) + downloadFile('/api/v4/wallets/' + this.walletId + '/receipts/' + receipt) }, stripeInit() { let script = $('#stripe-script') diff --git a/src/resources/vue/Widgets/ListTools.vue b/src/resources/vue/Widgets/ListTools.vue --- a/src/resources/vue/Widgets/ListTools.vue +++ b/src/resources/vue/Widgets/ListTools.vue @@ -76,26 +76,14 @@ if (!loader.length || get.page == 1) { loader = $(this.$el).find('tfoot td') } + } else { + loader = true } } else { this.currentSearch = null } - if (params && params.init) { - this.$root.startLoading() - } else { - this.$root.addLoader(loader) - } - - const finish = () => { - if (params && params.init) { - this.$root.stopLoading() - } else { - this.$root.removeLoader(loader) - } - } - - axios.get(url, { params: get }) + axios.get(url, { params: get, loader }) .then(response => { // Note: In Vue we can't just use .concat() for (let i in response.data.list) { @@ -104,11 +92,6 @@ this.hasMore = response.data.hasMore this.page = response.data.page || 1 - - finish() - }) - .catch(error => { - finish() }) } } diff --git a/src/resources/vue/Widgets/PackageSelect.vue b/src/resources/vue/Widgets/PackageSelect.vue --- a/src/resources/vue/Widgets/PackageSelect.vue +++ b/src/resources/vue/Widgets/PackageSelect.vue @@ -58,12 +58,8 @@ // assign currency, discount, discount_description of the current user this.$root.userWalletProps(this) - this.$root.startLoading() - - axios.get('/api/v4/packages') + axios.get('/api/v4/packages', { loader: true }) .then(response => { - this.$root.stopLoading() - this.packages = response.data.filter(pkg => { if (this.type == 'domain') { return pkg.isDomain diff --git a/src/resources/vue/Widgets/SubscriptionSelect.vue b/src/resources/vue/Widgets/SubscriptionSelect.vue --- a/src/resources/vue/Widgets/SubscriptionSelect.vue +++ b/src/resources/vue/Widgets/SubscriptionSelect.vue @@ -72,12 +72,8 @@ this.discount_description = this.object.wallet.discount_description } - this.$root.startLoading() - - axios.get('/api/v4/' + this.type + 's/' + this.object.id + '/skus') + axios.get('/api/v4/' + this.type + 's/' + this.object.id + '/skus', { loader: true }) .then(response => { - this.$root.stopLoading() - if (this.readonly) { response.data = response.data.filter(sku => { return sku.id in this.object.skus }) } diff --git a/src/resources/vue/Widgets/TransactionLog.vue b/src/resources/vue/Widgets/TransactionLog.vue --- a/src/resources/vue/Widgets/TransactionLog.vue +++ b/src/resources/vue/Widgets/TransactionLog.vue @@ -58,19 +58,14 @@ let cell = record.find('td.description') let details = $('
    ').appendTo(cell) - this.$root.addLoader(cell) - axios.get('/api/v4/wallets/' + this.walletId + '/transactions' + '?transaction=' + id) + axios.get('/api/v4/wallets/' + this.walletId + '/transactions' + '?transaction=' + id, { loader: cell }) .then(response => { - this.$root.removeLoader(cell) record.find('button').remove() let list = details.find('ul') response.data.list.forEach(elem => { list.append($('
  • ').text(this.description(elem))) }) }) - .catch(error => { - this.$root.removeLoader(cell) - }) }, amount(transaction) { return this.$root.price(transaction.amount, transaction.currency)