Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add layout for authenticated pages #3058

Merged
merged 15 commits into from
May 25, 2023
Merged

Add layout for authenticated pages #3058

merged 15 commits into from
May 25, 2023

Conversation

flozia
Copy link
Collaborator

@flozia flozia commented May 19, 2023

Description

Adds the layout for pages that we use for authenticated pages.

Screenshot

pr-authenticated-layout

How to test

  • Auth with FxA visiting /oauth/init
  • Visit /user/breaches and make sure the user menu shows the current users data

Checklist (Definition of Done)

  • Localization strings (if needed) have been added.
  • Commits in this PR are minimal and have descriptive commit messages.
  • I've added or updated the relevant sections in readme and/or code comments
  • I've added a unit test to test for potential regressions of this bug.
  • Product Owner accepted the User Story (demo of functionality completed) or waived the privilege.
  • All acceptance criteria are met.
  • Jira ticket has been updated (if needed) to match changes made during the development process.
  • Jira ticket has been updated (if needed) with suggestions for QA when this PR is deployed to stage.

@flozia flozia requested review from Vinnl and rhelmer May 19, 2023 14:09
Copy link
Collaborator

@Vinnl Vinnl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for figuring this out! That said, let's not merge this yet, since it includes #3051 which is probably not the right approach. But once we've figured out auth, we can based the followup work on this.

(Alternatively, we can remove the commit from that PR from this PR?)

@flozia flozia marked this pull request as draft May 19, 2023 14:58
@flozia
Copy link
Collaborator Author

flozia commented May 19, 2023

Thanks for figuring this out! That said, let's not merge this yet, since it includes #3051 which is probably not the right approach. But once we've figured out auth, we can based the followup work on this.

(Alternatively, we can remove the commit from that PR from this PR?)

Waiting for deciding on how to handle auth sounds good to me — marked this PR as “draft”.

</div>
</a>
</li>
<hr />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Not sure this is valid. I ran the following snippet through https://validator.w3.org/nu/#textarea and it threw the following error:

Error: Element hr not allowed as child of element menu in this context. (Suppressing further errors from this subtree.)

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>OK, Computer</title>
  </head>

  <body>
    <menu>
      <li>One</li>
      <li>Two</li>
      <hr />
      <li>Four</li>
    </menu>
  </body>
</html>

Copy link
Collaborator Author

@flozia flozia May 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. Thanks for catching this one. This right now is how we have it on the existing Monitor site — I’ll replace it because invalid syntax is really not optimal. Otherwise, the JSX for the pages is almost one-to-one taken from the existing string literals as a first step to replace the existing architecture with Next.

Seems like moving it does the trick: d98c919.

<li tabIndex={1}>
<a
href={AppConstants.FXA_SETTINGS_URL}
target="_blank"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to add rel="noreferrer noopener" to these external links, or is that handled elsewhere in headers, etc?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that noopener is implied when using _blank now: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/noopener - it's not harmful but not necessary in modern browsers to avoid reverse-tabnapping etc. if the FxA server was compromised.

I think we probably do want to send the referrer to FxA though.

className="user-menu-button"
title={l10n.getString("menu-button-title")}
>
<img src={fxaUser?.avatar} alt={l10n.getString("menu-button-alt")} />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Should this be a vanilla <img> or Next <Image>?
Also, what's the ?. check in the src attribute? Does that imply that we might not actually have an avatar image and if so, we'd get something like an empty src="" or something like src="undefined" if somehow the fxaUser was falsey?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess follow up question, do we need a width= and/or height= attribute on the image (whichever version we go with) to avoid potential CLS?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I have the same question re: Image, one of the benefits to switching in the ADR was to get things like the automatic image optimization without having to do the way we do it now.

If however the approach here is to first convert from existing templates -> jsx with a minimum of changes and then later convert to Next components I think that's a fine approach, but we should be explicit if so.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, looks like the only usage of <img /> tag in src/app/(nextjs_migration/** directory:

git grep -n "<img " src/app/\(nextjs_migration\) | cat

src/app/(nextjs_migration)/(authenticated)/user/layout.tsx:61:          <img src={fxaUser?.avatar} alt={l10n.getString("menu-button-alt")} />

It's been a while since I've looked at Next.js linting, so I might need to read up on https://nextjs.org/docs/pages/building-your-application/configuring/eslint and see what the latest recommendations are.

"Well, actually…", it looks like running npx next lint flags this, fascinating!

npx next lint

./src/app/(nextjs_migration)/(authenticated)/user/layout.tsx
61:11  Warning: Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element  @next/next/no-img-element

...

NOTE: And for anybody else odd enough to try getting into piping and grepping next lint output, I had to tweak the following to disable colors (since --no-color was --no-bueno, and they seem to be writing to STDERR instead of STDOUT):

FORCE_COLOR=0 npx next lint &> lint.log

