fix feedback widget display

This commit is contained in:
corsacca 2026-05-21 10:49:55 +01:00
parent 383f675d33
commit 08cb99615f
9 changed files with 73 additions and 74 deletions

View file

@ -1,5 +1,37 @@
<script setup lang="ts">
// Site-wide feedback widget. It lives here (outside NuxtLayout) so it renders
// on every layout — public, admin and auth. It stays opted out by default and
// surfaces only when the visitor opts in: via ?feedback=true or by logging in.
// Both set the component's `show-feedback-widget` localStorage flag (login is
// wired in useAuth). The per-page map widgets are separate (FeedbackWidgetSlot,
// anchored inside each map).
useFeedbackScript()
const siteFeedbackConfig = JSON.stringify({
profile: 'chat-bubble',
apiBase: 'https://support.gospelambition.org',
enabled: true,
showByDefault: false,
instanceId: 'fb-site',
projectId: '3c0e8534-f593-4222-8315-31dda4514760'
})
</script>
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
<ClientOnly>
<div class="site-feedback-widget">
<feedback-web-component :profile-config="siteFeedbackConfig" />
</div>
</ClientOnly>
</template>
<style scoped>
.site-feedback-widget {
position: fixed;
right: 16px;
bottom: 16px;
z-index: 2147483647;
}
</style>

View file

@ -1,56 +0,0 @@
<template>
<div class="feedback-widget-slot">
<feedback-web-component :key="String(isLoggedIn)" :profile-config="profileConfig" />
</div>
</template>
<script setup lang="ts">
const { public: pub } = useRuntimeConfig()
const { isLoggedIn } = useAuth()
const profileConfig = computed(() => JSON.stringify({
profile: 'chat-bubble',
apiBase: pub.feedbackApiBase,
enabled: true,
showByDefault: isLoggedIn.value,
instanceId: 'fb-doxa-life-parent',
projectId: pub.feedbackProjectId
}))
// The feedback widget is a ~95 KB third-party script that is not needed for
// first paint. Load it once the browser is idle so it stays off the initial
// load critical path. The <feedback-web-component> below upgrades in place
// when the definition arrives.
const FEEDBACK_SCRIPT = 'https://support.gospelambition.org/js/feedback-web-component.iife.js'
function loadFeedbackScript() {
if (document.querySelector(`script[src="${FEEDBACK_SCRIPT}"]`)) return
const script = document.createElement('script')
script.src = FEEDBACK_SCRIPT
script.async = true
document.head.appendChild(script)
}
onMounted(() => {
if ('requestIdleCallback' in window) {
requestIdleCallback(loadFeedbackScript, { timeout: 3000 })
} else {
setTimeout(loadFeedbackScript, 2000)
}
})
</script>
<template>
<div class="feedback-widget-slot">
<feedback-web-component :profile-config="profileConfig" />
</div>
</template>
<style scoped>
.feedback-widget-slot {
position: fixed;
right: 20px;
bottom: 20px;
z-index: 2147483000;
}
</style>

View file

@ -26,6 +26,17 @@ const clearCache = () => {
}
}
// Opt this browser into the feedback widget — the same `show-feedback-widget`
// flag that ?feedback=true sets. Lets logged-in users see the widget on public,
// edge-cached pages without a /api/auth/me check (we don't run one there).
const enableFeedbackWidget = () => {
if (import.meta.client) {
try {
localStorage.setItem('show-feedback-widget', 'true')
} catch {}
}
}
export const useAuth = () => {
const user = useState('auth.user', () => null as any)
const authReady = useState('auth.ready', () => false)
@ -47,6 +58,7 @@ export const useAuth = () => {
if (response.success) {
user.value = response.user
saveToCache(response.user)
enableFeedbackWidget()
const route = useRoute()
const redirectTo = route.query.redirect as string
@ -112,6 +124,7 @@ export const useAuth = () => {
const response = await $fetch('/api/auth/me') as { user: any }
user.value = response.user
saveToCache(response.user)
enableFeedbackWidget()
return response.user
} catch {
user.value = null

View file

@ -0,0 +1,24 @@
// Idle-loads the third-party feedback web-component bundle. It's a ~95 KB
// script not needed for first paint, so we defer it until the browser is idle
// to keep it off the initial load critical path. Call this from any layout
// that renders a <feedback-web-component>; the in-DOM guard means multiple
// callers still load the bundle only once.
const FEEDBACK_SCRIPT = 'https://support.gospelambition.org/js/feedback-web-component.iife.js'
function loadFeedbackScript() {
if (document.querySelector(`script[src="${FEEDBACK_SCRIPT}"]`)) return
const script = document.createElement('script')
script.src = FEEDBACK_SCRIPT
script.async = true
document.head.appendChild(script)
}
export function useFeedbackScript() {
onMounted(() => {
if ('requestIdleCallback' in window) {
requestIdleCallback(loadFeedbackScript, { timeout: 3000 })
} else {
setTimeout(loadFeedbackScript, 2000)
}
})
}

View file

@ -24,13 +24,6 @@ const preconnectOrigins = computed(() => {
})
const config = useRuntimeConfig()
const siteFeedbackConfig = JSON.stringify({
profile: 'chat-bubble',
apiBase: 'https://support.gospelambition.org',
enabled: true,
instanceId: 'fb-site',
projectId: '3c0e8534-f593-4222-8315-31dda4514760'
})
// Direction attribute per locale (Arabic = rtl)
const currentLocale = computed(() =>
@ -77,23 +70,12 @@ const { isLoading } = useLoadingIndicator()
<slot />
</main>
<TheFooter />
<ClientOnly>
<div class="site-feedback-widget">
<feedback-web-component :profile-config="siteFeedbackConfig" />
</div>
</ClientOnly>
</div>
</template>
<style lang="scss" src="~/assets/styles/main.scss"></style>
<style scoped>
.site-feedback-widget {
position: fixed;
right: 16px;
bottom: 16px;
z-index: 2147483647;
}
.site-main {
transition: opacity 150ms ease-out;
}

View file

@ -33,6 +33,7 @@ const adoptFeedbackConfig = JSON.stringify({
profile: 'chat-bubble',
apiBase: 'https://support.gospelambition.org',
enabled: true,
showByDefault: false,
instanceId: 'fb-adopt-map',
projectId: 'dd1d9144-3da9-4a3b-87e8-7c17d9e94af0'
})

View file

@ -45,6 +45,7 @@ const homeFeedbackConfig = JSON.stringify({
profile: 'chat-bubble',
apiBase: 'https://support.gospelambition.org',
enabled: true,
showByDefault: false,
instanceId: 'fb-home-map',
projectId: '7bb8f5ba-eb45-4933-89de-bc93fcda09b2'
})

View file

@ -31,6 +31,7 @@ const prayFeedbackConfig = JSON.stringify({
profile: 'chat-bubble',
apiBase: 'https://support.gospelambition.org',
enabled: true,
showByDefault: false,
instanceId: 'fb-pray-map',
projectId: '809ee16b-46e2-4bcd-a93d-b7ea0879d93d'
})

View file

@ -36,6 +36,7 @@ const researchFeedbackConfig = JSON.stringify({
profile: 'chat-bubble',
apiBase: 'https://support.gospelambition.org',
enabled: true,
showByDefault: false,
instanceId: 'fb-research-map',
projectId: '1be56abd-60fd-4366-ad4f-178dddef657d'
})