Skip to content

Commit 0bad2dd

Browse files
authored
chore(*): updated styling for example apps
chore(*): updated styling for example apps
2 parents c11766b + 8b6f73f commit 0bad2dd

File tree

37 files changed

+1511
-200
lines changed

37 files changed

+1511
-200
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
"use client";
18+
19+
import { multiFactor, sendEmailVerification, signOut } from "firebase/auth";
20+
import { useRouter } from "next/navigation";
21+
import { useUser } from "@/lib/firebase/hooks";
22+
import { auth } from "@/lib/firebase/clientApp";
23+
import { type User } from "firebase/auth";
24+
25+
export function AuthenticatedApp({ initialUser }: { initialUser: User | null }) {
26+
const user = useUser(initialUser);
27+
const router = useRouter();
28+
29+
if (!user) {
30+
return null;
31+
}
32+
33+
const mfa = multiFactor(user);
34+
35+
return (
36+
<div className="max-w-sm mx-auto pt-36 space-y-6 pb-36">
37+
<div className="border border-neutral-200 dark:border-neutral-800 rounded-md p-4 space-y-4">
38+
<h1 className="text-md font-medium">Welcome, {user.displayName || user.email || user.phoneNumber}</h1>
39+
{user.email ? (
40+
<>
41+
{user.emailVerified ? (
42+
<div className="text-green-500">Email verified</div>
43+
) : (
44+
<button
45+
className="bg-red-500 text-white px-3 py-1.5 rounded text-sm"
46+
onClick={async () => {
47+
try {
48+
await sendEmailVerification(user);
49+
alert("Email verification sent, please check your email");
50+
} catch (error) {
51+
console.error(error);
52+
alert("Error sending email verification, check console");
53+
}
54+
}}
55+
>
56+
Verify Email &rarr;
57+
</button>
58+
)}
59+
</>
60+
) : null}
61+
62+
<hr className="opacity-30" />
63+
<h2 className="text-sm font-medium">Multi-factor Authentication</h2>
64+
{mfa.enrolledFactors.map((factor) => {
65+
return (
66+
<div key={factor.factorId}>
67+
{factor.factorId} - {factor.displayName}
68+
</div>
69+
);
70+
})}
71+
<button
72+
className="bg-blue-500 text-white px-3 py-1.5 rounded text-sm"
73+
onClick={() => {
74+
router.push("/screens/mfa-enrollment-screen");
75+
}}
76+
>
77+
Add MFA Factor &rarr;
78+
</button>
79+
<hr className="opacity-30" />
80+
<button
81+
className="bg-blue-500 text-white px-3 py-1.5 rounded text-sm"
82+
onClick={async () => await signOut(auth)}
83+
>
84+
Sign Out &rarr;
85+
</button>
86+
</div>
87+
</div>
88+
);
89+
}

examples/nextjs-ssr/app/globals.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,25 @@
1515
*/
1616

1717
@import "tailwindcss";
18+
@custom-variant dark (&:where(.dark, .dark *));
1819
@import "@invertase/firebaseui-styles/tailwind";
1920

21+
/* Prevent flash by hiding content until theme is loaded */
22+
html:not(.theme-loaded) body {
23+
visibility: hidden;
24+
}
25+
26+
html.theme-loaded body {
27+
visibility: visible;
28+
}
29+
30+
.fui-provider__button[data-provider="oidc.line"][data-themed="true"] {
31+
--line-primary: #07B53B;
32+
--color-primary: var(--line-primary);
33+
--color-primary-hover: --alpha(var(--line-primary) / 85%);
34+
--color-primary-surface: #FFFFFF;
35+
--color-border: var(--line-primary);
36+
}
37+
2038
/* @import "@invertase/firebaseui-styles/themes/dark.css"; */
2139
/* @import "@invertase/firebaseui-styles/themes/brutalist.css"; */

examples/nextjs-ssr/app/layout.tsx

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,13 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { getCurrentUser } from "@/lib/firebase/serverApp";
1817
import type { Metadata } from "next";
19-
import { Geist, Geist_Mono } from "next/font/google";
20-
21-
import { Header } from "@/lib/components/header";
18+
import Script from "next/script";
2219
import { FirebaseUIProviderHoc } from "@/lib/firebase/ui";
20+
import { ThemeToggle } from "@/lib/components/theme-toggle";
21+
import { PirateToggle } from "@/lib/components/pirate-toggle";
2322
import "./globals.css";
2423

