Clean up toasts.
This commit is contained in:
parent
2e1f09c5a6
commit
e275c5a044
|
@ -10,8 +10,6 @@
|
||||||
|
|
||||||
const { configure } = require('quasar/wrappers');
|
const { configure } = require('quasar/wrappers');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { withCtx } = require('vue');
|
|
||||||
|
|
||||||
// Load environment variables from different files depending on if we're in development.
|
// Load environment variables from different files depending on if we're in development.
|
||||||
let envPath = '.env.production';
|
let envPath = '.env.production';
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
@ -22,7 +20,7 @@ if (result.error) {
|
||||||
throw result.error;
|
throw result.error;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = configure(function (ctx) {
|
module.exports = configure(function () {
|
||||||
return {
|
return {
|
||||||
eslint: {
|
eslint: {
|
||||||
// fix: true,
|
// fix: true,
|
||||||
|
@ -123,7 +121,7 @@ module.exports = configure(function (ctx) {
|
||||||
// directives: [],
|
// directives: [],
|
||||||
|
|
||||||
// Quasar plugins
|
// Quasar plugins
|
||||||
plugins: ['Notify'],
|
plugins: ['Notify', 'Dialog'],
|
||||||
},
|
},
|
||||||
|
|
||||||
// animations: 'all', // --- includes all animations
|
// animations: 'all', // --- includes all animations
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
<!--
|
||||||
|
An item that's meant for the main UI menu, and features a dropdown with some
|
||||||
|
account-related actions.
|
||||||
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div class="q-mx-sm">
|
<div class="q-mx-sm">
|
||||||
<q-btn-dropdown
|
<q-btn-dropdown
|
||||||
|
|
|
@ -52,6 +52,7 @@ export default {
|
||||||
},
|
},
|
||||||
accountPrivate: 'This account is private.',
|
accountPrivate: 'This account is private.',
|
||||||
recentLifts: 'Recent Lifts',
|
recentLifts: 'Recent Lifts',
|
||||||
|
requestedToFollow: 'Requested to follow this user.',
|
||||||
},
|
},
|
||||||
userSearchPage: {
|
userSearchPage: {
|
||||||
searchHint: 'Search for a user'
|
searchHint: 'Search for a user'
|
||||||
|
@ -83,6 +84,11 @@ export default {
|
||||||
save: 'Save',
|
save: 'Save',
|
||||||
undo: 'Undo'
|
undo: 'Undo'
|
||||||
},
|
},
|
||||||
|
submissionPage: {
|
||||||
|
confirmDeletion: 'Confirm Deletion',
|
||||||
|
confirmDeletionMsg: 'Are you sure you want to delete this submission? It will be removed permanently.',
|
||||||
|
deletionSuccessful: 'Submission deleted successfully.'
|
||||||
|
},
|
||||||
accountMenuItem: {
|
accountMenuItem: {
|
||||||
logIn: 'Login',
|
logIn: 'Login',
|
||||||
profile: 'Profile',
|
profile: 'Profile',
|
||||||
|
|
|
@ -47,6 +47,7 @@ export default {
|
||||||
},
|
},
|
||||||
accountPrivate: 'Dit account is privaat.',
|
accountPrivate: 'Dit account is privaat.',
|
||||||
recentLifts: 'Recente liften',
|
recentLifts: 'Recente liften',
|
||||||
|
requestedToFollow: 'Gevraagd om deze gebruiker te volgen.',
|
||||||
},
|
},
|
||||||
userSearchPage: {
|
userSearchPage: {
|
||||||
searchHint: 'Zoek een gebruiker'
|
searchHint: 'Zoek een gebruiker'
|
||||||
|
@ -78,6 +79,11 @@ export default {
|
||||||
save: 'Opslaan',
|
save: 'Opslaan',
|
||||||
undo: 'Terugzetten'
|
undo: 'Terugzetten'
|
||||||
},
|
},
|
||||||
|
submissionPage: {
|
||||||
|
confirmDeletion: 'Bevestig verwijderen',
|
||||||
|
confirmDeletionMsg: 'Ben je zeker dat je dit submissie willen verwijderen? Het zal permanent verwijderd worden.',
|
||||||
|
deletionSuccessful: 'Submissie succesvol verwijderd.'
|
||||||
|
},
|
||||||
accountMenuItem: {
|
accountMenuItem: {
|
||||||
logIn: 'Inloggen',
|
logIn: 'Inloggen',
|
||||||
profile: 'Profile',
|
profile: 'Profile',
|
||||||
|
|
|
@ -63,16 +63,30 @@ onMounted(async () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a confirmation dialog asking the user if they really want to delete
|
||||||
|
* their submission, and if they say okay, go ahead and delete it, and bring
|
||||||
|
* the user back to their home page that shows all their lifts.
|
||||||
|
*/
|
||||||
async function deleteSubmission() {
|
async function deleteSubmission() {
|
||||||
// TODO: Confirm via a dialog or something before deleting.
|
quasar.dialog({
|
||||||
|
title: i18n.t('submissionPage.confirmDeletion'),
|
||||||
|
message: i18n.t('submissionPage.confirmDeletionMsg'),
|
||||||
|
cancel: true
|
||||||
|
}).onOk(async () => {
|
||||||
if (!submission.value) return;
|
if (!submission.value) return;
|
||||||
try {
|
try {
|
||||||
await api.gyms.submissions.deleteSubmission(submission.value.id, authStore);
|
await api.gyms.submissions.deleteSubmission(submission.value.id, authStore);
|
||||||
await router.push('/');
|
await router.replace(`/users/${submission.value.user.id}`);
|
||||||
|
quasar.notify({
|
||||||
|
message: i18n.t('submissionPage.deletionSuccessful'),
|
||||||
|
position: 'top',
|
||||||
|
color: 'secondary'
|
||||||
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
showApiErrorToast(error);
|
||||||
showApiErrorToast(i18n, quasar);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
@ -101,11 +101,10 @@ import EditablePropertyRow from 'components/EditablePropertyRow.vue';
|
||||||
import {WeightUnit} from 'src/api/main/submission';
|
import {WeightUnit} from 'src/api/main/submission';
|
||||||
import {resolveLocale, supportedLocales} from 'src/i18n';
|
import {resolveLocale, supportedLocales} from 'src/i18n';
|
||||||
import {useI18n} from 'vue-i18n';
|
import {useI18n} from 'vue-i18n';
|
||||||
import {useQuasar} from 'quasar';
|
import {showApiErrorToast, showSuccessToast, showWarningToast} from 'src/utils';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const quasar = useQuasar();
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const i18n = useI18n({useScope: 'global'});
|
const i18n = useI18n({useScope: 'global'});
|
||||||
|
|
||||||
|
@ -121,7 +120,7 @@ onMounted(async () => {
|
||||||
// Redirect away from the page if the user isn't viewing their own settings.
|
// Redirect away from the page if the user isn't viewing their own settings.
|
||||||
const userId = route.params.userId as string;
|
const userId = route.params.userId as string;
|
||||||
if (!authStore.user || authStore.user.id !== userId) {
|
if (!authStore.user || authStore.user.id !== userId) {
|
||||||
await router.push(`/users/${userId}`);
|
await router.replace(`/users/${userId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
personalDetails.value = await api.auth.getMyPersonalDetails(authStore);
|
personalDetails.value = await api.auth.getMyPersonalDetails(authStore);
|
||||||
|
@ -157,7 +156,7 @@ async function savePersonalDetails() {
|
||||||
if (error.response && error.response.status === 400) {
|
if (error.response && error.response.status === 400) {
|
||||||
console.warn('bad request');
|
console.warn('bad request');
|
||||||
} else {
|
} else {
|
||||||
console.error(error);
|
showApiErrorToast(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,25 +188,13 @@ async function updatePassword() {
|
||||||
try {
|
try {
|
||||||
await api.auth.updatePassword(newPassword.value, authStore);
|
await api.auth.updatePassword(newPassword.value, authStore);
|
||||||
newPassword.value = '';
|
newPassword.value = '';
|
||||||
quasar.notify({
|
showSuccessToast('userSettingsPage.passwordUpdated');
|
||||||
message: i18n.t('userSettingsPage.passwordUpdated'),
|
|
||||||
type: 'positive',
|
|
||||||
position: 'top'
|
|
||||||
});
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error.response && error.response.status === 400) {
|
if (error.response && error.response.status === 400) {
|
||||||
newPassword.value = '';
|
newPassword.value = '';
|
||||||
quasar.notify({
|
showWarningToast('userSettingsPage.passwordInvalid');
|
||||||
message: i18n.t('userSettingsPage.passwordInvalid'),
|
|
||||||
type: 'warning',
|
|
||||||
position: 'top'
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
quasar.notify({
|
showApiErrorToast(error);
|
||||||
message: i18n.t('generalErrors.apiError'),
|
|
||||||
type: 'danger',
|
|
||||||
position: 'top'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,7 +210,7 @@ async function onSubmitted() {
|
||||||
submitButtonLabel.value = i18n.t('gymPage.submitPage.submitFailed');
|
submitButtonLabel.value = i18n.t('gymPage.submitPage.submitFailed');
|
||||||
await sleep(3000);
|
await sleep(3000);
|
||||||
} else {
|
} else {
|
||||||
showApiErrorToast(i18n, quasar);
|
showApiErrorToast(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise, report the failed submission and give up.
|
// Otherwise, report the failed submission and give up.
|
||||||
|
@ -219,7 +219,7 @@ async function onSubmitted() {
|
||||||
await sleep(3000);
|
await sleep(3000);
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
showApiErrorToast(i18n, quasar);
|
showApiErrorToast(error);
|
||||||
} finally {
|
} finally {
|
||||||
submitting.value = false;
|
submitting.value = false;
|
||||||
submitButtonLabel.value = i18n.t('gymPage.submitPage.submit');
|
submitButtonLabel.value = i18n.t('gymPage.submitPage.submit');
|
||||||
|
|
|
@ -63,8 +63,6 @@ import {UserFollowResponse, UserProfile} from 'src/api/main/auth';
|
||||||
import api from 'src/api/main';
|
import api from 'src/api/main';
|
||||||
import {useRoute} from 'vue-router';
|
import {useRoute} from 'vue-router';
|
||||||
import {useAuthStore} from 'stores/auth-store';
|
import {useAuthStore} from 'stores/auth-store';
|
||||||
import {useI18n} from 'vue-i18n';
|
|
||||||
import {useQuasar} from 'quasar';
|
|
||||||
import {showApiErrorToast, showInfoToast} from 'src/utils';
|
import {showApiErrorToast, showInfoToast} from 'src/utils';
|
||||||
import PageMenu from 'components/PageMenu.vue';
|
import PageMenu from 'components/PageMenu.vue';
|
||||||
import UserSubmissionsPage from 'pages/user/UserSubmissionsPage.vue';
|
import UserSubmissionsPage from 'pages/user/UserSubmissionsPage.vue';
|
||||||
|
@ -75,8 +73,6 @@ import {useDevStore} from 'stores/dev-store';
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const devStore = useDevStore();
|
const devStore = useDevStore();
|
||||||
const i18n = useI18n();
|
|
||||||
const quasar = useQuasar();
|
|
||||||
|
|
||||||
const profile: Ref<UserProfile | undefined> = ref();
|
const profile: Ref<UserProfile | undefined> = ref();
|
||||||
const isOwnUser = ref(false);
|
const isOwnUser = ref(false);
|
||||||
|
@ -102,7 +98,7 @@ async function loadUser(id: string) {
|
||||||
isOwnUser.value = authStore.loggedIn && profile.value.id === authStore.user?.id;
|
isOwnUser.value = authStore.loggedIn && profile.value.id === authStore.user?.id;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (!(error.response && error.response.status === 404)) {
|
if (!(error.response && error.response.status === 404)) {
|
||||||
showApiErrorToast(i18n, quasar);
|
showApiErrorToast(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,10 +110,10 @@ async function followUser() {
|
||||||
if (result === UserFollowResponse.FOLLOWED) {
|
if (result === UserFollowResponse.FOLLOWED) {
|
||||||
await loadUser(profile.value.id);
|
await loadUser(profile.value.id);
|
||||||
} else if (result === UserFollowResponse.REQUESTED) {
|
} else if (result === UserFollowResponse.REQUESTED) {
|
||||||
showInfoToast(quasar, 'Requested to follow this user!');
|
showInfoToast('userPage.requestedToFollow');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showApiErrorToast(i18n, quasar);
|
showApiErrorToast(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,7 +124,7 @@ async function unfollowUser() {
|
||||||
await api.auth.unfollowUser(profile.value.id, authStore);
|
await api.auth.unfollowUser(profile.value.id, authStore);
|
||||||
await loadUser(profile.value.id);
|
await loadUser(profile.value.id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showApiErrorToast(i18n, quasar);
|
showApiErrorToast(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {useI18n} from 'vue-i18n';
|
|
||||||
import {useQuasar} from 'quasar';
|
|
||||||
import {useAuthStore} from 'stores/auth-store';
|
import {useAuthStore} from 'stores/auth-store';
|
||||||
import {onMounted, ref, Ref} from 'vue';
|
import {onMounted, ref, Ref} from 'vue';
|
||||||
import api from 'src/api/main';
|
import api from 'src/api/main';
|
||||||
|
@ -30,20 +28,14 @@ interface Props {
|
||||||
}
|
}
|
||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
const i18n = useI18n();
|
|
||||||
const quasar = useQuasar();
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
const submissions: Ref<ExerciseSubmission[]> = ref([]);
|
const submissions: Ref<ExerciseSubmission[]> = ref([]);
|
||||||
const loader = new InfinitePageLoader(submissions, async paginationOptions => {
|
const loader = new InfinitePageLoader(submissions, async paginationOptions => {
|
||||||
try {
|
try {
|
||||||
return await api.users.getSubmissions(props.userId, authStore, paginationOptions);
|
return await api.users.getSubmissions(props.userId, authStore, paginationOptions);
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
if (error.response) {
|
showApiErrorToast(error);
|
||||||
showApiErrorToast(i18n, quasar);
|
|
||||||
} else {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,46 @@
|
||||||
import {QVueGlobals} from 'quasar';
|
import {useQuasar} from 'quasar';
|
||||||
|
import {useI18n} from 'vue-i18n';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sleeps for a given number of milliseconds before resolving.
|
||||||
|
* @param ms The milliseconds to sleep for.
|
||||||
|
*/
|
||||||
export const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
export const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
||||||
|
|
||||||
export function showApiErrorToast(i18n: any, quasar: QVueGlobals) {
|
function showToast(type: string, messageKey: string) {
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const i18n = useI18n();
|
||||||
quasar.notify({
|
quasar.notify({
|
||||||
message: i18n.t('generalErrors.apiError'),
|
message: i18n.t(messageKey),
|
||||||
type: 'danger',
|
type: type,
|
||||||
position: 'top'
|
position: 'top'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showInfoToast(quasar: QVueGlobals, translatedMessage: string) {
|
/**
|
||||||
quasar.notify({
|
* Shows a generic API error message toast notification, for when an API call
|
||||||
message: translatedMessage,
|
* fails to return any status. This should only be called within the context of
|
||||||
type: 'info',
|
* a Vue component, as it utilizes some dependencies that are only available
|
||||||
position: 'top'
|
* then.
|
||||||
});
|
* @param error The error to display.
|
||||||
|
*/
|
||||||
|
export function showApiErrorToast(error?: unknown) {
|
||||||
|
showToast('danger', 'generalErrors.apiError');
|
||||||
|
if (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showInfoToast(messageKey: string) {
|
||||||
|
showToast('info', messageKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showSuccessToast(messageKey: string) {
|
||||||
|
showToast('positive', messageKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showWarningToast(messageKey: string) {
|
||||||
|
showToast('warning', messageKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDocumentHeight() {
|
export function getDocumentHeight() {
|
||||||
|
|
Loading…
Reference in New Issue