Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(useWebNotification): prevent notifications when checking for support #4019

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

9romise
Copy link
Contributor

@9romise 9romise commented Jun 5, 2024

Before submitting the PR, please make sure you do the following

  • Read the Contributing Guidelines.
  • Read the Pull Request Guidelines.
  • Check that there isn't already a PR that solves the problem the same way to avoid creating a duplicate.
  • Provide a description in this PR that addresses what the PR is solving, or reference the issue that it solves (e.g. fixes #123).
  • Ideally, include relevant tests that fail without this PR but pass with it.
⚠️ Slowing down new functions

Warning: Slowing down new functions

As the VueUse audience continues to grow, we have been inundated with an overwhelming number of feature requests and pull requests. As a result, maintaining the project has become increasingly challenging and has stretched our capacity to its limits. As such, in the near future, we may need to slow down our acceptance of new features and prioritize the stability and quality of existing functions. Please note that new features for VueUse may not be accepted at this time. If you have any new ideas, we suggest that you first incorporate them into your own codebase, iterate on them to suit your needs, and assess their generalizability. If you strongly believe that your ideas are beneficial to the community, you may submit a pull request along with your use cases, and we would be happy to review and discuss them. Thank you for your understanding.


Description

fixes #4017

Additional context

@dosubot dosubot bot added the size:XS This PR changes 0-9 lines, ignoring generated files. label Jun 5, 2024
@Alfred-Skyblue
Copy link
Member

In this PR, we are still triggering a notification, which I believe is unreasonable for the users. On stackoverflow, I found that it throws an error when used, and the error message is as follows:

Failed to construct 'Notification': Illegal constructor. Use ServiceWorkerRegistration.showNotification() instead. TypeError: Failed to construct 'Notification': Illegal constructor. Use ServiceWorkerRegistration.showNotification() instead.

I found that when I use new Notification() without passing any parameters, it does not issue a notification, and the error message is as follows:

Failed to construct 'Notification': 1 argument required, but only 0 present.

Can we determine support based on the error message? If the error message contains:

Failed to construct 'Notification': Illegal constructor

Can we then assume that Notification is not supported?

Of course, this is just my guess, as I do not have the corresponding device to test, so I am not sure about the priority of these two errors.

@Alfred-Skyblue
Copy link
Member

Additionally, as of now, it still triggers two notifications because isSupported is a computed property. We trigger the getter at:

const permissionGranted = ref(isSupported.value && 'permission' in Notification && Notification.permission === 'granted')

and

if (!isSupported.value)

This causes new Notification to be executed every time the getter is triggered. However, I believe it shouldn't be a computed property because whether the current environment supports Notification will never change. If isSupported.value is false when the page first loads, then it will always be false in subsequent checks. Therefore, should we cache the result upon the first check and return that in subsequent checks?

@9romise
Copy link
Contributor Author

9romise commented Jun 6, 2024

Can you tell me what environment you still trigger two notifications?
I try to run the following code in the devtool after granting permission from this demo.

// Notifications will show
const notification = new Notification('')

// Notifications will not show
const notification = new Notification('')
notification.onshow = () => {
  notification.close()
}

I also verified the changes in the development environment. Btw, I'm using Chrome under Windows. And vitepress may have a cache and not update to the latest code immediately(I'm having this issue).

@9romise
Copy link
Contributor Author

9romise commented Jun 6, 2024

Additionally, as of now, it still triggers two notifications because isSupported is a computed property. We trigger the getter at:

const permissionGranted = ref(isSupported.value && 'permission' in Notification && Notification.permission === 'granted')

and

if (!isSupported.value)

This causes new Notification to be executed every time the getter is triggered. However, I believe it shouldn't be a computed property because whether the current environment supports Notification will never change. If isSupported.value is false when the page first loads, then it will always be false in subsequent checks. Therefore, should we cache the result upon the first check and return that in subsequent checks?

The fact that computed in useSupported already caches the results.
And useSupported has been used in other methods that need to check isSupported, I think this kind of change should be done together.

@Alfred-Skyblue
Copy link
Member

Sorry, I think I didn't describe it clearly just now. When I initialize, there are two notifications in my notification list. Here is my screen recording:

QQ20240606-151916-HD.mp4

The reason is that isMounted in useSupported triggered the setter:

export function useSupported(callback: () => unknown) {
const isMounted = useMounted()
return computed(() => {
// to trigger the ref
// eslint-disable-next-line no-unused-expressions
isMounted.value
return Boolean(callback())
})
}

@9romise
Copy link
Contributor Author

9romise commented Jun 6, 2024

Thanks for the reminder, but in this PR, I try to close the notification when it shows. And It works for me in Chrome under Windows. So this will not show the notification when computed was triggered. If it shows in your env, I need more info.

const notification = new Notification('')
notification.onshow = () => {
  notification.close()
}
20240606-160954.mp4

Besides, I wonder if it's updated to the latest code, because I had this issue while debugging because of vitepress's caching.

Also, I'll try to use the error type to do this soon, and if it works, I'll submit a new commit in this PR.

@Alfred-Skyblue
Copy link
Member

Alfred-Skyblue commented Jun 6, 2024

I am on macOS and encountered this issue in the Arc browser. It seems that I did not reproduce it in Chrome. I am sure that I have updated to the latest code, as shown in the video:

QQ20240606-170028-HD.mp4

I am sure there is nothing wrong with my browser settings. Even if there is, we don't need to notify it.

@9romise
Copy link
Contributor Author

9romise commented Jun 7, 2024

I tested new Notification() without passing any parameters in Android Chrome. Unfortunately, this error has a higher priority.

Failed to construct 'Notification': 1 argument required, but only 0 present.

@erkstruwe
Copy link

The two issues linked in isSupported's source actually suggest a condition to prevent empty notifications from popping up (https://issues.chromium.org/issues/40415865#comment4):

function isNewNotificationSupported() {
    if (!window.Notification || !Notification.requestPermission)
        return false;
    if (Notification.permission == 'granted')
        throw new Error('You must only call this *before* calling Notification.requestPermission(), otherwise this feature detect would bug the user with an actual notification!');
    try {
        new Notification('');
    } catch (e) {
        if ([e.name](http://e.name/) == 'TypeError')
            return false;
    }
    return true;
}

However, that code throws an error which we can't do in isSupported. @9romise Would it make sense to early-return true if the permission has been granted?

@9romise
Copy link
Contributor Author

9romise commented Jun 28, 2024

The two issues linked in isSupported's source actually suggest a condition to prevent empty notifications from popping up (https://issues.chromium.org/issues/40415865#comment4):

function isNewNotificationSupported() {
    if (!window.Notification || !Notification.requestPermission)
        return false;
    if (Notification.permission == 'granted')
        throw new Error('You must only call this *before* calling Notification.requestPermission(), otherwise this feature detect would bug the user with an actual notification!');
    try {
        new Notification('');
    } catch (e) {
        if ([e.name](http://e.name/) == 'TypeError')
            return false;
    }
    return true;
}

However, that code throws an error which we can't do in isSupported. @9romise Would it make sense to early-return true if the permission has been granted?

Sorry for late, I think this should be the best solution right now. Let's have a try.

@dosubot dosubot bot added size:S This PR changes 10-29 lines, ignoring generated files. and removed size:XS This PR changes 0-9 lines, ignoring generated files. labels Jun 28, 2024
@erkstruwe
Copy link

Until this is merged, this is a workaround:

    if (window.Notification) {
        window.Notification = class Notification extends window.Notification {
            constructor(title: string, options?: NotificationOptions) {
                super(title, options)

                if (!title) {
                    this.onshow = () => {
                        this.close()
                    }
                }
            }
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size:S This PR changes 10-29 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

useWebNotification emitting notification when mounted
3 participants