aerni / statamic-livewire-forms

Supercharge your Statamic forms with Livewire
https://statamic.com/addons/aerni/livewire-forms
Other
28 stars 2 forks source link

reCAPTCHA script sometimes doesn't run #66

Open godismyjudge95 opened 1 month ago

godismyjudge95 commented 1 month ago

I am having an issue with the callbacks for the reCAPTCHA field not being run so the CAPTCHA fails.

I narrowed it down to these lines: https://github.com/aerni/statamic-livewire-forms/blob/main/resources/views/default/fields/captcha.blade.php#L30-L38

Which appear to not be running. I think this is due to me having lazy enabled on all my forms.

What's interesting is that moving it into the @assets block above it fixes the issue.

Is there any reason why it is not already inside that @assets block? I can submit a PR to move it if there is not.

aerni commented 1 month ago

Have you tried disabling lazy for the forms? Does it work then?

From what I remember, I did have a reason to not moving the script into @assets or @script. I think it was some sort of caching related stuff. Not sure anymore.

aerni commented 2 weeks ago

I took a closer look at this issue.

The issue with lazy Livewire components is that the reCAPTCHA script is trying to initialize the field before it is in the DOM. So we have to explicitly render the reCAPTCHA instead of relying on automatic instantiation.

Here's an updated captcha.blade.php view that uses Alpine to do so. This works for both lazy and non-lazy components. Please let me know if it works for you and I'll release an update.

@formView('messages.display')

@if($field->instructions && $field->instructions_position === 'above')
    @formView('messages.instructions')
@endif

<div
    x-data="grecaptcha"
    id="{{ $field->id }}"
    class="g-recaptcha"
    wire:ignore
    aria-label="{{ $field->id }}-label"
    @if($field->instructions)
        aria-describedby="{{ $field->id }}-instructions"
    @endif
></div>

@if($errors->has($field->key))
    @formView('messages.error')
@elseif($field->instructions && $field->instructions_position === 'below')
    @formView('messages.instructions')
@endif

@assets
    <script>
        window.grecaptchaOnloadCallback = function() {
            window.grecaptchaIsReady = true
        }
    </script>
    <script async defer src="https://www.google.com/recaptcha/api.js?onload=grecaptchaOnloadCallback&render=explicit"></script>
@endassets

@script
    <script>
        Alpine.data('grecaptcha', () => {
            return {
                init() {
                    if (typeof window.grecaptchaIsReady === 'undefined') {
                        return setTimeout(() => this.init(), 100)
                    }

                    grecaptcha.render(this.$el, {
                        'sitekey': '@captchaKey',
                        'callback': (token) => $wire.set('{{ $field->key }}', token),
                        'expired-callback': () => $wire.set('{{ $field->key }}', null),
                    })
                },
            }
        })
    </script>
@endscript
aerni commented 2 weeks ago

Already created a PR as well: https://github.com/aerni/statamic-livewire-forms/pull/69.

godismyjudge95 commented 6 days ago

Sorry I haven't gotten to review the PR yet, I will let you know if it works.