Took a bit of poking around https://github.com/vercel/next.js/blob/canary/packages/next/src/cli/next-lint.ts and https://github.com/chalk/chalk to piece that together.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the awesome grep reference!

<div className="pages-nav">
<a
href="/user/breaches"
className={`nav-item ${isBreachesPage ? "current" : ""}`}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: not an endorsement one way or the other, but in FxA/SubPlat, we've been using https://www.npmjs.com/package/classnames and it seems to be a nice enhancement.

className={ classNames("nav-item", { current: isBreachesPage }) }

Same w/ L198 below, if we want to add the extra dependency (which might already be in the node_modules anyways):

className={`nav-item ${isSettingsPage ? "current" : ""}`}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like classNames — I’ll bring it up with the other engineers.

href="https://relay.firefox.com/?utm_medium=mozilla-websites&utm_source=monitor&utm_campaign=&utm_content=nav-bar-global"
target="_blank"
>
<Image alt={l10n.getString("brand-relay")} src={RelayLogo} />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed below (L237) for the Mozilla logo we added loading="lazy" attribute. Not sure if we need that here (and possibly L224 for the VPN logo).

Comment on lines 97 to 101
{new Date(props.breach.AddedDate).toLocaleString(getLocale(), {
year: "numeric",
month: "long",
day: "numeric",
})}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like the date localizer should possibly be in a helper. I think I've seen this pattern 2-3+ other places in the codebase too, so might be nice to just have a single helper that formats a date using a specified locale.

git grep -En "day: ['\"]numeric['\"]" | cat

public/nextjs_migration/client/js/scan.js:118:  newCard.querySelector('.exposure-scan-breach-added dd').textContent = new Date(breach.AddedDate).toLocaleString(navigator.languages, { year: 'numeric', month: 'long', day: 'numeric' })
src/app/(nextjs_migration)/(guest)/breaches/layout.tsx:100:                day: "numeric",
src/app/(nextjs_migration)/(guest)/breaches/page.tsx:77:            <dd>{new Date(props.breach.AddedDate).toLocaleString(getLocale(), { year: 'numeric', month: 'long', day: 'numeric' })}</dd>
src/client/js/partials/exposureScan.js:118:  newCard.querySelector('.exposure-scan-breach-added dd').textContent = new Date(breach.AddedDate).toLocaleString(navigator.languages, { year: 'numeric', month: 'long', day: 'numeric' })
src/utils/formatDate.js:7:  const options = { year: 'numeric', month: 'long', day: 'numeric' }
src/views/partials/allBreaches.js:19:          <dd>${new Date(breach.AddedDate).toLocaleString(getLocale(), { year: 'numeric', month: 'long', day: 'numeric' })}</dd>
src/views/partials/breachDetail.js:152:          breachDate: data.breach.BreachDate.toLocaleString(getLocale(), { year: 'numeric', month: 'long', day: 'numeric' }),
src/views/partials/breachDetail.js:154:          addedDate: data.breach.AddedDate.toLocaleString(getLocale(), { year: 'numeric', month: 'long', day: 'numeric' })

Actually, we might already have said helper function in https://github.com/mozilla/blurts-server/blob/main/src/utils/formatDate.js.

function formatDate (date, locales) {
const jsDate = new Date(date)
const options = { year: 'numeric', month: 'long', day: 'numeric' }
const intlDateTimeFormatter = new Intl.DateTimeFormat(locales, options)
return intlDateTimeFormatter.format(jsDate)
}
export { formatDate }

Comment on lines 126 to 129
return new Intl.ListFormat(getLocale(), {
type: "unit",
style: "short",
}).format(list);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍

cookieName: "monitor-iron-session",
password: process.env.IRON_SESSION_PASSPHRASE!,
cookieOptions: {
secure: process.env.NODE_ENV !== "development",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we need some helper to determine whether we're in a specific environment. I noticed we seem to do that quite often. Where we're checking if we're in development, NOT development, heroku, NOT production, or one suspicious check against "dev" (not "development").

git grep -En "process.env.NODE_ENV \!?={2,3}" | cat

next.config.js:19:              `script-src 'self' ${process.env.NODE_ENV === 'development' ? "'unsafe-eval' 'unsafe-inline'" : ''} https://*.googletagmanager.com`,
next.config.js:21:              `connect-src 'self' ${process.env.NODE_ENV === 'development' ? 'webpack://*' : ''} https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com`,
next.config.js:24:              `style-src 'self' ${process.env.NODE_ENV === 'development' ? "'unsafe-inline'" : ''}`,
src/app/functions/server/sessionHelpers.ts:19:    secure: process.env.NODE_ENV !== "development",
src/appConstants.js:60:if (!process.env.SERVER_URL && process.env.NODE_ENV === 'heroku') {
src/middleware/error.js:41:    message: process.env.NODE_ENV !== 'production' ? errMsg : 'Something went wrong', // hide error message when in production
src/middleware/error.js:42:    stack: process.env.NODE_ENV === 'dev' ? err.stack : {} // hide stack when not in dev

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that dev one seems suspicious. I’ll take a look!

Comment on lines +15 to +16
title: "Create Next App",
description: "Generated by create next app",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these user facing at all? (ie: getting somehow shimmed into <meta> tags?)
They look a bit suspicious.

Copy link
Collaborator

@rhelmer rhelmer May 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe these are intended for meta tags so they will show up in social media and search engines and we do indeed want to set them appropriately.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, think I found some useful docs on this… https://nextjs.org/docs/app/api-reference/functions/generate-metadata 👀

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, these are user-facing. We’ll have to port over the meta tags from the current Monitor site for which we have a separate task. :)

'use client'

import { signIn, useSession } from "next-auth/react";
"use client";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* 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/. */

"use client";
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, this would not need to be a client-side component …

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was confused for a bit why it couldn't be (I'm still getting used to the distinction as well), but I think it's fine. We basically want this component to update whenever the user changes pages, which means we need code to run on the client-side - hence client components. Client components will still be prerendered on the server side; the differences is that additionally, code will also be shipped to the client-side to re-render it in response to user changes.

