Bladeren bron

Merge pull request #4956 from overleaf/jpa-jk-contact-form-de-ng

[web] de-ng contact form

GitOrigin-RevId: 8a92b37163555d6466e4b8c565f1ef490f73d49a
Jakob Ackermann 5 jaren geleden
bovenliggende
commit
7919017118

+ 1 - 1
services/web/app/views/layout-marketing.pug

@@ -121,7 +121,7 @@ html(
 		if (typeof(suppressFooter) == "undefined")
 			include layout/footer-marketing
 
-		!= moduleIncludes("contactModal", locals)
+		!= moduleIncludes("contactModal-marketing", locals)
 
 		block foot-scripts
 			each file in entrypointScripts(entrypoint)

+ 19 - 0
services/web/frontend/js/features/algolia-search/search-wiki.js

@@ -0,0 +1,19 @@
+import _ from 'lodash'
+import AlgoliaSearch from 'algoliasearch'
+import getMeta from '../../utils/meta'
+
+let wikiIdx
+export async function searchWiki(...args) {
+  if (!wikiIdx) {
+    const algoliaConfig = getMeta('ol-algolia')
+    const wikiIndex = _.get(algoliaConfig, 'indexes.wiki')
+    if (wikiIndex) {
+      const client = AlgoliaSearch(algoliaConfig.appId, algoliaConfig.apiKey)
+      wikiIdx = client.initIndex(wikiIndex)
+    }
+  }
+  if (!wikiIdx) {
+    return { hits: [], nbHits: 0, nbPages: 0 }
+  }
+  return wikiIdx.search(...args)
+}

+ 12 - 0
services/web/frontend/js/features/contact-form/index.js

@@ -0,0 +1,12 @@
+import { setupSearch } from './search'
+
+document
+  .querySelectorAll('[data-ol-contact-form-with-search]')
+  .forEach(setupSearch)
+
+document.querySelectorAll('a[ng-click="contactUsModal()"]').forEach(el => {
+  el.addEventListener('click', function (e) {
+    e.preventDefault()
+    $('[data-ol-contact-form-modal]').modal()
+  })
+})

+ 86 - 0
services/web/frontend/js/features/contact-form/search.js

@@ -0,0 +1,86 @@
+import _ from 'lodash'
+import { searchWiki } from '../algolia-search/search-wiki'
+import { sendMB } from '../../infrastructure/event-tracking'
+
+function formatHit(hit) {
+  const pageUnderscored = hit.pageName.replace(/\s/g, '_')
+  const pageSlug = encodeURIComponent(pageUnderscored)
+  const pagePath = hit.kb ? 'how-to' : 'latex'
+
+  let pageAnchor = ''
+  let pageName = hit._highlightResult.pageName.value
+  if (hit.sectionName) {
+    pageAnchor = `#${hit.sectionName.replace(/\s/g, '_')}`
+    pageName += ' - ' + hit.sectionName
+  }
+
+  const url = `/learn/${pagePath}/${pageSlug}${pageAnchor}`
+  return { url, pageName }
+}
+
+export function setupSearch(formEl) {
+  const inputEl = formEl.querySelector('[name="subject"]')
+  const resultsEl = formEl.querySelector('[data-ol-search-results]')
+  const wrapperEl = formEl.querySelector('[data-ol-search-results-wrapper]')
+
+  let lastValue = ''
+  function hideResults() {
+    wrapperEl.setAttribute('hidden', '')
+  }
+  function showResults() {
+    wrapperEl.removeAttribute('hidden')
+  }
+
+  async function handleChange() {
+    const value = inputEl.value
+    if (value === lastValue) return
+    lastValue = value
+    if (value.length < 3) {
+      hideResults()
+      return
+    }
+
+    try {
+      const { hits, nbHits } = await searchWiki(value, {
+        hitsPerPage: 3,
+        typoTolerance: 'strict',
+      })
+      resultsEl.innerText = ''
+
+      for (const hit of hits) {
+        const { url, pageName } = formatHit(hit)
+        const liEl = document.createElement('li')
+
+        const linkEl = document.createElement('a')
+        linkEl.className = 'contact-suggestion-list-item'
+        linkEl.href = url
+        linkEl.target = '_blank'
+        liEl.append(linkEl)
+
+        const contentEl = document.createElement('span')
+        contentEl.innerHTML = pageName
+        linkEl.append(contentEl)
+
+        const iconEl = document.createElement('i')
+        iconEl.className = 'fa fa-angle-right'
+        iconEl.setAttribute('aria-hidden', 'true')
+        linkEl.append(contentEl)
+
+        resultsEl.append(liEl)
+      }
+      if (nbHits > 0) {
+        showResults()
+        sendMB('contact-form-suggestions-shown')
+      } else {
+        hideResults()
+      }
+    } catch (e) {
+      hideResults()
+    }
+  }
+
+  inputEl.addEventListener('input', _.debounce(handleChange, 350))
+
+  // display initial results
+  handleChange()
+}

+ 16 - 4
services/web/frontend/js/features/form-helpers/hydrate-form.js

@@ -26,6 +26,7 @@ function formSubmitHelper(formEl) {
       const captchaResponse = await validateCaptcha(formEl)
 
       const data = await sendFormRequest(formEl, captchaResponse)
+      formEl.dispatchEvent(new Event('sent'))
 
       // Handle redirects. From poking around, this still appears to be the
       // "correct" way of handling redirects with fetch
@@ -110,16 +111,27 @@ function formInflightHelper(el) {
     disabledEl.disabled = false
     toggleDisplay(showWhenInflightEl, showWhenNotInflightEl)
   })
+}
 
-  function toggleDisplay(hideEl, showEl) {
-    hideEl.setAttribute('hidden', '')
-    showEl.removeAttribute('hidden')
-  }
+function formSentHelper(el) {
+  const showWhenPending = el.querySelector('[data-ol-not-sent]')
+  const showWhenDone = el.querySelector('[data-ol-sent]')
+  if (!showWhenDone) return
+
+  el.addEventListener('sent', () => {
+    toggleDisplay(showWhenPending, showWhenDone)
+  })
+}
+
+function toggleDisplay(hideEl, showEl) {
+  hideEl.setAttribute('hidden', '')
+  showEl.removeAttribute('hidden')
 }
 
 export function hydrateForm(el) {
   formSubmitHelper(el)
   formInflightHelper(el)
+  formSentHelper(el)
 }
 
 document.querySelectorAll(`[data-ol-form]`).forEach(form => hydrateForm(form))

+ 1 - 0
services/web/frontend/js/marketing.js

@@ -2,5 +2,6 @@ import './utils/webpack-public-path'
 import 'jquery'
 import 'bootstrap'
 import './features/form-helpers/hydrate-form'
+import './features/contact-form'
 
 $('[data-ol-lang-selector-tooltip]').tooltip({ trigger: 'hover' })