@@ -28,6 +28,8 @@ interface Turnstile {
2828 " error-callback" ? : (error : string ) => void ;
2929 },
3030 ): string | null | undefined ;
31+ reset(widgetId ? : string ): void ;
32+ remove(widgetId ? : string ): void ;
3133}
3234
3335definePageMeta ({
@@ -63,6 +65,8 @@ const formQuery = ref("");
6365const messages = ref <EntitiesMessage []>([]);
6466const totalMessages = ref (0 );
6567const selectedIds = ref <string []>([]);
68+ let turnstileWidgetId: string | null = null ;
69+ let captchaPromise: Promise <string > | null = null ;
6670
6771const page = ref (1 );
6872const itemsPerPage = ref (100 );
@@ -123,23 +127,51 @@ const messageStatusSelectItems = [
123127];
124128
125129function getCaptcha(): Promise <string > {
126- return new Promise <string >((resolve , reject ) => {
130+ if (captchaPromise ) {
131+ return captchaPromise ;
132+ }
133+
134+ captchaPromise = new Promise <string >((resolve , reject ) => {
127135 const turnstile = (window as unknown as { turnstile? : Turnstile })
128136 .turnstile ;
129137 if (! turnstile ) {
130138 resolve (" " );
131139 return ;
132140 }
141+
142+ const resetPromise = () => {
143+ captchaPromise = null ;
144+ };
145+
133146 turnstile .ready (() => {
134- turnstile .render (" #cloudflare-turnstile" , {
135- sitekey: (config .public as Record <string , string >)
136- .cloudflareTurnstileSiteKey ! ,
137- action: " search_messages" ,
138- callback : (token ) => resolve (token ),
139- " error-callback" : (error : string ) => reject (error ),
140- });
147+ const complete = (token : string ) => {
148+ resetPromise ();
149+ resolve (token );
150+ };
151+ const fail = (error : string ) => {
152+ resetPromise ();
153+ reject (error );
154+ };
155+
156+ if (turnstileWidgetId ) {
157+ turnstile .reset (turnstileWidgetId );
158+ return ;
159+ }
160+
161+ turnstileWidgetId =
162+ turnstile .render (" #cloudflare-turnstile" , {
163+ sitekey: (config .public as Record <string , string >)
164+ .cloudflareTurnstileSiteKey ! ,
165+ action: " search_messages" ,
166+ callback: complete ,
167+ " error-callback" : fail ,
168+ }) ?? null ;
141169 });
142170 });
171+
172+ return captchaPromise .finally (() => {
173+ captchaPromise = null ;
174+ });
143175}
144176
145177function parseErrors(error : any ): ErrorMessages {
@@ -329,6 +361,15 @@ onMounted(async () => {
329361 await fetchMessages (true );
330362 }
331363});
364+
365+ onBeforeUnmount (() => {
366+ const turnstile = (window as unknown as { turnstile? : Turnstile })
367+ .turnstile ;
368+ if (turnstile && turnstileWidgetId ) {
369+ turnstile .remove (turnstileWidgetId );
370+ turnstileWidgetId = null ;
371+ }
372+ });
332373 </script >
333374
334375<template >
0 commit comments