Clean up toasts.

This commit is contained in:
Andrew Lalis 2023-03-27 18:57:31 +02:00
parent 2e1f09c5a6
commit e275c5a044
10 changed files with 90 additions and 62 deletions

View File

@ -10,8 +10,6 @@
const { configure } = require('quasar/wrappers');
const path = require('path');
const { withCtx } = require('vue');
// Load environment variables from different files depending on if we're in development.
let envPath = '.env.production';
if (process.env.NODE_ENV === 'development') {
@ -22,7 +20,7 @@ if (result.error) {
throw result.error;
}
module.exports = configure(function (ctx) {
module.exports = configure(function () {
return {
eslint: {
// fix: true,
@ -123,7 +121,7 @@ module.exports = configure(function (ctx) {
// directives: [],
// Quasar plugins
plugins: ['Notify'],
plugins: ['Notify', 'Dialog'],
},
// animations: 'all', // --- includes all animations

View File

@ -1,3 +1,7 @@
<!--
An item that's meant for the main UI menu, and features a dropdown with some
account-related actions.
-->
<template>
<div class="q-mx-sm">
<q-btn-dropdown

View File

@ -52,6 +52,7 @@ export default {
},
accountPrivate: 'This account is private.',
recentLifts: 'Recent Lifts',
requestedToFollow: 'Requested to follow this user.',
},
userSearchPage: {
searchHint: 'Search for a user'
@ -83,6 +84,11 @@ export default {
save: 'Save',
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: {
logIn: 'Login',
profile: 'Profile',

View File

@ -47,6 +47,7 @@ export default {
},
accountPrivate: 'Dit account is privaat.',
recentLifts: 'Recente liften',
requestedToFollow: 'Gevraagd om deze gebruiker te volgen.',
},
userSearchPage: {
searchHint: 'Zoek een gebruiker'
@ -78,6 +79,11 @@ export default {
save: 'Opslaan',
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: {
logIn: 'Inloggen',
profile: 'Profile',

View File

@ -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() {
// 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;
try {
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) {
console.error(error);
showApiErrorToast(i18n, quasar);
showApiErrorToast(error);
}
});
}
</script>
<style scoped>

View File

@ -101,11 +101,10 @@ import EditablePropertyRow from 'components/EditablePropertyRow.vue';
import {WeightUnit} from 'src/api/main/submission';
import {resolveLocale, supportedLocales} from 'src/i18n';
import {useI18n} from 'vue-i18n';
import {useQuasar} from 'quasar';
import {showApiErrorToast, showSuccessToast, showWarningToast} from 'src/utils';
const route = useRoute();
const router = useRouter();
const quasar = useQuasar();
const authStore = useAuthStore();
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.
const userId = route.params.userId as string;
if (!authStore.user || authStore.user.id !== userId) {
await router.push(`/users/${userId}`);
await router.replace(`/users/${userId}`);
}
personalDetails.value = await api.auth.getMyPersonalDetails(authStore);
@ -157,7 +156,7 @@ async function savePersonalDetails() {
if (error.response && error.response.status === 400) {
console.warn('bad request');
} else {
console.error(error);
showApiErrorToast(error);
}
}
}
@ -189,25 +188,13 @@ async function updatePassword() {
try {
await api.auth.updatePassword(newPassword.value, authStore);
newPassword.value = '';
quasar.notify({
message: i18n.t('userSettingsPage.passwordUpdated'),
type: 'positive',
position: 'top'
});
showSuccessToast('userSettingsPage.passwordUpdated');
} catch (error: any) {
if (error.response && error.response.status === 400) {
newPassword.value = '';
quasar.notify({
message: i18n.t('userSettingsPage.passwordInvalid'),
type: 'warning',
position: 'top'
});
showWarningToast('userSettingsPage.passwordInvalid');
} else {
quasar.notify({
message: i18n.t('generalErrors.apiError'),
type: 'danger',
position: 'top'
});
showApiErrorToast(error);
}
}
}

View File

@ -210,7 +210,7 @@ async function onSubmitted() {
submitButtonLabel.value = i18n.t('gymPage.submitPage.submitFailed');
await sleep(3000);
} else {
showApiErrorToast(i18n, quasar);
showApiErrorToast(error);
}
}
// Otherwise, report the failed submission and give up.
@ -219,7 +219,7 @@ async function onSubmitted() {
await sleep(3000);
}
} catch (error: any) {
showApiErrorToast(i18n, quasar);
showApiErrorToast(error);
} finally {
submitting.value = false;
submitButtonLabel.value = i18n.t('gymPage.submitPage.submit');

View File

@ -63,8 +63,6 @@ import {UserFollowResponse, UserProfile} from 'src/api/main/auth';
import api from 'src/api/main';
import {useRoute} from 'vue-router';
import {useAuthStore} from 'stores/auth-store';
import {useI18n} from 'vue-i18n';
import {useQuasar} from 'quasar';
import {showApiErrorToast, showInfoToast} from 'src/utils';
import PageMenu from 'components/PageMenu.vue';
import UserSubmissionsPage from 'pages/user/UserSubmissionsPage.vue';
@ -75,8 +73,6 @@ import {useDevStore} from 'stores/dev-store';
const route = useRoute();
const authStore = useAuthStore();
const devStore = useDevStore();
const i18n = useI18n();
const quasar = useQuasar();
const profile: Ref<UserProfile | undefined> = ref();
const isOwnUser = ref(false);
@ -102,7 +98,7 @@ async function loadUser(id: string) {
isOwnUser.value = authStore.loggedIn && profile.value.id === authStore.user?.id;
} catch (error: any) {
if (!(error.response && error.response.status === 404)) {
showApiErrorToast(i18n, quasar);
showApiErrorToast(error);
}
}
}
@ -114,10 +110,10 @@ async function followUser() {
if (result === UserFollowResponse.FOLLOWED) {
await loadUser(profile.value.id);
} else if (result === UserFollowResponse.REQUESTED) {
showInfoToast(quasar, 'Requested to follow this user!');
showInfoToast('userPage.requestedToFollow');
}
} catch (error) {
showApiErrorToast(i18n, quasar);
showApiErrorToast(error);
}
}
}
@ -128,7 +124,7 @@ async function unfollowUser() {
await api.auth.unfollowUser(profile.value.id, authStore);
await loadUser(profile.value.id);
} catch (error) {
showApiErrorToast(i18n, quasar);
showApiErrorToast(error);
}
}
}

View File

@ -14,8 +14,6 @@
</template>
<script setup lang="ts">
import {useI18n} from 'vue-i18n';
import {useQuasar} from 'quasar';
import {useAuthStore} from 'stores/auth-store';
import {onMounted, ref, Ref} from 'vue';
import api from 'src/api/main';
@ -30,20 +28,14 @@ interface Props {
}
const props = defineProps<Props>();
const i18n = useI18n();
const quasar = useQuasar();
const authStore = useAuthStore();
const submissions: Ref<ExerciseSubmission[]> = ref([]);
const loader = new InfinitePageLoader(submissions, async paginationOptions => {
try {
return await api.users.getSubmissions(props.userId, authStore, paginationOptions);
} catch (error: any) {
if (error.response) {
showApiErrorToast(i18n, quasar);
} else {
console.log(error);
}
} catch (error) {
showApiErrorToast(error);
}
});

View File

@ -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 function showApiErrorToast(i18n: any, quasar: QVueGlobals) {
function showToast(type: string, messageKey: string) {
const quasar = useQuasar();
const i18n = useI18n();
quasar.notify({
message: i18n.t('generalErrors.apiError'),
type: 'danger',
message: i18n.t(messageKey),
type: type,
position: 'top'
});
}
export function showInfoToast(quasar: QVueGlobals, translatedMessage: string) {
quasar.notify({
message: translatedMessage,
type: 'info',
position: 'top'
});
/**
* Shows a generic API error message toast notification, for when an API call
* fails to return any status. This should only be called within the context of
* a Vue component, as it utilizes some dependencies that are only available
* 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() {