I found this comment helpful.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, that makes sense. Thanks for the link to the comment!

src/app/components/client/SiteNavigation.tsx Outdated Show resolved Hide resolved
};

export const UserMenu = (props: Props) => {
export const UserMenu = ({ session, fxaSettingsUrl }: Props) => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We’ll have to decide how we handle duplicate components best: The one we need/would like to have for the current site and then new ones that will be built in a “React way” for Premium.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest (like in a different comment in this PR) to keep everything that's directly copied over from the "old" site inside the (nextjs_migration) directory, and create new versions of these components outside of them. I'd like to avoid applying the global stylesheet from the old website inside the new one, and would rather incrementally recreate them using CSS modules.

So all the components in this PR that are inside /components/client would be moved, since they won't look right in the parts of the website we do "the React way".

@flozia flozia marked this pull request as ready for review May 24, 2023 16:44
Copy link
Collaborator

@Vinnl Vinnl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good and working well! I'd like to keep all the "ported" code together, but if you change that and remove the layout.tsx that I think was inadvertently added, we can merge this and start cranking out the auth'd pages!

* 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/. */

"use client";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was confused for a bit why it couldn't be (I'm still getting used to the distinction as well), but I think it's fine. We basically want this component to update whenever the user changes pages, which means we need code to run on the client-side - hence client components. Client components will still be prerendered on the server side; the differences is that additionally, code will also be shipped to the client-side to re-render it in response to user changes.

I found this comment helpful.

Comment on lines 14 to 15
import { UserMenu } from "../../../components/client/UserMenu";
import { SiteNavigation } from "../../../components/client/SiteNavigation";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we maybe move these components into the (nextjs_migration) folder as well? Just to indicate that we probably don't want to use them as-is on proper React pages.

I'm still not sure exactly where we'll land in the end when it comes to where to save components (i.e. do we keep them inside of /src/app/?), but that'll probably be easier to figure out when we get rid of a lot of the old folders, and moving them is easy with TypeScript anyway.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved them into nextjs_migration with 5a5c222.

href="https://support.mozilla.org/kb/firefox-monitor-faq"
target="_blank"
>
FAQ
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL that we never submitted this for translation 😂

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was already wondering if “FAQ” is just something that was decided to not be translated.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

src/app/(nextjs_migration)/(guest)/breaches/layout.tsx Outdated Show resolved Hide resolved
src/app/(nextjs_migration)/(guest)/layout.tsx Outdated Show resolved Hide resolved
Comment on lines 21 to 24
const session = await getServerSession(authOptions);
if (session) {
redirect("/user/breaches");
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't it intentional in the current website that we wouldn't redirect use to the dashboard from the landing page, even if they're logged in?

Copy link
Collaborator Author

@flozia flozia May 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I’ve got it the other way around with the pre-refresh site: 5f2509a and 7f99b0e.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I didn't realise you'd have to specify a redirect URL when calling signIn, but nice, that works well.

src/app/(nextjs_migration)/layout.tsx Show resolved Hide resolved
};

export const UserMenu = (props: Props) => {
export const UserMenu = ({ session, fxaSettingsUrl }: Props) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest (like in a different comment in this PR) to keep everything that's directly copied over from the "old" site inside the (nextjs_migration) directory, and create new versions of these components outside of them. I'd like to avoid applying the global stylesheet from the old website inside the new one, and would rather incrementally recreate them using CSS modules.

So all the components in this PR that are inside /components/client would be moved, since they won't look right in the parts of the website we do "the React way".

@flozia flozia requested a review from Vinnl May 25, 2023 11:08
Copy link
Collaborator

@Vinnl Vinnl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, nice work, let's merge this and get porting!

@flozia flozia merged commit 0a3a7bc into nextjs May 25, 2023
@flozia flozia deleted the nextjs-authenticated-layout branch May 25, 2023 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants