Skip to content

Commit

Permalink
Merge pull request #3050 from mozilla/nextjs-landing-page
Browse files Browse the repository at this point in the history
Port over the existing landing page
  • Loading branch information
flozia authored May 17, 2023
2 parents 3668956 + 2971d96 commit b246224
Show file tree
Hide file tree
Showing 5 changed files with 263 additions and 333 deletions.
27 changes: 27 additions & 0 deletions public/nextjs_migration/client/js/landing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/** @type {HTMLDivElement} */
// @ts-ignore: We guard against a null value by not calling init():
const landingPartial = document.querySelector("[data-partial='landing']")

if (landingPartial) {
init()
}

async function init () {
const landingForm = landingPartial.querySelector('form.exposure-scan')
if (!(landingForm instanceof HTMLFormElement)) {
return
}
landingForm.addEventListener('submit', (e) => {
e.preventDefault()
const emailField = landingForm.elements.namedItem('email')
const newLocation = new URL(document.location)
newLocation.pathname = '/scan/'
newLocation.hash = `#email=${encodeURIComponent(emailField.value)}`
document.location = newLocation.href
})
landingForm.hidden = false
}
81 changes: 81 additions & 0 deletions public/nextjs_migration/client/js/transitionObserver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// percentage a section that has to be in view in order to appear
const sectionThreshold = 0.1
const queueIntervalDuration = 150

let observers
let queueInterval
// holds the sections so they can appear one after another
const entryQueue = []

function handleShowSection () {
if (!entryQueue.length && queueInterval) {
clearInterval(queueInterval)
queueInterval = null
return
}

const nextEntry = entryQueue.shift()
nextEntry.target.dataset.enterTransition = 'visible'
}

function setQueueInterval () {
queueInterval = setInterval(handleShowSection, queueIntervalDuration)
}

function handleScroll (entries) {
entries.forEach(entry => {
const sectionElement = entry.target
const hasEntered = sectionElement.getAttribute('data-enter-transition') === 'visible'

if (hasEntered) {
return
}

const isInViewport = entry.isIntersecting
if (isInViewport) {
entryQueue.push(entry)
}

if (!queueInterval) {
setQueueInterval()
}
})
}

function init (sections) {
const observer = new IntersectionObserver(handleScroll, {
threshold: sectionThreshold
})

observers = [...sections].map(section => {
section.dataset.enterTransition = 'entering'

observer.observe(section)
return observer
})

setQueueInterval()
}

if (!observers) {
const mediaQuery = window.matchMedia('(prefers-reduced-motion: no-preference)')
const allowMotion = mediaQuery && mediaQuery.matches

const sections = document.querySelectorAll('[data-enter-transition]')

// Don’t hide elements that are marked for entering transitions
// while users set their motion preferences.
mediaQuery.addEventListener('change', () => {
const documentStyle = document.documentElement.style
documentStyle.setProperty('--enter-transition-opacity', '1')
documentStyle.setProperty('--enter-transition-y', '0')
})

if (allowMotion) {
init(sections)
}
}
155 changes: 155 additions & 0 deletions src/app/(nextjs_migration)/(guest)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import Image from "next/image";
import Script from "next/script";
import "../../../client/css/partials/landing.css";

import { getL10n } from "../../functions/server/l10n";

import HeroImage from "../../../client/images/landing-hero@2x.webp";
import LaptopImage from "../../../client/images/landing-laptop@2x.webp";
import LockImage from "../../../client/images/landing-lock@2x.webp";
import MailImage from "../../../client/images/landing-mail@2x.webp";
import NaturePhoneImage from "../../../client/images/landing-nature-phone@2x.webp";

export default function Home() {
const l10n = getL10n();

return (
<div data-partial="landing">
<Script src="/nextjs_migration/client/js/transitionObserver.js" />
<Script src="/nextjs_migration/client/js/landing.js" />
<section className="hero">
<div>
<h1>{l10n.getString("exposure-landing-hero-heading")}</h1>
<p>{l10n.getString("exposure-landing-hero-lead")}</p>
<form hidden className="exposure-scan">
<label htmlFor="scan-email-adddress" className="visually-hidden">
{l10n.getString("exposure-landing-hero-email-label")}
</label>
<input
id="scan-email-address"
name="email"
type="email"
placeholder={l10n.getString(
"exposure-landing-hero-email-placeholder"
)}
required
/>
<button
type="submit"
className="button primary"
data-cta-id="exposure-landing-1"
>
{l10n.getString("exposure-landing-hero-cta-label")}
</button>
</form>
</div>
<figure>
<Image alt="" src={HeroImage} />
</figure>
</section>

<section className="why-use-monitor" data-enter-transition>
<h2>{l10n.getString("why-use-monitor")}</h2>
<p>{l10n.getString("identifying-breaches")}</p>
<ul>
<li>
<h3>{l10n.getString("protect-account")}</h3>
<p>{l10n.getString("protect-account-prevent-hackers")}</p>
</li>
<li>
<h3>{l10n.getString("prevent-fraud")}</h3>
<p>{l10n.getString("prevent-fraud-keep-info")}</p>
</li>
<li>
<h3>{l10n.getString("get-alerts")}</h3>
<p>{l10n.getString("get-alerts-find-out")}</p>
</li>
</ul>
</section>

<section className="how-it-works" data-enter-transition>
<h2>{l10n.getString("how-it-works")}</h2>
<ol>
<li>
<Image alt="" src={LaptopImage} />
<h3>{l10n.getString("check-for-breaches")}</h3>
<p>{l10n.getString("check-for-breaches-we-search")}</p>
</li>
<li>
<Image alt="" src={LockImage} />
<h3>{l10n.getString("protect-accounts")}</h3>
<p>{l10n.getString("protect-accounts-clear-steps")}</p>
</li>
<li>
<Image alt="" src={MailImage} />
<h3>{l10n.getString("alerts-for-breaches")}</h3>
<p>{l10n.getString("alerts-for-breaches-monitor-new")}</p>
</li>
</ol>
</section>

<section className="safe-with-us" data-enter-transition>
<div>
<h2>{l10n.getString("safe-with-us")}</h2>
<p>{l10n.getString("parent-company")}</p>
<p>{l10n.getString("our-mission")}</p>
<p>
<a href="https://www.mozilla.org/mission/" target="_blank">
{l10n.getString("learn-more-mission")}
</a>
</p>
</div>
<figure>
<Image alt="" src={NaturePhoneImage} />
</figure>
</section>

<section className="top-questions-about-monitor" data-enter-transition>
<div>
<h2>{l10n.getString("top-questions-about-monitor")}</h2>
<a
href="https://support.mozilla.org/kb/firefox-monitor-faq"
target="_blank"
>
{l10n.getString("see-all-faq")}
</a>
</div>
<div>
<details>
<summary>{l10n.getString("what-is-breach")}</summary>
<p>{l10n.getString("when-info-exposed")}</p>
</details>
<details>
<summary>{l10n.getString("what-do-i-do")}</summary>
<p>{l10n.getString("visit-monitor-to-learn")}</p>
</details>
<details>
<summary>{l10n.getString("what-gets-exposed")}</summary>
<p>{l10n.getString("depends-on-hackers")}</p>
</details>
</div>
</section>

<section className="see-if-data-breach" data-enter-transition>
<h2>{l10n.getString("see-if-data-breach")}</h2>
<a
className="button primary"
data-cta-id="landing-2"
href="/user/breaches"
>
{l10n.getString("get-started")}
</a>
<p
className="hibp-attribution"
dangerouslySetInnerHTML={{
__html: l10n.getString("hibp-footer-attribution"),
}}
/>
</section>
</div>
);
}
Loading

0 comments on commit b246224

Please sign in to comment.