5
votes

I'm building out a notification system and it's sorta working, but sorta not. I have the follow Composition function

const data = reactive({
    notifications: []
});
let notificationKey = 0;

export const useNotification = () => {
    const visibleNotifications = computed(() => {
        return data.notifications.slice().reverse();
    });

    const add = (notification: INotification) => {
        notification.key  = notificationKey++;
        notification.type = notification.type ?? 'success';

        notification.icon      = iconObject[notification.type];
        notification.iconColor = iconColorObject[notification.type];
        data.notifications.push(notification);

        notificationTimer[notification.key] = new Timer(() => {
            remove(notification.key);
        }, notificationTimeout);
    };

    const remove = (notificationKey: number) => {
        const notificationIndex = data.notifications.findIndex(notification => notification?.key === notificationKey);
        if (notificationTimer[notificationKey] !== undefined) {
            notificationTimer[notificationKey].stop();
        }
        if (notificationIndex > -1) {
            data.notifications.splice(notificationIndex, 1);
        }
    };

    const click = (notification: INotification) => {
       /// ... click code 
    };

    return {
        visibleNotifications,
        add,
        remove,
        click
    };
};

This is working (It's been simplified a little bit). Now, I have two entry points in Webpack. In one entry point (auth), I have the following code to load up a Vue Component for showing the Notification

 Promise.all([
    import(/* webpackChunkName: "Vue"*/ 'vue'),
    import(/* webpackChunkName: "@vue/composition-api"*/ '@vue/composition-api'),
    import(/* webpackChunkName: "Notifications"*/'components/Notifications.vue')
]).then((
    [
        { default: Vue },
        { default: VueCompositionAPI },
        { default: Notifications },
    ]) => {
    Vue.use(VueCompositionAPI);

    new Vue({
        render: h => h(Notifications)
    }).$mount('.notification-outer);
});

Now, this all works, and I add in there the following code

import { useNotification } from 'modules/compositionFunctions/notifications';
useNotification().add({
    title  : 'Error',
    message: 'This is an error notification',
    type   : 'error',
});

Then the notification shows as expectednotification image This is all happening inside the "auth" entry point, and the above is all typescript.

Now, if I go to my second entry point (editor), and in an existing JS file enter the following code

import(/* webpackChunkName: "useNotification"*/ 'modules/compositionFunctions/notifications').then(({ useNotification }) => {
    useNotification().add({
        title     : 'Block Deleted',
        message   : 'The block has been deleted',
        buttonText: 'Undo',
        buttonFunction () {
            undoDelete();
        }
    });
});

Then it "works", and by this I mean, all of the code from the useNotification function works. The add method will add it, (if I console log out the reactive property), and after 15000ms, the remove methods happens and I can add logs to show that. HOWEVER, the Vue component never sees this change. If I add a watch in the Vue Component, and log out as we go, the first notification (image above) will make JS log to the console, however, when adding it from the "editor" entry point, it won't do anything.

Vue Component JS

import { useNotification } from 'modules/compositionFunctions/notifications';
import { defineComponent } from '@vue/composition-api';

export default defineComponent({
    name : 'Notifications',
    setup () {
        const { visibleNotifications, remove, click: notificationClick } = useNotification();

        return {
            visibleNotifications,
            remove,
            notificationClick,
        };
    },
    watch: {
        visibleNotifications: (v) => {
            console.log(v);
        }
    }
});

PLEASE, someone, tell me they can help? This is starting to do my head in...

TIA

2
can you provide a demo in codesandbox?? - tuhin47
Can you show code of remove method ? - Krzysztof Kaczyński
@tuhin47 - I don't THINK so? It's a full Webpack setup with multiple endpoints which I don't think is possible in Codepen. - LeeR
@KrzysztofKaczyński I don't think that the remove method is the issue as the data contains has everything in there when I log it out. Added it in the above anyway - LeeR
Could you confirm that the Notifications component is actually rendered somewhere when you're using the editor entry point? Also, could you try putting console logging right at the top of your notifications.ts to confirm that the file only gets pulled in once (to confirm data.notifications is unique). Assuming neither of those is the problem, could you maybe put a stripped back, buildable, runnable version of your application on GitHub so we can try it ourselves? - skirtle

2 Answers

1
votes

Using the code provided in the GitHub repo, I added some console logging immediately before this part:

window.notifications = reactive({
   notifications: []
});

The logging was called twice. Once from entry1.hash.bundle.js and once from entry2.hash.bundle.js.

My understanding of Webpack is limited but it would seem that it is building those two entry-points to be self-contained and isn't expecting you to run both of them on the page at the same time.

I would have thought it would be possible to extract all the notification stuff to its own shared chunk, a bit like the vendors bundle. That said, I'm not really clear why you're using two entry-points rather than two chunks. My understanding is that entry-points should be one-per-page and then you split them up using chunks.

All that said, there is a quick hack to get it working:

if (!window.notifications) {
    window.notifications = reactive({
        notifications: []
    });
}

This ensures that the same array is being shared no matter how many times it gets run.

I should stress that I don't advocate the use of such a hack in production code. It just kicks the Webpack problems further down the road and pollutes the global namespace in the process. It does help to confirm what the problem is though.

0
votes

what i infer from your code is that, your components are tightly coupled. and i suggest that follow a more object oriented approach like user->data->notification->seen and so on. that might need a slight restructuring of your existing code (and or) in database.

i would also suggest you that have a look at firebase (FCM Firebase Cloud Messaging). a notification system. firebase is a google product, can trust their methodology.(not promoting it but the way they implement the notification system is absolutely scalable)

but if its a completely self developed app and does not want to rely on third party, then the next best move is using the "notify on change" method.

a rough overview will be like this.

  • some event in database/app (to which the user needs to be notified) happens
  • trace what the event does to affected users[scope of the event]
  • built a notifier object[some json file] stored in database or plain json, for example notif.json
  • now send that to the user once they are active.

you can check if they are active or not by setInterval() function on client side. and once they are active check the notif.json and if it has a pending notification push it to user and delete that entry from file or DB.

rule of thumb is don't waste time in existing code if its not working, sometimes rewriting in different approach saves more time.