25-
const geistSans = Geist({
26-
variable: "--font-geist-sans",
27-
subsets: ["latin"],
28-
});
29-
30-
const geistMono = Geist_Mono({
31-
variable: "--font-geist-mono",
32-
subsets: ["latin"],
33-
});
34-
3524
export const metadata: Metadata = {
3625
title: "Create Next App (SSR)",
3726
description: "Generated by create next app with SSR",
@@ -42,13 +31,36 @@ export default async function RootLayout({
4231
}: Readonly<{
4332
children: React.ReactNode;
4433
}>) {
45-
const { currentUser } = await getCurrentUser();
46-
4734
return (
48-
<html lang="en">
49-
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
50-
<Header currentUser={currentUser} />
51-
<FirebaseUIProviderHoc>{children}</FirebaseUIProviderHoc>
35+
<html lang="en" suppressHydrationWarning>
36+
<body className="dark:bg-neutral-900 dark:text-white" suppressHydrationWarning>
37+
<Script
38+
id="dark-mode-init"
39+
strategy="beforeInteractive"
40+
dangerouslySetInnerHTML={{
41+
__html: `
42+
!(function() {
43+
try {
44+
var theme = localStorage.getItem('theme');
45+
var isDark = theme === 'dark' || (!theme && window.matchMedia('(prefers-color-scheme: dark)').matches);
46+
if (isDark) {
47+
document.documentElement.classList.add('dark');
48+
}
49+
// Mark theme as loaded - this removes the visibility:hidden
50+
document.documentElement.classList.add('theme-loaded');
51+
} catch (e) {
52+
// If anything fails, still show content
53+
document.documentElement.classList.add('theme-loaded');
54+
}
55+
})();
56+
`,
57+
}}
58+
/>
59+
<FirebaseUIProviderHoc>
60+
<ThemeToggle />
61+
<PirateToggle />
62+
{children}
63+
</FirebaseUIProviderHoc>
5264
</body>
5365
</html>
5466
);

examples/nextjs-ssr/app/page.tsx

Lines changed: 7 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -15,75 +15,15 @@
1515
*/
1616

1717
import { getCurrentUser } from "@/lib/firebase/serverApp";
18-
import Link from "next/link";
18+
import { UnauthenticatedApp } from "./unauthenticated-app";
19+
import { AuthenticatedApp } from "./authenticated-app";
1920

2021
export default async function Home() {
2122
const { currentUser } = await getCurrentUser();
2223

23-
return (
24-
<div className="p-8 ">
25-
<h1 className="text-3xl font-bold mb-6">Firebase UI Demo (SSR)</h1>
26-
<div className="mb-6">{currentUser && <div>Welcome: {currentUser.email || currentUser.phoneNumber}</div>}</div>
27-
<div>
28-
<h2 className="text-2xl font-bold mb-4">Auth Screens</h2>
29-
<ul className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
30-
<li>
31-
<Link href="/screens/sign-in-auth-screen" className="text-blue-500 hover:underline">
32-
Sign In Auth Screen
33-
</Link>
34-
</li>
35-
<li>
36-
<Link href="/screens/sign-in-auth-screen-w-handlers" className="text-blue-500 hover:underline">
37-
Sign In Auth Screen with Handlers
38-
</Link>
39-
</li>
40-
<li>
41-
<Link href="/screens/sign-in-auth-screen-w-oauth" className="text-blue-500 hover:underline">
42-
Sign In Auth Screen with OAuth
43-
</Link>
44-
</li>
45-
<li>
46-
<Link href="/screens/email-link-auth-screen" className="text-blue-500 hover:underline">
47-
Email Link Auth Screen
48-
</Link>
49-
</li>
50-
<li>
51-
<Link href="/screens/email-link-auth-screen-w-oauth" className="text-blue-500 hover:underline">
52-
Email Link Auth Screen with OAuth
53-
</Link>
54-
</li>
55-
<li>
56-
<Link href="/screens/phone-auth-screen" className="text-blue-500 hover:underline">
57-
Phone Auth Screen
58-
</Link>
59-
</li>
60-
<li>
61-
<Link href="/screens/phone-auth-screen-w-oauth" className="text-blue-500 hover:underline">
62-
Phone Auth Screen with OAuth
63-
</Link>
64-
</li>
65-
<li>
66-
<Link href="/screens/sign-up-auth-screen" className="text-blue-500 hover:underline">
67-
Sign Up Auth Screen
68-
</Link>
69-
</li>
70-
<li>
71-
<Link href="/screens/sign-up-auth-screen-w-oauth" className="text-blue-500 hover:underline">
72-
Sign Up Auth Screen with OAuth
73-
</Link>
74-
</li>
75-
<li>
76-
<Link href="/screens/oauth-screen" className="text-blue-500 hover:underline">
77-
OAuth Screen
78-
</Link>
79-
</li>
80-
<li>
81-
<Link href="/screens/password-reset-screen" className="text-blue-500 hover:underline">
82-
Password Reset Screen
83-
</Link>
84-
</li>
85-
</ul>
86-
</div>
87-
</div>
88-
);
24+
if (currentUser) {
25+
return <AuthenticatedApp initialUser={currentUser} />;
26+
}
27+
28+
return <UnauthenticatedApp />;
8929
}

examples/nextjs-ssr/app/screens/email-link-auth-screen-w-oauth/page.tsx

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,59 @@
1616

1717
"use client";
1818

19-
import { EmailLinkAuthScreen, GoogleSignInButton } from "@invertase/firebaseui-react";
19+
import {
20+
AppleSignInButton,
21+
EmailLinkAuthScreen,
22+
FacebookSignInButton,
23+
GitHubSignInButton,
24+
GoogleSignInButton,
25+
MicrosoftSignInButton,
26+
TwitterSignInButton,
27+
OAuthButton,
28+
} from "@invertase/firebaseui-react";
29+
import { OAuthProvider } from "firebase/auth";
30+
import { useRouter } from "next/navigation";
2031

2132
export default function EmailLinkAuthScreenWithOAuthPage() {
33+
const router = useRouter();
34+
2235
return (
23-
<EmailLinkAuthScreen>
36+
<EmailLinkAuthScreen
37+
onEmailSent={() => {
38+
alert("Email has been sent - please check your email");
39+
}}
40+
onSignIn={(credential) => {
41+
console.log(credential);
42+
router.push("/");
43+
}}
44+
>
2445
<GoogleSignInButton />
46+
<FacebookSignInButton />
47+
<AppleSignInButton />
48+
<GitHubSignInButton />
49+
<MicrosoftSignInButton />
50+
<TwitterSignInButton />
51+
<LineSignInButton />
2552
</EmailLinkAuthScreen>
2653
);
2754
}
55+
56+
function LineSignInButton() {
57+
const provider = new OAuthProvider("oidc.line");
58+
59+
return (
60+
<OAuthButton provider={provider}>
61+
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 48 48">
62+
<path
63+
fill="#00c300"
64+
d="M12.5 42h23a6.5 6.5 0 0 0 6.5-6.5v-23A6.5 6.5 0 0 0 35.5 6h-23A6.5 6.5 0 0 0 6 12.5v23a6.5 6.5 0 0 0 6.5 6.5"
65+
/>
66+
<path
67+
fill="#fff"
68+
d="M37.113 22.417c0-5.865-5.88-10.637-13.107-10.637s-13.108 4.772-13.108 10.637c0 5.258 4.663 9.662 10.962 10.495.427.092 1.008.282 1.155.646.132.331.086.85.042 1.185 0 0-.153.925-.187 1.122-.057.331-.263 1.296 1.135.707s7.548-4.445 10.298-7.611h-.001c1.901-2.082 2.811-4.197 2.811-6.544m-18.238 3.49h-2.604a.69.69 0 0 1-.687-.688V20.01a.688.688 0 0 1 1.374 0v4.521h1.917a.688.688 0 0 1 0 1.376m2.693-.688a.688.688 0 1 1-1.374 0V20.01a.688.688 0 0 1 1.374 0zm6.27 0a.684.684 0 0 1-.688.688.69.69 0 0 1-.549-.275l-2.669-3.635v3.222a.689.689 0 0 1-1.376 0V20.01a.687.687 0 0 1 1.237-.412l2.67 3.635V20.01a.688.688 0 0 1 1.375 0zm4.214-3.292a.689.689 0 0 1 0 1.375h-1.917v1.23h1.917a.688.688 0 0 1 0 1.375h-2.604a.69.69 0 0 1-.687-.688v-5.208c0-.379.308-.687.687-.687h2.604a.688.688 0 1 1 0 1.374h-1.917v1.23h1.917z"
69+
/>
70+
</svg>
71+
<span>Sign in with Line</span>
72+
</OAuthButton>
73+
);
74+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import Link from "next/link";
18+
19+
export default function ScreensLayout({ children }: { children: React.ReactNode }) {
20+
return (
21+
<div className="p-8">
22+
<Link
23+
href="/"
24+
className="border border-gray-300 dark:border-gray-700 border-rounded px-4 py-2 rounded-md text-sm inline-block"
25+
>
26+
&larr; Back to overview
27+
</Link>
28+
<div className="pt-12">{children}</div>
29+
</div>
30+
);
31+
}

0 commit comments

Comments
 (0)