Maronato / vue-toastification

Vue notifications made easy!
https://vue-toastification.maronato.dev
MIT License
3.03k stars 139 forks source link

Queue Toast when called on SSR #336

Open mrleblanc101 opened 2 years ago

mrleblanc101 commented 2 years ago

Currently $toast is undefined on the server-side. I understand that this library is linked to the DOM, we can't show a Toast on the server-side.

Detailed Description

It would be nice if we could queue some server-side toast that would show on the client side. I currently made a custom implementation of this using beforeNuxtRender to add my message to a queue and check if there is anything in the queue using nuxtState on the client-only plugin.

Context

I think this is a pretty common usage partern for Nuxt/SSR users and should probably be baked-in.

Possible Implementation

Instead of having $toast undefined on the server-side, it should be a mock that add Toast message to the queue. I'm not sure how this would work in Vue 3 as Nuxt currently still doesn't support Vue 3. (Nuxt 3 is in beta but I haven't had time to check it out yet)

mrleblanc101 commented 2 years ago

If this is approved, I could work on a PR. Here's what I did as a workaround currently.

// /middleware/recover-cart.js
export default async function ({ beforeNuxtRender }) {
    try {
        // Some async code
    } catch (e) {
        if(process.server) {
            beforeNuxtRender(({ nuxtState }) => {
                nuxtState.toastQueue = nuxtState.toastQueue || [];
                nuxtState.toastQueue.push(e.response?.body?.message);
            });
        }
    }
}
// /plugins/vue-toastification.client.js
export default function ({ $toast, nuxtState }) {
    if (nuxtState.toastQueue) {
        nuxtState.toastQueue.forEach((m) => $toast.error(m));
    }
}

What I propose would allow to call $toast.error() and $toast.success() dirrectly on the server-side and it would queue the toast for when the plugin is loaded on the client-side.

mrleblanc101 commented 2 years ago

Here is what my final plugin looks like:

export default function ({ app, beforeNuxtRender, nuxtState }, inject) {
    if (process.server) {
        const toastQueue = [];
        const toast = (content, options) => toastQueue.push({ content, options });
        toast.success = (content, options) => toast(content, Object.assign({}, options, { type: TYPE.SUCCESS }));
        toast.info = (content, options) => toast(content, Object.assign({}, options, { type: TYPE.INFO }));
        toast.warning = (content, options) => toast(content, Object.assign({}, options, { type: TYPE.WARNING }));
        toast.error = (content, options) => toast(content, Object.assign({}, options, { type: TYPE.ERROR }));
        beforeNuxtRender(({ nuxtState }) => {
            nuxtState.toastQueue = toastQueue;
        });
        inject('toast', toast);
    }
    if (process.client) {
        if (nuxtState.toastQueue) {
            nuxtState.toastQueue.forEach((toast) => {
                app.$toast(...Object.values(toast));
            });
        }
    }
}